Skip to content

Commit f79f77c

Browse files
committed
[syntax-errors] Type parameter defaults before Python 3.13
Summary -- Detects the presence of a [PEP 696] type parameter default before Python 3.13. Test Plan -- New inline parser tests for type aliases, generic functions and generic classes. [PEP 696]: https://peps.python.org/pep-0696/#grammar-changes
1 parent e924ecb commit f79f77c

File tree

6 files changed

+404
-1
lines changed

6 files changed

+404
-1
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# parse_options: {"target-version": "3.12"}
2+
type X[T = int] = int
3+
def f[T = int](): ...
4+
class C[T = int](): ...
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# parse_options: {"target-version": "3.13"}
2+
type X[T = int] = int
3+
def f[T = int](): ...
4+
class C[T = int](): ...

crates/ruff_python_parser/src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,7 @@ pub enum UnsupportedSyntaxErrorKind {
449449
Match,
450450
Walrus,
451451
ExceptStar,
452+
TypeParamDefault,
452453
}
453454

454455
impl Display for UnsupportedSyntaxError {
@@ -457,6 +458,7 @@ impl Display for UnsupportedSyntaxError {
457458
UnsupportedSyntaxErrorKind::Match => "`match` statement",
458459
UnsupportedSyntaxErrorKind::Walrus => "named assignment expression (`:=`)",
459460
UnsupportedSyntaxErrorKind::ExceptStar => "`except*`",
461+
UnsupportedSyntaxErrorKind::TypeParamDefault => "type parameter default",
460462
};
461463
write!(
462464
f,
@@ -474,6 +476,7 @@ impl UnsupportedSyntaxErrorKind {
474476
UnsupportedSyntaxErrorKind::Match => PythonVersion::PY310,
475477
UnsupportedSyntaxErrorKind::Walrus => PythonVersion::PY38,
476478
UnsupportedSyntaxErrorKind::ExceptStar => PythonVersion::PY311,
479+
UnsupportedSyntaxErrorKind::TypeParamDefault => PythonVersion::PY313,
477480
}
478481
}
479482
}

crates/ruff_python_parser/src/parser/statement.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3267,8 +3267,28 @@ impl<'src> Parser<'src> {
32673267
None
32683268
};
32693269

