Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion Grammar/python.gram
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,7 @@ inversion[expr_ty] (memo):
# --------------------

comparison[expr_ty]:
| invalid_comparison
| a=bitwise_or b=compare_op_bitwise_or_pair+ {
_PyAST_Compare(
a,
Expand Down Expand Up @@ -825,6 +826,7 @@ bitwise_or[expr_ty]:

bitwise_xor[expr_ty]:
| a=bitwise_xor '^' b=bitwise_and { _PyAST_BinOp(a, BitXor, b, EXTRA) }
| invalid_bitwise_xor
| bitwise_and

bitwise_and[expr_ty]:
Expand All @@ -835,6 +837,7 @@ bitwise_and[expr_ty]:
shift_expr[expr_ty]:
| a=shift_expr '<<' b=sum { _PyAST_BinOp(a, LShift, b, EXTRA) }
| a=shift_expr '>>' b=sum { _PyAST_BinOp(a, RShift, b, EXTRA) }
| invalid_shift_expr
| sum

# Arithmetic operators
Expand Down Expand Up @@ -863,6 +866,7 @@ factor[expr_ty] (memo):

power[expr_ty]:
| a=await_primary '**' b=factor { _PyAST_BinOp(a, Pow, b, EXTRA) }
| invalid_power
| await_primary

# Primary elements
Expand Down Expand Up @@ -1373,7 +1377,7 @@ invalid_parameters:
| param_maybe_default+ '/' a='*' {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expected comma between / and *") }
invalid_default:
| a='=' &(')'|',') { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expected default value expression") }
| a='=' &(')'|','|':') { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expected default value expression") }
invalid_star_etc:
| a='*' (')' | ',' (')' | '**')) { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "named parameters must follow bare *") }
| '*' ',' TYPE_COMMENT { RAISE_SYNTAX_ERROR("bare * has associated type comment") }
Expand Down Expand Up @@ -1640,21 +1644,33 @@ invalid_arithmetic:
| sum ('+'|'-'|'*'|'/'|'%'|'//'|'@') a='not' b=inversion { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "'not' after an operator must be parenthesized") }
invalid_factor:
| ('+' | '-' | '~') a='not' b=factor { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "'not' after an operator must be parenthesized") }
invalid_comparison:
| bitwise_or ('=='|'!='|'<='|'<'|'>='|'>') a='not' b=inversion { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "'not' after an operator must be parenthesized") }
invalid_bitwise_xor:
| bitwise_xor '^' a='not' b=inversion { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "'not' after an operator must be parenthesized") }
invalid_shift_expr:
| shift_expr ('<<'|'>>') a='not' b=inversion { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "'not' after an operator must be parenthesized") }
invalid_power:
| await_primary '**' a='not' b=inversion { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "'not' after an operator must be parenthesized") }

invalid_type_params:
| '[' ','.type_param+ a='=' &(']' | ',') {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expected default value expression") }
| '[' token=']' {
RAISE_SYNTAX_ERROR_STARTING_FROM(
token,
"Type parameter list cannot be empty")}

invalid_bitwise_and:
| bitwise_and '&' a='not' b=inversion { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "'not' after an operator must be parenthesized") }
| a=bitwise_and b='&' c='&' {
_PyPegen_tokens_are_adjacent(b, c)
? RAISE_SYNTAX_ERROR_KNOWN_RANGE(b, c, "invalid syntax. Maybe you meant 'and' or '&' instead of '&&'?")
: NULL
}

invalid_bitwise_or:
| bitwise_or '|' a='not' b=inversion { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "'not' after an operator must be parenthesized") }
| a=bitwise_or b='|' c='|' {
_PyPegen_tokens_are_adjacent(b, c)
? RAISE_SYNTAX_ERROR_KNOWN_RANGE(b, c, "invalid syntax. Maybe you meant 'or' or '|' instead of '||'?")
Expand Down
56 changes: 56 additions & 0 deletions Lib/test/test_syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,10 @@
Traceback (most recent call last):
SyntaxError: expected default value expression

>>> lambda a,d=: None
Traceback (most recent call last):
SyntaxError: expected default value expression

>>> lambda a,d=3,c: None
Traceback (most recent call last):
SyntaxError: parameter without a default follows parameter with a default
Expand Down Expand Up @@ -2284,6 +2288,38 @@
Traceback (most recent call last):
SyntaxError: 'not' after an operator must be parenthesized

>>> 1 << not 2
Traceback (most recent call last):
SyntaxError: 'not' after an operator must be parenthesized

>>> 1 >> not 2
Traceback (most recent call last):
SyntaxError: 'not' after an operator must be parenthesized

>>> 1 & not 2
Traceback (most recent call last):
SyntaxError: 'not' after an operator must be parenthesized

>>> 1 ^ not 2
Traceback (most recent call last):
SyntaxError: 'not' after an operator must be parenthesized

>>> 1 | not 2
Traceback (most recent call last):
SyntaxError: 'not' after an operator must be parenthesized

>>> 1 < not 2
Traceback (most recent call last):
SyntaxError: 'not' after an operator must be parenthesized

>>> 1 == not 2
Traceback (most recent call last):
SyntaxError: 'not' after an operator must be parenthesized

>>> 2 ** not 2
Traceback (most recent call last):
SyntaxError: 'not' after an operator must be parenthesized

# Check that we don't introduce misleading errors
>>> not 1 */ 2
Traceback (most recent call last):
Expand Down Expand Up @@ -2588,6 +2624,26 @@ def f(x: *b)
...
SyntaxError: Type parameter list cannot be empty

>>> type A[T=] = int
Traceback (most recent call last):
...
SyntaxError: expected default value expression

>>> def f[T: int =](): ...
Traceback (most recent call last):
...
SyntaxError: expected default value expression

>>> class A[*T=]: ...
Traceback (most recent call last):
...
SyntaxError: expected default value expression

>>> class A[**T=, U]: ...
Traceback (most recent call last):
...
SyntaxError: expected default value expression

>>> class A[]: ...
Traceback (most recent call last):
...
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Produce specialized syntax error messages in more cases
where a generic or unrelated error was previously reported:
for ``not`` after shift, bitwise, comparison and power operators
(such as ``1 << not x``),
for a missing parameter default value in lambda expressions
(such as ``lambda x=: 0``),
and for a missing type parameter default value
(such as ``def f[T=](): pass``).
Loading
Loading