From 91d9f95ef83f63a6c241dd6cf4948fedf9110dcd Mon Sep 17 00:00:00 2001 From: Brent Westbrook Date: Tue, 25 Feb 2025 17:59:23 -0500 Subject: [PATCH] allow normal dotted names --- ...tor_expression_dotted_ident_before_py39.py | 3 + .../src/parser/statement.rs | 25 +++++- ...xpression_dotted_ident_before_py39.py.snap | 79 +++++++++++++++++++ 3 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 crates/ruff_python_parser/resources/inline/ok/decorator_expression_dotted_ident_before_py39.py create mode 100644 crates/ruff_python_parser/tests/snapshots/valid_syntax@decorator_expression_dotted_ident_before_py39.py.snap diff --git a/crates/ruff_python_parser/resources/inline/ok/decorator_expression_dotted_ident_before_py39.py b/crates/ruff_python_parser/resources/inline/ok/decorator_expression_dotted_ident_before_py39.py new file mode 100644 index 00000000000000..448be1ce873e91 --- /dev/null +++ b/crates/ruff_python_parser/resources/inline/ok/decorator_expression_dotted_ident_before_py39.py @@ -0,0 +1,3 @@ +# parse_options: { "target_version": "3.8" } +@buttons.clicked.connect +def spam(): ... diff --git a/crates/ruff_python_parser/src/parser/statement.rs b/crates/ruff_python_parser/src/parser/statement.rs index a0a76b7b076bcc..63772001714fea 100644 --- a/crates/ruff_python_parser/src/parser/statement.rs +++ b/crates/ruff_python_parser/src/parser/statement.rs @@ -2530,11 +2530,25 @@ impl<'src> Parser<'src> { fn try_parse_old_decorators(&mut self) -> Option { let errors = self.errors.len(); let start = self.node_start(); - let name = self.parse_name(); - if name.ctx.is_invalid() { + // initial identifier + let ident = self.parse_identifier(); + if !ident.is_valid() { return None; } - let name = Expr::from(name); + let mut name = Expr::from(ast::ExprName { + range: self.node_range(start), + id: ident.id, + ctx: ExprContext::Load, + }); + // ("." identifier)* + while self.at(TokenKind::Dot) { + let attr = self.parse_attribute_expression(name, start); + if !attr.attr.is_valid() { + return None; + } + name = Expr::from(attr); + } + // ["(" [argument_list [","]] ")"] NEWLINE let parsed = match self.current_token_kind() { TokenKind::Lpar => Some(Expr::Call(self.parse_call_expression(name, start)).into()), TokenKind::Newline => Some(name.into()), @@ -2564,6 +2578,11 @@ impl<'src> Parser<'src> { // before 3.9 but avoid false positives on examples like the `@_` "identity function hack" // or the "eval hack" called out in the PEP. + // test_ok decorator_expression_dotted_ident_before_py39 + // # parse_options: { "target_version": "3.8" } + // @buttons.clicked.connect + // def spam(): ... + // test_ok decorator_expression_identity_hack_before_py39 // # parse_options: { "target_version": "3.8" } // def _(x): return x diff --git a/crates/ruff_python_parser/tests/snapshots/valid_syntax@decorator_expression_dotted_ident_before_py39.py.snap b/crates/ruff_python_parser/tests/snapshots/valid_syntax@decorator_expression_dotted_ident_before_py39.py.snap new file mode 100644 index 00000000000000..4dcb023961173d --- /dev/null +++ b/crates/ruff_python_parser/tests/snapshots/valid_syntax@decorator_expression_dotted_ident_before_py39.py.snap @@ -0,0 +1,79 @@ +--- +source: crates/ruff_python_parser/tests/fixtures.rs +input_file: crates/ruff_python_parser/resources/inline/ok/decorator_expression_dotted_ident_before_py39.py +--- +## AST + +``` +Module( + ModModule { + range: 0..86, + body: [ + FunctionDef( + StmtFunctionDef { + range: 45..85, + is_async: false, + decorator_list: [ + Decorator { + range: 45..69, + expression: Attribute( + ExprAttribute { + range: 46..69, + value: Attribute( + ExprAttribute { + range: 46..61, + value: Name( + ExprName { + range: 46..53, + id: Name("buttons"), + ctx: Load, + }, + ), + attr: Identifier { + id: Name("clicked"), + range: 54..61, + }, + ctx: Load, + }, + ), + attr: Identifier { + id: Name("connect"), + range: 62..69, + }, + ctx: Load, + }, + ), + }, + ], + name: Identifier { + id: Name("spam"), + range: 74..78, + }, + type_params: None, + parameters: Parameters { + range: 78..80, + posonlyargs: [], + args: [], + vararg: None, + kwonlyargs: [], + kwarg: None, + }, + returns: None, + body: [ + Expr( + StmtExpr { + range: 82..85, + value: EllipsisLiteral( + ExprEllipsisLiteral { + range: 82..85, + }, + ), + }, + ), + ], + }, + ), + ], + }, +) +```