Skip to content

Commit 6311412

Browse files
authored
[syntax-errors] Star annotations before Python 3.11 (#16545)
Summary -- This is closely related to (and stacked on) #16544 and detects star annotations in function definitions. I initially called the variant `StarExpressionInAnnotation` to mirror `StarExpressionInIndex`, but I realized it's not really a "star expression" in this position and renamed it. `StarAnnotation` seems in line with the PEP. Test Plan -- Two new inline tests. It looked like there was pretty good existing coverage of this syntax, so I just added simple examples to test the version cutoff.
1 parent 4f28519 commit 6311412

File tree

6 files changed

+199
-2
lines changed

6 files changed

+199
-2
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# parse_options: {"target-version": "3.10"}
2+
def foo(*args: *Ts): ...
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# parse_options: {"target-version": "3.11"}
2+
def foo(*args: *Ts): ...

crates/ruff_python_parser/src/error.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,34 @@ pub enum UnsupportedSyntaxErrorKind {
644644
///
645645
/// [PEP 646]: https://peps.python.org/pep-0646/#change-1-star-expressions-in-indexes
646646
StarExpressionInIndex,
647+
648+
/// Represents the use of a [PEP 646] star annotations in a function definition.
649+
///
650+
/// ## Examples
651+
///
652+
/// Before Python 3.11, star annotations were not allowed in function definitions. This
653+
/// restriction was lifted in [PEP 646] to allow type annotations for `typing.TypeVarTuple`,
654+
/// also added in Python 3.11:
655+
///
656+
/// ```python
657+
/// from typing import TypeVarTuple
658+
///
659+
/// Ts = TypeVarTuple('Ts')
660+
///
661+
/// def foo(*args: *Ts): ...
662+
/// ```
663+
///
664+
/// Unlike [`UnsupportedSyntaxErrorKind::StarExpressionInIndex`], this does not include any
665+
/// other annotation positions:
666+
///
667+
/// ```python
668+
/// x: *Ts # Syntax error
669+
/// def foo(x: *Ts): ... # Syntax error
670+
/// ```
671+
///
672+
/// [PEP 646]: https://peps.python.org/pep-0646/#change-2-args-as-a-typevartuple
673+
StarAnnotation,
674+
647675
/// Represents the use of tuple unpacking in a `for` statement iterator clause before Python
648676
/// 3.9.
649677
///
@@ -699,6 +727,7 @@ impl Display for UnsupportedSyntaxError {
699727
UnsupportedSyntaxErrorKind::StarExpressionInIndex => {
700728
"Cannot use star expression in index"
701729
}
730+
UnsupportedSyntaxErrorKind::StarAnnotation => "Cannot use star annotation",
702731
UnsupportedSyntaxErrorKind::UnparenthesizedUnpackInFor => {
703732
"Cannot use iterable unpacking in `for` statements"
704733
}
@@ -750,6 +779,7 @@ impl UnsupportedSyntaxErrorKind {
750779
UnsupportedSyntaxErrorKind::StarExpressionInIndex => {
751780
Change::Added(PythonVersion::PY311)
752781
}
782+
UnsupportedSyntaxErrorKind::StarAnnotation => Change::Added(PythonVersion::PY311),
753783
UnsupportedSyntaxErrorKind::UnparenthesizedUnpackInFor => {
754784
Change::Added(PythonVersion::PY39)
755785
}

crates/ruff_python_parser/src/parser/statement.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2882,9 +2882,23 @@ impl<'src> Parser<'src> {
28822882
// def foo(*args: *int or str): ...
28832883
// def foo(*args: *yield x): ...
28842884
// # def foo(*args: **int): ...
2885-
self.parse_conditional_expression_or_higher_impl(
2885+
let parsed_expr = self.parse_conditional_expression_or_higher_impl(
28862886
ExpressionContext::starred_bitwise_or(),
2887-
)
2887+
);
2888+
2889+
// test_ok param_with_star_annotation_py311
2890+
// # parse_options: {"target-version": "3.11"}
2891+
// def foo(*args: *Ts): ...
2892+
2893+
// test_err param_with_star_annotation_py310
2894+
// # parse_options: {"target-version": "3.10"}
2895+
// def foo(*args: *Ts): ...
2896+
self.add_unsupported_syntax_error(
2897+
UnsupportedSyntaxErrorKind::StarAnnotation,
2898+
parsed_expr.range(),
2899+
);
2900+
2901+
parsed_expr
28882902
}
28892903
AllowStarAnnotation::No => {
28902904
// test_ok param_with_annotation
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
---
2+
source: crates/ruff_python_parser/tests/fixtures.rs
3+
input_file: crates/ruff_python_parser/resources/inline/err/param_with_star_annotation_py310.py
4+
---
5+
## AST
6+
7+
```
8+
Module(
9+
ModModule {
10+
range: 0..69,
11+
body: [
12+
FunctionDef(
13+
StmtFunctionDef {
14+
range: 44..68,
15+
is_async: false,
16+
decorator_list: [],
17+
name: Identifier {
18+
id: Name("foo"),
19+
range: 48..51,
20+
},
21+
type_params: None,
22+
parameters: Parameters {
23+
range: 51..63,
24+
posonlyargs: [],
25+
args: [],
26+
vararg: Some(
27+
Parameter {
28+
range: 52..62,
29+
name: Identifier {
30+
id: Name("args"),
31+
range: 53..57,
32+
},
33+
annotation: Some(
34+
Starred(
35+
ExprStarred {
36+
range: 59..62,
37+
value: Name(
38+
ExprName {
39+
range: 60..62,
40+
id: Name("Ts"),
41+
ctx: Load,
42+
},
43+
),
44+
ctx: Load,
45+
},
46+
),
47+
),
48+
},
49+
),
50+
kwonlyargs: [],
51+
kwarg: None,
52+
},
53+
returns: None,
54+
body: [
55+
Expr(
56+
StmtExpr {
57+
range: 65..68,
58+
value: EllipsisLiteral(
59+
ExprEllipsisLiteral {
60+
range: 65..68,
61+
},
62+
),
63+
},
64+
),
65+
],
66+
},
67+
),
68+
],
69+
},
70+
)
71+
```
72+
## Unsupported Syntax Errors
73+
74+
|
75+
1 | # parse_options: {"target-version": "3.10"}
76+
2 | def foo(*args: *Ts): ...
77+
| ^^^ Syntax Error: Cannot use star annotation on Python 3.10 (syntax was added in Python 3.11)
78+
|
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
source: crates/ruff_python_parser/tests/fixtures.rs
3+
input_file: crates/ruff_python_parser/resources/inline/ok/param_with_star_annotation_py311.py
4+
---
5+
## AST
6+
7+
```
8+
Module(
9+
ModModule {
10+
range: 0..69,
11+
body: [
12+
FunctionDef(
13+
StmtFunctionDef {
14+
range: 44..68,
15+
is_async: false,
16+
decorator_list: [],
17+
name: Identifier {
18+
id: Name("foo"),
19+
range: 48..51,
20+
},
21+
type_params: None,
22+
parameters: Parameters {
23+
range: 51..63,
24+
posonlyargs: [],
25+
args: [],
26+
vararg: Some(
27+
Parameter {
28+
range: 52..62,
29+
name: Identifier {
30+
id: Name("args"),
31+
range: 53..57,
32+
},
33+
annotation: Some(
34+
Starred(
35+
ExprStarred {
36+
range: 59..62,
37+
value: Name(
38+
ExprName {
39+
range: 60..62,
40+
id: Name("Ts"),
41+
ctx: Load,
42+
},
43+
),
44+
ctx: Load,
45+
},
46+
),
47+
),
48+
},
49+
),
50+
kwonlyargs: [],
51+
kwarg: None,
52+
},
53+
returns: None,
54+
body: [
55+
Expr(
56+
StmtExpr {
57+
range: 65..68,
58+
value: EllipsisLiteral(
59+
ExprEllipsisLiteral {
60+
range: 65..68,
61+
},
62+
),
63+
},
64+
),
65+
],
66+
},
67+
),
68+
],
69+
},
70+
)
71+
```

0 commit comments

Comments
 (0)