Skip to content

Commit 150a28a

Browse files
committed
pythongh-125326: Improve SyntaxError for invalid type params definiton
1 parent 5d8739e commit 150a28a

File tree

4 files changed

+485
-295
lines changed

4 files changed

+485
-295
lines changed

Grammar/python.gram

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -649,9 +649,10 @@ type_param_seq[asdl_type_param_seq*]: a[asdl_type_param_seq*]=','.type_param+ ['
649649

650650
type_param[type_param_ty] (memo):
651651
| a=NAME b=[type_param_bound] c=[type_param_default] { _PyAST_TypeVar(a->v.Name.id, b, c, EXTRA) }
652-
| invalid_type_param
652+
| invalid_type_param_def
653653
| '*' a=NAME b=[type_param_starred_default] { _PyAST_TypeVarTuple(a->v.Name.id, b, EXTRA) }
654654
| '**' a=NAME b=[type_param_default] { _PyAST_ParamSpec(a->v.Name.id, b, EXTRA) }
655+
| invalid_type_param_expr
655656

656657
type_param_bound[expr_ty]: ':' e=expression { e }
657658
type_param_default[expr_ty]: '=' e=expression {
@@ -1165,7 +1166,15 @@ invalid_legacy_expression:
11651166
_PyPegen_check_legacy_stmt(p, a) ? RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b,
11661167
"Missing parentheses in call to '%U'. Did you mean %U(...)?", a->v.Name.id, a->v.Name.id) : NULL}
11671168

1168-
invalid_type_param:
1169+
# Invalid type parameters:
1170+
1171+
invalid_type_params:
1172+
| '[' token=']' {
1173+
RAISE_SYNTAX_ERROR_STARTING_FROM(
1174+
token,
1175+
"Type parameter list cannot be empty")}
1176+
1177+
invalid_type_param_def:
11691178
| '*' a=NAME colon=':' e=expression {
11701179
RAISE_SYNTAX_ERROR_STARTING_FROM(colon, e->kind == Tuple_kind
11711180
? "cannot use constraints with TypeVarTuple"
@@ -1177,6 +1186,23 @@ invalid_type_param:
11771186
: "cannot use bound with ParamSpec")
11781187
}
11791188

1189+
invalid_type_param_expr:
1190+
| a=expression [type_param_bound] [type_param_default] {
1191+
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
1192+
a, "expected a type parameter definition, found %s",
1193+
_PyPegen_get_expr_name(a))
1194+
}
1195+
| '*' a=expression [type_param_starred_default] {
1196+
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
1197+
a, "expected a type parameter definition, found %s",
1198+
_PyPegen_get_expr_name(a))
1199+
}
1200+
| '**' a=expression [type_param_default] {
1201+
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
1202+
a, "expected a type parameter definition, found %s",
1203+
_PyPegen_get_expr_name(a))
1204+
}
1205+
11801206
invalid_expression:
11811207
# !(NAME STRING) is not matched so we don't show this error with some invalid string prefixes like: kf"dsfsdf"
11821208
# Soft keywords need to also be ignored because they can be parsed as NAME NAME
@@ -1453,9 +1479,3 @@ invalid_arithmetic:
14531479
| sum ('+'|'-'|'*'|'/'|'%'|'//'|'@') a='not' b=inversion { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "'not' after an operator must be parenthesized") }
14541480
invalid_factor:
14551481
| ('+' | '-' | '~') a='not' b=factor { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "'not' after an operator must be parenthesized") }
1456-
1457-
invalid_type_params:
1458-
| '[' token=']' {
1459-
RAISE_SYNTAX_ERROR_STARTING_FROM(
1460-
token,
1461-
"Type parameter list cannot be empty")}

Lib/test/test_syntax.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2175,6 +2175,36 @@ def f(x: *b)
21752175
...
21762176
SyntaxError: Type parameter list cannot be empty
21772177
2178+
>>> type A[1] = ...
2179+
Traceback (most recent call last):
2180+
...
2181+
SyntaxError: expected a type parameter definition, found literal
2182+
2183+
>>> type A[*()] = ...
2184+
Traceback (most recent call last):
2185+
...
2186+
SyntaxError: expected a type parameter definition, found tuple
2187+
2188+
>>> type A[**{}] = ...
2189+
Traceback (most recent call last):
2190+
...
2191+
SyntaxError: expected a type parameter definition, found dict literal
2192+
2193+
>>> def some["abc"](): ...
2194+
Traceback (most recent call last):
2195+
...
2196+
SyntaxError: expected a type parameter definition, found literal
2197+
2198+
>>> class Some[*...]: ...
2199+
Traceback (most recent call last):
2200+
...
2201+
SyntaxError: expected a type parameter definition, found ellipsis
2202+
2203+
>>> async def some[**{}](): ...
2204+
Traceback (most recent call last):
2205+
...
2206+
SyntaxError: expected a type parameter definition, found dict literal
2207+
21782208
>>> def f[T: (x:=3)](): pass
21792209
Traceback (most recent call last):
21802210
...
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improve :exc:`SyntaxError` message for using invalid type parameters
2+
definition.

0 commit comments

Comments
 (0)