Skip to content

Commit 49405ac

Browse files
committed
improve error reporting for invalid starred expressions
1 parent c23496e commit 49405ac

File tree

4 files changed

+2609
-1882
lines changed

4 files changed

+2609
-1882
lines changed

Grammar/python.gram

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -709,12 +709,17 @@ expressions[expr_ty]:
709709
| expression
710710

711711
expression[expr_ty] (memo):
712+
| invalid_if_expression
712713
| invalid_expression
713714
| invalid_legacy_expression
714-
| a=disjunction 'if' b=disjunction 'else' c=expression { _PyAST_IfExp(b, a, c, EXTRA) }
715+
| if_expression
715716
| disjunction
716717
| lambdef
717718

719+
if_expression[expr_ty]:
720+
| a=disjunction 'if' b=disjunction 'else' c=expression { _PyAST_IfExp(b, a, c, EXTRA) }
721+
| invalid_if_expression
722+
718723
yield_expr[expr_ty]:
719724
| 'yield' 'from' a=expression { _PyAST_YieldFrom(a, EXTRA) }
720725
| 'yield' a=[star_expressions] { _PyAST_Yield(a, EXTRA) }
@@ -731,10 +736,16 @@ star_expression[expr_ty] (memo):
731736

732737
star_named_expressions[asdl_expr_seq*]: a[asdl_expr_seq*]=','.star_named_expression+ [','] { a }
733738

739+
star_named_expressions_sequence[asdl_expr_seq*]: a[asdl_expr_seq*]=','.star_named_expression_sequence+ [','] { a }
740+
734741
star_named_expression[expr_ty]:
735742
| '*' a=bitwise_or { _PyAST_Starred(a, Load, EXTRA) }
736743
| named_expression
737744

745+
star_named_expression_sequence[expr_ty]:
746+
| invalid_starred_expression_unpacking_sequence
747+
| star_named_expression
748+
738749
assignment_expression[expr_ty]:
739750
| a=NAME ':=' ~ b=expression {
740751
CHECK_VERSION(expr_ty, 8, "Assignment expressions are",
@@ -998,13 +1009,13 @@ strings[expr_ty] (memo):
9981009
| a[asdl_expr_seq*]=tstring+ { _PyPegen_concatenate_tstrings(p, a, EXTRA) }
9991010

10001011
list[expr_ty]:
1001-
| '[' a=[star_named_expressions] ']' { _PyAST_List(a, Load, EXTRA) }
1012+
| '[' a=[star_named_expressions_sequence] ']' { _PyAST_List(a, Load, EXTRA) }
10021013

10031014
tuple[expr_ty]:
1004-
| '(' a=[y=star_named_expression ',' z=[star_named_expressions] { _PyPegen_seq_insert_in_front(p, y, z) } ] ')' {
1015+
| '(' a=[y=star_named_expression_sequence ',' z=[star_named_expressions_sequence] { _PyPegen_seq_insert_in_front(p, y, z) } ] ')' {
10051016
_PyAST_Tuple(a, Load, EXTRA) }
10061017

1007-
set[expr_ty]: '{' a=star_named_expressions '}' { _PyAST_Set(a, EXTRA) }
1018+
set[expr_ty]: '{' a=star_named_expressions_sequence '}' { _PyAST_Set(a, EXTRA) }
10081019

10091020
# Dicts
10101021
# -----
@@ -1262,6 +1273,12 @@ invalid_expression:
12621273
| a='lambda' [lambda_params] b=':' &TSTRING_MIDDLE {
12631274
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "t-string: lambda expressions are not allowed without parentheses") }
12641275

1276+
invalid_if_expression:
1277+
| disjunction 'if' b=disjunction 'else' a='*' {
1278+
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "cannot unpack only part of a conditional expression") }
1279+
| disjunction 'if' b=disjunction 'else' a='**' {
1280+
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "cannot use dict unpacking on only part of a conditional expression") }
1281+
12651282
invalid_named_expression(memo):
12661283
| a=expression ':=' expression {
12671284
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
@@ -1529,21 +1546,32 @@ invalid_class_def_raw:
15291546
RAISE_INDENTATION_ERROR("expected an indented block after class definition on line %d", a->lineno) }
15301547

15311548
invalid_double_starred_kvpairs:
1532-
| ','.double_starred_kvpair+ ',' invalid_kvpair
1533-
| expression ':' a='*' bitwise_or { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "cannot use a starred expression in a dictionary value") }
1534-
| expression ':' a='**' bitwise_or { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "cannot use dict unpacking in a dictionary value") }
1549+
| invalid_kvpair_unpacking [',']
1550+
| ','.double_starred_kvpair+ ',' (invalid_kvpair | invalid_kvpair_unpacking)
15351551
| expression a=':' &('}'|',') { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expression expected after dictionary key and ':'") }
1552+
invalid_kvpair_unpacking:
1553+
| a='**' b=if_expression {
1554+
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "invalid double starred expression. Did you forget to wrap the conditional expression in parentheses?") }
1555+
| a='*' b=bitwise_or ':' expression { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "cannot use a starred expression in a dictionary key") }
1556+
| a='**' b=bitwise_or ':' expression { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "cannot use dict unpacking in a dictionary key") }
1557+
| expression ':' a='*' b=bitwise_or { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "cannot use a starred expression in a dictionary value") }
1558+
| expression ':' a='**' b=bitwise_or { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "cannot use dict unpacking in a dictionary value") }
15361559
invalid_kvpair:
15371560
| a=expression !(':') {
15381561
RAISE_ERROR_KNOWN_LOCATION(p, PyExc_SyntaxError, a->lineno, a->end_col_offset - 1, a->end_lineno, -1, "':' expected after dictionary key") }
15391562
| expression ':' a='*' bitwise_or { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "cannot use a starred expression in a dictionary value") }
15401563
| expression ':' a='**' bitwise_or { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "cannot use dict unpacking in a dictionary value") }
15411564
| expression a=':' &('}'|',') {RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expression expected after dictionary key and ':'") }
15421565
invalid_starred_expression_unpacking:
1566+
| a='*' b=if_expression {
1567+
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "invalid starred expression. Did you forget to wrap the conditional expression in parentheses?") }
15431568
| a='*' expression '=' b=expression { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "cannot assign to iterable argument unpacking") }
1569+
invalid_starred_expression_unpacking_sequence:
1570+
| a='**' bitwise_or {
1571+
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "cannot use dict unpacking here") }
1572+
| invalid_starred_expression_unpacking
15441573
invalid_starred_expression:
15451574
| '*' { RAISE_SYNTAX_ERROR("Invalid star expression") }
1546-
15471575
invalid_fstring_replacement_field:
15481576
| '{' a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '='") }
15491577
| '{' a='!' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "f-string: valid expression required before '!'") }

Lib/test/test_exceptions.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,6 @@ def baz():
337337
check('x=1\nfrom __future__ import division', 2, 1)
338338
check('foo(1=2)', 1, 5)
339339
check('def f():\n x, y: int', 2, 3)
340-
check('[*x for x in xs]', 1, 2)
341340
check('foo(x for x in range(10), 100)', 1, 5)
342341
check('for 1 in []: pass', 1, 5)
343342
check('(yield i) = 2', 1, 2)

Lib/test/test_unpack_ex.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,6 @@
340340
SyntaxError: cannot use dict unpacking in a dictionary value
341341
342342
343-
# Pegen is better here.
344343
# Generator expression in function arguments
345344
346345
>>> list(*x for x in (range(5) for i in range(3)))

0 commit comments

Comments
 (0)