mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-28 06:14:27 +00:00
[syntax-errors] Named expressions in decorators before Python 3.9 (#16386)
Summary -- This PR detects the relaxed grammar for decorators proposed in [PEP 614](https://peps.python.org/pep-0614/) on Python 3.8 and lower. The 3.8 grammar for decorators is [here](https://docs.python.org/3.8/reference/compound_stmts.html#grammar-token-decorators): ``` decorators ::= decorator+ decorator ::= "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE dotted_name ::= identifier ("." identifier)* ``` in contrast to the current grammar [here](https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-decorators) ``` decorators ::= decorator+ decorator ::= "@" assignment_expression NEWLINE assignment_expression ::= [identifier ":="] expression ``` Test Plan -- New inline parser tests.
This commit is contained in:
parent
d0623888b3
commit
318f503714
16 changed files with 876 additions and 3 deletions
|
@ -0,0 +1,101 @@
|
|||
---
|
||||
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_parser/resources/inline/err/decorator_expression_py38.py
|
||||
---
|
||||
## AST
|
||||
|
||||
```
|
||||
Module(
|
||||
ModModule {
|
||||
range: 0..89,
|
||||
body: [
|
||||
FunctionDef(
|
||||
StmtFunctionDef {
|
||||
range: 45..88,
|
||||
is_async: false,
|
||||
decorator_list: [
|
||||
Decorator {
|
||||
range: 45..72,
|
||||
expression: Attribute(
|
||||
ExprAttribute {
|
||||
range: 46..72,
|
||||
value: Attribute(
|
||||
ExprAttribute {
|
||||
range: 46..64,
|
||||
value: Subscript(
|
||||
ExprSubscript {
|
||||
range: 46..56,
|
||||
value: Name(
|
||||
ExprName {
|
||||
range: 46..53,
|
||||
id: Name("buttons"),
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
slice: NumberLiteral(
|
||||
ExprNumberLiteral {
|
||||
range: 54..55,
|
||||
value: Int(
|
||||
0,
|
||||
),
|
||||
},
|
||||
),
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
attr: Identifier {
|
||||
id: Name("clicked"),
|
||||
range: 57..64,
|
||||
},
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
attr: Identifier {
|
||||
id: Name("connect"),
|
||||
range: 65..72,
|
||||
},
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
name: Identifier {
|
||||
id: Name("spam"),
|
||||
range: 77..81,
|
||||
},
|
||||
type_params: None,
|
||||
parameters: Parameters {
|
||||
range: 81..83,
|
||||
posonlyargs: [],
|
||||
args: [],
|
||||
vararg: None,
|
||||
kwonlyargs: [],
|
||||
kwarg: None,
|
||||
},
|
||||
returns: None,
|
||||
body: [
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 85..88,
|
||||
value: EllipsisLiteral(
|
||||
ExprEllipsisLiteral {
|
||||
range: 85..88,
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
)
|
||||
```
|
||||
## Unsupported Syntax Errors
|
||||
|
||||
|
|
||||
1 | # parse_options: { "target-version": "3.8" }
|
||||
2 | @buttons[0].clicked.connect
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ Syntax Error: Unsupported expression in decorators on Python 3.8 (syntax was added in Python 3.9)
|
||||
3 | def spam(): ...
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
---
|
||||
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_parser/resources/inline/err/decorator_named_expression_py37.py
|
||||
---
|
||||
## AST
|
||||
|
||||
```
|
||||
Module(
|
||||
ModModule {
|
||||
range: 0..85,
|
||||
body: [
|
||||
FunctionDef(
|
||||
StmtFunctionDef {
|
||||
range: 45..84,
|
||||
is_async: false,
|
||||
decorator_list: [
|
||||
Decorator {
|
||||
range: 45..69,
|
||||
expression: Call(
|
||||
ExprCall {
|
||||
range: 46..69,
|
||||
func: Named(
|
||||
ExprNamed {
|
||||
range: 47..63,
|
||||
target: Name(
|
||||
ExprName {
|
||||
range: 47..48,
|
||||
id: Name("x"),
|
||||
ctx: Store,
|
||||
},
|
||||
),
|
||||
value: Lambda(
|
||||
ExprLambda {
|
||||
range: 52..63,
|
||||
parameters: Some(
|
||||
Parameters {
|
||||
range: 59..60,
|
||||
posonlyargs: [],
|
||||
args: [
|
||||
ParameterWithDefault {
|
||||
range: 59..60,
|
||||
parameter: Parameter {
|
||||
range: 59..60,
|
||||
name: Identifier {
|
||||
id: Name("x"),
|
||||
range: 59..60,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
default: None,
|
||||
},
|
||||
],
|
||||
vararg: None,
|
||||
kwonlyargs: [],
|
||||
kwarg: None,
|
||||
},
|
||||
),
|
||||
body: Name(
|
||||
ExprName {
|
||||
range: 62..63,
|
||||
id: Name("x"),
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
arguments: Arguments {
|
||||
range: 64..69,
|
||||
args: [
|
||||
Name(
|
||||
ExprName {
|
||||
range: 65..68,
|
||||
id: Name("foo"),
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
],
|
||||
keywords: [],
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
name: Identifier {
|
||||
id: Name("bar"),
|
||||
range: 74..77,
|
||||
},
|
||||
type_params: None,
|
||||
parameters: Parameters {
|
||||
range: 77..79,
|
||||
posonlyargs: [],
|
||||
args: [],
|
||||
vararg: None,
|
||||
kwonlyargs: [],
|
||||
kwarg: None,
|
||||
},
|
||||
returns: None,
|
||||
body: [
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 81..84,
|
||||
value: EllipsisLiteral(
|
||||
ExprEllipsisLiteral {
|
||||
range: 81..84,
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
)
|
||||
```
|
||||
## Unsupported Syntax Errors
|
||||
|
||||
|
|
||||
1 | # parse_options: { "target-version": "3.7" }
|
||||
2 | @(x := lambda x: x)(foo)
|
||||
| ^^^^^^^^^^^^^^^^ Syntax Error: Cannot use named assignment expression (`:=`) on Python 3.7 (syntax was added in Python 3.8)
|
||||
3 | def bar(): ...
|
||||
|
|
||||
|
||||
|
||||
|
|
||||
1 | # parse_options: { "target-version": "3.7" }
|
||||
2 | @(x := lambda x: x)(foo)
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^ Syntax Error: Unsupported expression in decorators on Python 3.7 (syntax was added in Python 3.9)
|
||||
3 | def bar(): ...
|
||||
|
|
|
@ -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_py38.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,
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
)
|
||||
```
|
|
@ -0,0 +1,88 @@
|
|||
---
|
||||
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_parser/resources/inline/ok/decorator_expression_eval_hack_py38.py
|
||||
---
|
||||
## AST
|
||||
|
||||
```
|
||||
Module(
|
||||
ModModule {
|
||||
range: 0..97,
|
||||
body: [
|
||||
FunctionDef(
|
||||
StmtFunctionDef {
|
||||
range: 45..96,
|
||||
is_async: false,
|
||||
decorator_list: [
|
||||
Decorator {
|
||||
range: 45..80,
|
||||
expression: Call(
|
||||
ExprCall {
|
||||
range: 46..80,
|
||||
func: Name(
|
||||
ExprName {
|
||||
range: 46..50,
|
||||
id: Name("eval"),
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
arguments: Arguments {
|
||||
range: 50..80,
|
||||
args: [
|
||||
StringLiteral(
|
||||
ExprStringLiteral {
|
||||
range: 51..79,
|
||||
value: StringLiteralValue {
|
||||
inner: Single(
|
||||
StringLiteral {
|
||||
range: 51..79,
|
||||
value: "buttons[0].clicked.connect",
|
||||
flags: StringLiteralFlags {
|
||||
quote_style: Double,
|
||||
prefix: Empty,
|
||||
triple_quoted: false,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
},
|
||||
),
|
||||
],
|
||||
keywords: [],
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
name: Identifier {
|
||||
id: Name("spam"),
|
||||
range: 85..89,
|
||||
},
|
||||
type_params: None,
|
||||
parameters: Parameters {
|
||||
range: 89..91,
|
||||
posonlyargs: [],
|
||||
args: [],
|
||||
vararg: None,
|
||||
kwonlyargs: [],
|
||||
kwarg: None,
|
||||
},
|
||||
returns: None,
|
||||
body: [
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 93..96,
|
||||
value: EllipsisLiteral(
|
||||
ExprEllipsisLiteral {
|
||||
range: 93..96,
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
)
|
||||
```
|
|
@ -0,0 +1,161 @@
|
|||
---
|
||||
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_parser/resources/inline/ok/decorator_expression_identity_hack_py38.py
|
||||
---
|
||||
## AST
|
||||
|
||||
```
|
||||
Module(
|
||||
ModModule {
|
||||
range: 0..111,
|
||||
body: [
|
||||
FunctionDef(
|
||||
StmtFunctionDef {
|
||||
range: 45..63,
|
||||
is_async: false,
|
||||
decorator_list: [],
|
||||
name: Identifier {
|
||||
id: Name("_"),
|
||||
range: 49..50,
|
||||
},
|
||||
type_params: None,
|
||||
parameters: Parameters {
|
||||
range: 50..53,
|
||||
posonlyargs: [],
|
||||
args: [
|
||||
ParameterWithDefault {
|
||||
range: 51..52,
|
||||
parameter: Parameter {
|
||||
range: 51..52,
|
||||
name: Identifier {
|
||||
id: Name("x"),
|
||||
range: 51..52,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
default: None,
|
||||
},
|
||||
],
|
||||
vararg: None,
|
||||
kwonlyargs: [],
|
||||
kwarg: None,
|
||||
},
|
||||
returns: None,
|
||||
body: [
|
||||
Return(
|
||||
StmtReturn {
|
||||
range: 55..63,
|
||||
value: Some(
|
||||
Name(
|
||||
ExprName {
|
||||
range: 62..63,
|
||||
id: Name("x"),
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
FunctionDef(
|
||||
StmtFunctionDef {
|
||||
range: 64..110,
|
||||
is_async: false,
|
||||
decorator_list: [
|
||||
Decorator {
|
||||
range: 64..94,
|
||||
expression: Call(
|
||||
ExprCall {
|
||||
range: 65..94,
|
||||
func: Name(
|
||||
ExprName {
|
||||
range: 65..66,
|
||||
id: Name("_"),
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
arguments: Arguments {
|
||||
range: 66..94,
|
||||
args: [
|
||||
Attribute(
|
||||
ExprAttribute {
|
||||
range: 67..93,
|
||||
value: Attribute(
|
||||
ExprAttribute {
|
||||
range: 67..85,
|
||||
value: Subscript(
|
||||
ExprSubscript {
|
||||
range: 67..77,
|
||||
value: Name(
|
||||
ExprName {
|
||||
range: 67..74,
|
||||
id: Name("buttons"),
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
slice: NumberLiteral(
|
||||
ExprNumberLiteral {
|
||||
range: 75..76,
|
||||
value: Int(
|
||||
0,
|
||||
),
|
||||
},
|
||||
),
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
attr: Identifier {
|
||||
id: Name("clicked"),
|
||||
range: 78..85,
|
||||
},
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
attr: Identifier {
|
||||
id: Name("connect"),
|
||||
range: 86..93,
|
||||
},
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
],
|
||||
keywords: [],
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
name: Identifier {
|
||||
id: Name("spam"),
|
||||
range: 99..103,
|
||||
},
|
||||
type_params: None,
|
||||
parameters: Parameters {
|
||||
range: 103..105,
|
||||
posonlyargs: [],
|
||||
args: [],
|
||||
vararg: None,
|
||||
kwonlyargs: [],
|
||||
kwarg: None,
|
||||
},
|
||||
returns: None,
|
||||
body: [
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 107..110,
|
||||
value: EllipsisLiteral(
|
||||
ExprEllipsisLiteral {
|
||||
range: 107..110,
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
)
|
||||
```
|
|
@ -0,0 +1,195 @@
|
|||
---
|
||||
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_parser/resources/inline/ok/decorator_expression_py39.py
|
||||
---
|
||||
## AST
|
||||
|
||||
```
|
||||
Module(
|
||||
ModModule {
|
||||
range: 0..129,
|
||||
body: [
|
||||
FunctionDef(
|
||||
StmtFunctionDef {
|
||||
range: 45..88,
|
||||
is_async: false,
|
||||
decorator_list: [
|
||||
Decorator {
|
||||
range: 45..72,
|
||||
expression: Attribute(
|
||||
ExprAttribute {
|
||||
range: 46..72,
|
||||
value: Attribute(
|
||||
ExprAttribute {
|
||||
range: 46..64,
|
||||
value: Subscript(
|
||||
ExprSubscript {
|
||||
range: 46..56,
|
||||
value: Name(
|
||||
ExprName {
|
||||
range: 46..53,
|
||||
id: Name("buttons"),
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
slice: NumberLiteral(
|
||||
ExprNumberLiteral {
|
||||
range: 54..55,
|
||||
value: Int(
|
||||
0,
|
||||
),
|
||||
},
|
||||
),
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
attr: Identifier {
|
||||
id: Name("clicked"),
|
||||
range: 57..64,
|
||||
},
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
attr: Identifier {
|
||||
id: Name("connect"),
|
||||
range: 65..72,
|
||||
},
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
name: Identifier {
|
||||
id: Name("spam"),
|
||||
range: 77..81,
|
||||
},
|
||||
type_params: None,
|
||||
parameters: Parameters {
|
||||
range: 81..83,
|
||||
posonlyargs: [],
|
||||
args: [],
|
||||
vararg: None,
|
||||
kwonlyargs: [],
|
||||
kwarg: None,
|
||||
},
|
||||
returns: None,
|
||||
body: [
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 85..88,
|
||||
value: EllipsisLiteral(
|
||||
ExprEllipsisLiteral {
|
||||
range: 85..88,
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
FunctionDef(
|
||||
StmtFunctionDef {
|
||||
range: 89..128,
|
||||
is_async: false,
|
||||
decorator_list: [
|
||||
Decorator {
|
||||
range: 89..113,
|
||||
expression: Call(
|
||||
ExprCall {
|
||||
range: 90..113,
|
||||
func: Named(
|
||||
ExprNamed {
|
||||
range: 91..107,
|
||||
target: Name(
|
||||
ExprName {
|
||||
range: 91..92,
|
||||
id: Name("x"),
|
||||
ctx: Store,
|
||||
},
|
||||
),
|
||||
value: Lambda(
|
||||
ExprLambda {
|
||||
range: 96..107,
|
||||
parameters: Some(
|
||||
Parameters {
|
||||
range: 103..104,
|
||||
posonlyargs: [],
|
||||
args: [
|
||||
ParameterWithDefault {
|
||||
range: 103..104,
|
||||
parameter: Parameter {
|
||||
range: 103..104,
|
||||
name: Identifier {
|
||||
id: Name("x"),
|
||||
range: 103..104,
|
||||
},
|
||||
annotation: None,
|
||||
},
|
||||
default: None,
|
||||
},
|
||||
],
|
||||
vararg: None,
|
||||
kwonlyargs: [],
|
||||
kwarg: None,
|
||||
},
|
||||
),
|
||||
body: Name(
|
||||
ExprName {
|
||||
range: 106..107,
|
||||
id: Name("x"),
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
arguments: Arguments {
|
||||
range: 108..113,
|
||||
args: [
|
||||
Name(
|
||||
ExprName {
|
||||
range: 109..112,
|
||||
id: Name("foo"),
|
||||
ctx: Load,
|
||||
},
|
||||
),
|
||||
],
|
||||
keywords: [],
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
],
|
||||
name: Identifier {
|
||||
id: Name("bar"),
|
||||
range: 118..121,
|
||||
},
|
||||
type_params: None,
|
||||
parameters: Parameters {
|
||||
range: 121..123,
|
||||
posonlyargs: [],
|
||||
args: [],
|
||||
vararg: None,
|
||||
kwonlyargs: [],
|
||||
kwarg: None,
|
||||
},
|
||||
returns: None,
|
||||
body: [
|
||||
Expr(
|
||||
StmtExpr {
|
||||
range: 125..128,
|
||||
value: EllipsisLiteral(
|
||||
ExprEllipsisLiteral {
|
||||
range: 125..128,
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
),
|
||||
],
|
||||
},
|
||||
)
|
||||
```
|
Loading…
Add table
Add a link
Reference in a new issue