mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-02 18:02:58 +00:00
[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:
parent
4da6936ec4
commit
38bfda94ce
15 changed files with 574 additions and 19 deletions
|
@ -0,0 +1,4 @@
|
||||||
|
# parse_options: { "target-version": "3.8" }
|
||||||
|
async def foo():
|
||||||
|
@await bar
|
||||||
|
def baz(): ...
|
|
@ -0,0 +1,3 @@
|
||||||
|
# parse_options: { "target-version": "3.8" }
|
||||||
|
@{3: 3}
|
||||||
|
def bar(): ...
|
|
@ -0,0 +1,3 @@
|
||||||
|
# parse_options: { "target-version": "3.8" }
|
||||||
|
@3.14
|
||||||
|
def bar(): ...
|
|
@ -0,0 +1,3 @@
|
||||||
|
# parse_options: { "target-version": "3.8" }
|
||||||
|
@foo().bar()
|
||||||
|
def baz(): ...
|
|
@ -0,0 +1,4 @@
|
||||||
|
# parse_options: { "target-version": "3.9" }
|
||||||
|
async def foo():
|
||||||
|
@await bar
|
||||||
|
def baz(): ...
|
|
@ -601,7 +601,7 @@ pub enum UnsupportedSyntaxErrorKind {
|
||||||
/// [PEP 614]: https://peps.python.org/pep-0614/
|
/// [PEP 614]: https://peps.python.org/pep-0614/
|
||||||
/// [`dotted_name`]: https://docs.python.org/3.8/reference/compound_stmts.html#grammar-token-dotted-name
|
/// [`dotted_name`]: https://docs.python.org/3.8/reference/compound_stmts.html#grammar-token-dotted-name
|
||||||
/// [decorator grammar]: https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-decorator
|
/// [decorator grammar]: https://docs.python.org/3/reference/compound_stmts.html#grammar-token-python-grammar-decorator
|
||||||
RelaxedDecorator,
|
RelaxedDecorator(RelaxedDecoratorError),
|
||||||
|
|
||||||
/// Represents the use of a [PEP 570] positional-only parameter before Python 3.8.
|
/// Represents the use of a [PEP 570] positional-only parameter before Python 3.8.
|
||||||
///
|
///
|
||||||
|
@ -768,7 +768,28 @@ impl Display for UnsupportedSyntaxError {
|
||||||
UnsupportedSyntaxErrorKind::StarTuple(StarTupleKind::Yield) => {
|
UnsupportedSyntaxErrorKind::StarTuple(StarTupleKind::Yield) => {
|
||||||
"Cannot use iterable unpacking in yield expressions"
|
"Cannot use iterable unpacking in yield expressions"
|
||||||
}
|
}
|
||||||
UnsupportedSyntaxErrorKind::RelaxedDecorator => "Unsupported expression in decorators",
|
UnsupportedSyntaxErrorKind::RelaxedDecorator(relaxed_decorator_error) => {
|
||||||
|
return match relaxed_decorator_error {
|
||||||
|
RelaxedDecoratorError::CallExpression => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Cannot use a call expression in a decorator on Python {} \
|
||||||
|
unless it is the top-level expression or it occurs \
|
||||||
|
in the argument list of a top-level call expression \
|
||||||
|
(relaxed decorator syntax was {changed})",
|
||||||
|
self.target_version,
|
||||||
|
changed = self.kind.changed_version(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
RelaxedDecoratorError::Other(description) => write!(
|
||||||
|
f,
|
||||||
|
"Cannot use {description} outside function call arguments in a decorator on Python {} \
|
||||||
|
(syntax was {changed})",
|
||||||
|
self.target_version,
|
||||||
|
changed = self.kind.changed_version(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
UnsupportedSyntaxErrorKind::PositionalOnlyParameter => {
|
UnsupportedSyntaxErrorKind::PositionalOnlyParameter => {
|
||||||
"Cannot use positional-only parameter separator"
|
"Cannot use positional-only parameter separator"
|
||||||
}
|
}
|
||||||
|
@ -795,6 +816,12 @@ impl Display for UnsupportedSyntaxError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum RelaxedDecoratorError {
|
||||||
|
CallExpression,
|
||||||
|
Other(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents the kind of change in Python syntax between versions.
|
/// Represents the kind of change in Python syntax between versions.
|
||||||
enum Change {
|
enum Change {
|
||||||
Added(PythonVersion),
|
Added(PythonVersion),
|
||||||
|
@ -822,7 +849,9 @@ impl UnsupportedSyntaxErrorKind {
|
||||||
Change::Added(PythonVersion::PY39)
|
Change::Added(PythonVersion::PY39)
|
||||||
}
|
}
|
||||||
UnsupportedSyntaxErrorKind::StarTuple(_) => Change::Added(PythonVersion::PY38),
|
UnsupportedSyntaxErrorKind::StarTuple(_) => Change::Added(PythonVersion::PY38),
|
||||||
UnsupportedSyntaxErrorKind::RelaxedDecorator => Change::Added(PythonVersion::PY39),
|
UnsupportedSyntaxErrorKind::RelaxedDecorator { .. } => {
|
||||||
|
Change::Added(PythonVersion::PY39)
|
||||||
|
}
|
||||||
UnsupportedSyntaxErrorKind::PositionalOnlyParameter => {
|
UnsupportedSyntaxErrorKind::PositionalOnlyParameter => {
|
||||||
Change::Added(PythonVersion::PY38)
|
Change::Added(PythonVersion::PY38)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use ruff_python_ast::{self as ast, CmpOp, Expr, ExprContext};
|
use ruff_python_ast::{self as ast, CmpOp, Expr, ExprContext, Number};
|
||||||
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::TokenKind;
|
use crate::{error::RelaxedDecoratorError, TokenKind};
|
||||||
|
|
||||||
/// Set the `ctx` for `Expr::Id`, `Expr::Attribute`, `Expr::Subscript`, `Expr::Starred`,
|
/// Set the `ctx` for `Expr::Id`, `Expr::Attribute`, `Expr::Subscript`, `Expr::Starred`,
|
||||||
/// `Expr::Tuple` and `Expr::List`. If `expr` is either `Expr::Tuple` or `Expr::List`,
|
/// `Expr::Tuple` and `Expr::List`. If `expr` is either `Expr::Tuple` or `Expr::List`,
|
||||||
|
@ -47,11 +48,56 @@ pub(super) const fn token_kind_to_cmp_op(tokens: [TokenKind; 2]) -> Option<CmpOp
|
||||||
/// Helper for `parse_decorators` to determine if `expr` is a [`dotted_name`] from the decorator
|
/// Helper for `parse_decorators` to determine if `expr` is a [`dotted_name`] from the decorator
|
||||||
/// grammar before Python 3.9.
|
/// grammar before Python 3.9.
|
||||||
///
|
///
|
||||||
|
/// Returns `Some((error, range))` if `expr` is not a `dotted_name`, or `None` if it is a `dotted_name`.
|
||||||
|
///
|
||||||
/// [`dotted_name`]: https://docs.python.org/3.8/reference/compound_stmts.html#grammar-token-dotted-name
|
/// [`dotted_name`]: https://docs.python.org/3.8/reference/compound_stmts.html#grammar-token-dotted-name
|
||||||
pub(super) fn is_name_or_attribute_expression(expr: &Expr) -> bool {
|
pub(super) fn detect_invalid_pre_py39_decorator_node(
|
||||||
match expr {
|
expr: &Expr,
|
||||||
Expr::Attribute(attr) => is_name_or_attribute_expression(&attr.value),
|
) -> Option<(RelaxedDecoratorError, TextRange)> {
|
||||||
Expr::Name(_) => true,
|
let description = match expr {
|
||||||
_ => false,
|
Expr::Name(_) => return None,
|
||||||
}
|
|
||||||
|
Expr::Attribute(attribute) => {
|
||||||
|
return detect_invalid_pre_py39_decorator_node(&attribute.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
Expr::Call(_) => return Some((RelaxedDecoratorError::CallExpression, expr.range())),
|
||||||
|
|
||||||
|
Expr::NumberLiteral(number) => match &number.value {
|
||||||
|
Number::Int(_) => "an int literal",
|
||||||
|
Number::Float(_) => "a float literal",
|
||||||
|
Number::Complex { .. } => "a complex literal",
|
||||||
|
},
|
||||||
|
|
||||||
|
Expr::BoolOp(_) => "boolean expression",
|
||||||
|
Expr::BinOp(_) => "binary-operation expression",
|
||||||
|
Expr::UnaryOp(_) => "unary-operation expression",
|
||||||
|
Expr::Await(_) => "`await` expression",
|
||||||
|
Expr::Lambda(_) => "lambda expression",
|
||||||
|
Expr::If(_) => "conditional expression",
|
||||||
|
Expr::Dict(_) => "a dict literal",
|
||||||
|
Expr::Set(_) => "a set literal",
|
||||||
|
Expr::List(_) => "a list literal",
|
||||||
|
Expr::Tuple(_) => "a tuple literal",
|
||||||
|
Expr::Starred(_) => "starred expression",
|
||||||
|
Expr::Slice(_) => "slice expression",
|
||||||
|
Expr::BytesLiteral(_) => "a bytes literal",
|
||||||
|
Expr::StringLiteral(_) => "a string literal",
|
||||||
|
Expr::EllipsisLiteral(_) => "an ellipsis literal",
|
||||||
|
Expr::NoneLiteral(_) => "a `None` literal",
|
||||||
|
Expr::BooleanLiteral(_) => "a boolean literal",
|
||||||
|
Expr::ListComp(_) => "a list comprehension",
|
||||||
|
Expr::SetComp(_) => "a set comprehension",
|
||||||
|
Expr::DictComp(_) => "a dict comprehension",
|
||||||
|
Expr::Generator(_) => "generator expression",
|
||||||
|
Expr::Yield(_) => "`yield` expression",
|
||||||
|
Expr::YieldFrom(_) => "`yield from` expression",
|
||||||
|
Expr::Compare(_) => "comparison expression",
|
||||||
|
Expr::FString(_) => "f-string",
|
||||||
|
Expr::Named(_) => "assignment expression",
|
||||||
|
Expr::Subscript(_) => "subscript expression",
|
||||||
|
Expr::IpyEscapeCommand(_) => "IPython escape command",
|
||||||
|
};
|
||||||
|
|
||||||
|
Some((RelaxedDecoratorError::Other(description), expr.range()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -2705,17 +2705,45 @@ impl<'src> Parser<'src> {
|
||||||
// # parse_options: { "target-version": "3.7" }
|
// # parse_options: { "target-version": "3.7" }
|
||||||
// @(x := lambda x: x)(foo)
|
// @(x := lambda x: x)(foo)
|
||||||
// def bar(): ...
|
// def bar(): ...
|
||||||
let allowed_decorator = match &parsed_expr.expr {
|
|
||||||
|
// test_err decorator_dict_literal_py38
|
||||||
|
// # parse_options: { "target-version": "3.8" }
|
||||||
|
// @{3: 3}
|
||||||
|
// def bar(): ...
|
||||||
|
|
||||||
|
// test_err decorator_float_literal_py38
|
||||||
|
// # parse_options: { "target-version": "3.8" }
|
||||||
|
// @3.14
|
||||||
|
// def bar(): ...
|
||||||
|
|
||||||
|
// test_ok decorator_await_expression_py39
|
||||||
|
// # parse_options: { "target-version": "3.9" }
|
||||||
|
// async def foo():
|
||||||
|
// @await bar
|
||||||
|
// def baz(): ...
|
||||||
|
|
||||||
|
// test_err decorator_await_expression_py38
|
||||||
|
// # parse_options: { "target-version": "3.8" }
|
||||||
|
// async def foo():
|
||||||
|
// @await bar
|
||||||
|
// def baz(): ...
|
||||||
|
|
||||||
|
// test_err decorator_non_toplevel_call_expression_py38
|
||||||
|
// # parse_options: { "target-version": "3.8" }
|
||||||
|
// @foo().bar()
|
||||||
|
// def baz(): ...
|
||||||
|
|
||||||
|
let relaxed_decorator_error = match &parsed_expr.expr {
|
||||||
Expr::Call(expr_call) => {
|
Expr::Call(expr_call) => {
|
||||||
helpers::is_name_or_attribute_expression(&expr_call.func)
|
helpers::detect_invalid_pre_py39_decorator_node(&expr_call.func)
|
||||||
}
|
}
|
||||||
expr => helpers::is_name_or_attribute_expression(expr),
|
expr => helpers::detect_invalid_pre_py39_decorator_node(expr),
|
||||||
};
|
};
|
||||||
|
|
||||||
if !allowed_decorator {
|
if let Some((error, range)) = relaxed_decorator_error {
|
||||||
self.add_unsupported_syntax_error(
|
self.add_unsupported_syntax_error(
|
||||||
UnsupportedSyntaxErrorKind::RelaxedDecorator,
|
UnsupportedSyntaxErrorKind::RelaxedDecorator(error),
|
||||||
parsed_expr.range(),
|
range,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(): ...
|
||||||
|
|
|
|
@ -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(): ...
|
||||||
|
|
|
|
@ -96,6 +96,6 @@ Module(
|
||||||
|
|
|
|
||||||
1 | # parse_options: { "target-version": "3.8" }
|
1 | # parse_options: { "target-version": "3.8" }
|
||||||
2 | @buttons[0].clicked.connect
|
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(): ...
|
3 | def spam(): ...
|
||||||
|
|
|
|
||||||
|
|
|
@ -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(): ...
|
||||||
|
|
|
|
@ -128,6 +128,6 @@ Module(
|
||||||
|
|
|
|
||||||
1 | # parse_options: { "target-version": "3.7" }
|
1 | # parse_options: { "target-version": "3.7" }
|
||||||
2 | @(x := lambda x: x)(foo)
|
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(): ...
|
3 | def bar(): ...
|
||||||
|
|
|
|
||||||
|
|
|
@ -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(): ...
|
||||||
|
|
|
|
@ -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,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
Loading…
Add table
Add a link
Reference in a new issue