3270+
// test_ok type_param_default_py313
3271+
// # parse_options: {"target-version": "3.13"}
3272+
// type X[T = int] = int
3273+
// def f[T = int](): ...
3274+
// class C[T = int](): ...
3275+
3276+
// test_err type_param_default_py312
3277+
// # parse_options: {"target-version": "3.12"}
3278+
// type X[T = int] = int
3279+
// def f[T = int](): ...
3280+
// class C[T = int](): ...
3281+
3282+
let range = self.node_range(start);
3283+
if default.is_some() {
3284+
self.add_unsupported_syntax_error(
3285+
UnsupportedSyntaxErrorKind::TypeParamDefault,
3286+
range,
3287+
);
3288+
}
3289+
32703290
ast::TypeParam::TypeVar(ast::TypeParamTypeVar {
3271-
range: self.node_range(start),
3291+
range,
32723292
name,
32733293
bound,
32743294
default,
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
---
2+
source: crates/ruff_python_parser/tests/fixtures.rs
3+
input_file: crates/ruff_python_parser/resources/inline/err/type_param_default_py312.py
4+
---
5+
## AST
6+
7+
```
8+
Module(
9+
ModModule {
10+
range: 0..112,
11+
body: [
12+
TypeAlias(
13+
StmtTypeAlias {
14+
range: 44..65,
15+
name: Name(
16+
ExprName {
17+
range: 49..50,
18+
id: Name("X"),
19+
ctx: Store,
20+
},
21+
),
22+
type_params: Some(
23+
TypeParams {
24+
range: 50..59,
25+
type_params: [
26+
TypeVar(
27+
TypeParamTypeVar {
28+
range: 51..58,
29+
name: Identifier {
30+
id: Name("T"),
31+
range: 51..52,
32+
},
33+
bound: None,
34+
default: Some(
35+
Name(
36+
ExprName {
37+
range: 55..58,
38+
id: Name("int"),
39+
ctx: Load,
40+
},
41+
),
42+
),
43+
},
44+
),
45+
],
46+
},
47+
),
48+
value: Name(
49+
ExprName {
50+
range: 62..65,
51+
id: Name("int"),
52+
ctx: Load,
53+
},
54+
),
55+
},
56+
),
57+
FunctionDef(
58+
StmtFunctionDef {
59+
range: 66..87,
60+
is_async: false,
61+
decorator_list: [],
62+
name: Identifier {
63+
id: Name("f"),
64+
range: 70..71,
65+
},
66+
type_params: Some(
67+
TypeParams {
68+
range: 71..80,
69+
type_params: [
70+
TypeVar(
71+
TypeParamTypeVar {
72+
range: 72..79,
73+
name: Identifier {
74+
id: Name("T"),
75+
range: 72..73,
76+
},
77+
bound: None,
78+
default: Some(
79+
Name(
80+
ExprName {
81+
range: 76..79,
82+
id: Name("int"),
83+
ctx: Load,
84+
},
85+
),
86+
),
87+
},
88+
),
89+
],
90+
},
91+
),
92+
parameters: Parameters {
93+
range: 80..82,
94+
posonlyargs: [],
95+
args: [],
96+
vararg: None,
97+
kwonlyargs: [],
98+
kwarg: None,
99+
},
100+
returns: None,
101+
body: [
102+
Expr(
103+
StmtExpr {
104+
range: 84..87,
105+
value: EllipsisLiteral(
106+
ExprEllipsisLiteral {
107+
range: 84..87,
108+
},
109+
),
110+
},
111+
),
112+
],
113+
},
114+
),
115+
ClassDef(
116+
StmtClassDef {
117+
range: 88..111,
118+
decorator_list: [],
119+
name: Identifier {
120+
id: Name("C"),
121+
range: 94..95,
122+
},
123+
type_params: Some(
124+
TypeParams {
125+
range: 95..104,
126+
type_params: [
127+
TypeVar(
128+
TypeParamTypeVar {
129+
range: 96..103,
130+
name: Identifier {
131+
id: Name("T"),
132+
range: 96..97,
133+
},
134+
bound: None,
135+
default: Some(
136+
Name(
137+
ExprName {
138+
range: 100..103,
139+
id: Name("int"),
140+
ctx: Load,
141+
},
142+
),
143+
),
144+
},
145+
),
146+
],
147+
},
148+
),
149+
arguments: Some(
150+
Arguments {
151+
range: 104..106,
152+
args: [],
153+
keywords: [],
154+
},
155+
),
156+
body: [
157+
Expr(
158+
StmtExpr {
159+
range: 108..111,
160+
value: EllipsisLiteral(
161+
ExprEllipsisLiteral {
162+
range: 108..111,
163+
},
164+
),
165+
},
166+
),
167+
],
168+
},
169+
),
170+
],
171+
},
172+
)
173+
```
174+
## Unsupported Syntax Errors
175+
176+
|
177+
1 | # parse_options: {"target-version": "3.12"}
178+
2 | type X[T = int] = int
179+
| ^^^^^^^ Syntax Error: Cannot use type parameter default on Python 3.12 (syntax was added in Python 3.13)
180+
3 | def f[T = int](): ...
181+
4 | class C[T = int](): ...
182+
|
183+
184+
185+
|
186+
1 | # parse_options: {"target-version": "3.12"}
187+
2 | type X[T = int] = int
188+
3 | def f[T = int](): ...
189+
| ^^^^^^^ Syntax Error: Cannot use type parameter default on Python 3.12 (syntax was added in Python 3.13)
190+
4 | class C[T = int](): ...
191+
|
192+
193+
194+
|
195+
2 | type X[T = int] = int
196+
3 | def f[T = int](): ...
197+
4 | class C[T = int](): ...
198+
| ^^^^^^^ Syntax Error: Cannot use type parameter default on Python 3.12 (syntax was added in Python 3.13)
199+
|

0 commit comments

Comments
 (0)