[syntax-errors] Improve error message and range for pre-PEP-614 decorator syntax errors (#16581)

## Summary

A small followup to https://github.com/astral-sh/ruff/pull/16386. We now
tell the user exactly what it was about their decorator that constituted
invalid syntax on Python <3.9, and the range now highlights the specific
sub-expression that is invalid rather than highlighting the whole
decorator

## Test Plan

Inline snapshots are updated, and new ones are added.
This commit is contained in:
Alex Waygood 2025-03-17 11:17:27 +00:00 committed by GitHub
parent 4da6936ec4
commit 38bfda94ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 574 additions and 19 deletions

View file

@ -0,0 +1,96 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/err/decorator_await_expression_py38.py
---
## AST
```
Module(
ModModule {
range: 0..96,
body: [
FunctionDef(
StmtFunctionDef {
range: 45..95,
is_async: true,
decorator_list: [],
name: Identifier {
id: Name("foo"),
range: 55..58,
},
type_params: None,
parameters: Parameters {
range: 58..60,
posonlyargs: [],
args: [],
vararg: None,
kwonlyargs: [],
kwarg: None,
},
returns: None,
body: [
FunctionDef(
StmtFunctionDef {
range: 66..95,
is_async: false,
decorator_list: [
Decorator {
range: 66..76,
expression: Await(
ExprAwait {
range: 67..76,
value: Name(
ExprName {
range: 73..76,
id: Name("bar"),
ctx: Load,
},
),
},
),
},
],
name: Identifier {
id: Name("baz"),
range: 85..88,
},
type_params: None,
parameters: Parameters {
range: 88..90,
posonlyargs: [],
args: [],
vararg: None,
kwonlyargs: [],
kwarg: None,
},
returns: None,
body: [
Expr(
StmtExpr {
range: 92..95,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 92..95,
},
),
},
),
],
},
),
],
},
),
],
},
)
```
## Unsupported Syntax Errors
|
1 | # parse_options: { "target-version": "3.8" }
2 | async def foo():
3 | @await bar
| ^^^^^^^^^ Syntax Error: Cannot use `await` expression outside function call arguments in a decorator on Python 3.8 (syntax was added in Python 3.9)
4 | def baz(): ...
|

View file

@ -0,0 +1,87 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/err/decorator_dict_literal_py38.py
---
## AST
```
Module(
ModModule {
range: 0..68,
body: [
FunctionDef(
StmtFunctionDef {
range: 45..67,
is_async: false,
decorator_list: [
Decorator {
range: 45..52,
expression: Dict(
ExprDict {
range: 46..52,
items: [
DictItem {
key: Some(
NumberLiteral(
ExprNumberLiteral {
range: 47..48,
value: Int(
3,
),
},
),
),
value: NumberLiteral(
ExprNumberLiteral {
range: 50..51,
value: Int(
3,
),
},
),
},
],
},
),
},
],
name: Identifier {
id: Name("bar"),
range: 57..60,
},
type_params: None,
parameters: Parameters {
range: 60..62,
posonlyargs: [],
args: [],
vararg: None,
kwonlyargs: [],
kwarg: None,
},
returns: None,
body: [
Expr(
StmtExpr {
range: 64..67,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 64..67,
},
),
},
),
],
},
),
],
},
)
```
## Unsupported Syntax Errors
|
1 | # parse_options: { "target-version": "3.8" }
2 | @{3: 3}
| ^^^^^^ Syntax Error: Cannot use a dict literal outside function call arguments in a decorator on Python 3.8 (syntax was added in Python 3.9)
3 | def bar(): ...
|

View file

@ -96,6 +96,6 @@ Module(
|
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)
| ^^^^^^^^^^ Syntax Error: Cannot use subscript expression outside function call arguments in a decorator on Python 3.8 (syntax was added in Python 3.9)
3 | def spam(): ...
|

View file

@ -0,0 +1,68 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/err/decorator_float_literal_py38.py
---
## AST
```
Module(
ModModule {
range: 0..66,
body: [
FunctionDef(
StmtFunctionDef {
range: 45..65,
is_async: false,
decorator_list: [
Decorator {
range: 45..50,
expression: NumberLiteral(
ExprNumberLiteral {
range: 46..50,
value: Float(
3.14,
),
},
),
},
],
name: Identifier {
id: Name("bar"),
range: 55..58,
},
type_params: None,
parameters: Parameters {
range: 58..60,
posonlyargs: [],
args: [],
vararg: None,
kwonlyargs: [],
kwarg: None,
},
returns: None,
body: [
Expr(
StmtExpr {
range: 62..65,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 62..65,
},
),
},
),
],
},
),
],
},
)
```
## Unsupported Syntax Errors
|
1 | # parse_options: { "target-version": "3.8" }
2 | @3.14
| ^^^^ Syntax Error: Cannot use a float literal outside function call arguments in a decorator on Python 3.8 (syntax was added in Python 3.9)
3 | def bar(): ...
|

View file

@ -128,6 +128,6 @@ Module(
|
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)
| ^^^^^^^^^^^^^^^^ Syntax Error: Cannot use assignment expression outside function call arguments in a decorator on Python 3.7 (syntax was added in Python 3.9)
3 | def bar(): ...
|

View file

@ -0,0 +1,97 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/err/decorator_non_toplevel_call_expression_py38.py
---
## AST
```
Module(
ModModule {
range: 0..73,
body: [
FunctionDef(
StmtFunctionDef {
range: 45..72,
is_async: false,
decorator_list: [
Decorator {
range: 45..57,
expression: Call(
ExprCall {
range: 46..57,
func: Attribute(
ExprAttribute {
range: 46..55,
value: Call(
ExprCall {
range: 46..51,
func: Name(
ExprName {
range: 46..49,
id: Name("foo"),
ctx: Load,
},
),
arguments: Arguments {
range: 49..51,
args: [],
keywords: [],
},
},
),
attr: Identifier {
id: Name("bar"),
range: 52..55,
},
ctx: Load,
},
),
arguments: Arguments {
range: 55..57,
args: [],
keywords: [],
},
},
),
},
],
name: Identifier {
id: Name("baz"),
range: 62..65,
},
type_params: None,
parameters: Parameters {
range: 65..67,
posonlyargs: [],
args: [],
vararg: None,
kwonlyargs: [],
kwarg: None,
},
returns: None,
body: [
Expr(
StmtExpr {
range: 69..72,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 69..72,
},
),
},
),
],
},
),
],
},
)
```
## Unsupported Syntax Errors
|
1 | # parse_options: { "target-version": "3.8" }
2 | @foo().bar()
| ^^^^^ Syntax Error: Cannot use a call expression in a decorator on Python 3.8 unless it is the top-level expression or it occurs in the argument list of a top-level call expression (relaxed decorator syntax was added in Python 3.9)
3 | def baz(): ...
|

View file

@ -0,0 +1,87 @@
---
source: crates/ruff_python_parser/tests/fixtures.rs
input_file: crates/ruff_python_parser/resources/inline/ok/decorator_await_expression_py39.py
---
## AST
```
Module(
ModModule {
range: 0..96,
body: [
FunctionDef(
StmtFunctionDef {
range: 45..95,
is_async: true,
decorator_list: [],
name: Identifier {
id: Name("foo"),
range: 55..58,
},
type_params: None,
parameters: Parameters {
range: 58..60,
posonlyargs: [],
args: [],
vararg: None,
kwonlyargs: [],
kwarg: None,
},
returns: None,
body: [
FunctionDef(
StmtFunctionDef {
range: 66..95,
is_async: false,
decorator_list: [
Decorator {
range: 66..76,
expression: Await(
ExprAwait {
range: 67..76,
value: Name(
ExprName {
range: 73..76,
id: Name("bar"),
ctx: Load,
},
),
},
),
},
],
name: Identifier {
id: Name("baz"),
range: 85..88,
},
type_params: None,
parameters: Parameters {
range: 88..90,
posonlyargs: [],
args: [],
vararg: None,
kwonlyargs: [],
kwarg: None,
},
returns: None,
body: [
Expr(
StmtExpr {
range: 92..95,
value: EllipsisLiteral(
ExprEllipsisLiteral {
range: 92..95,
},
),
},
),
],
},
),
],
},
),
],
},
)
```