https://github.com/python/cpython/commit/1034e73f699e858b2b7700d9e9b9ef692ccbad66 commit: 1034e73f699e858b2b7700d9e9b9ef692ccbad66 branch: main author: Serhiy Storchaka <[email protected]> committer: serhiy-storchaka <[email protected]> date: 2026-07-06T13:00:05+03:00 summary:
gh-153171: Produce specialized syntax errors for 'not' after an operator in more positions (GH-153175) Move the invalid_factor rule from term to factor and the invalid_arithmetic rule from shift_expr to sum, so that they are tried at all positions where the invalid construct can occur. Previously a generic "invalid syntax" error was reported for constructs like "1 << 2 + not x" or "1 * + not x". Co-authored-by: Claude Fable 5 <[email protected]> files: A Misc/NEWS.d/next/Core_and_Builtins/2026-07-06-18-00-00.gh-issue-153171.errloc1.rst M Grammar/python.gram M Lib/test/test_syntax.py M Parser/parser.c diff --git a/Grammar/python.gram b/Grammar/python.gram index 221629a6230270d..ac1b4a64f38c75a 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -835,7 +835,6 @@ 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_arithmetic | sum # Arithmetic operators @@ -844,6 +843,7 @@ shift_expr[expr_ty]: sum[expr_ty]: | a=sum '+' b=term { _PyAST_BinOp(a, Add, b, EXTRA) } | a=sum '-' b=term { _PyAST_BinOp(a, Sub, b, EXTRA) } + | invalid_arithmetic | term term[expr_ty]: @@ -852,7 +852,6 @@ term[expr_ty]: | a=term '//' b=factor { _PyAST_BinOp(a, FloorDiv, b, EXTRA) } | a=term '%' b=factor { _PyAST_BinOp(a, Mod, b, EXTRA) } | a=term '@' b=factor { CHECK_VERSION(expr_ty, 5, "The '@' operator is", _PyAST_BinOp(a, MatMult, b, EXTRA)) } - | invalid_factor | factor factor[expr_ty] (memo): @@ -860,6 +859,7 @@ factor[expr_ty] (memo): | '-' a=factor { _PyAST_UnaryOp(USub, a, EXTRA) } | '~' a=factor { _PyAST_UnaryOp(Invert, a, EXTRA) } | power + | invalid_factor power[expr_ty]: | a=await_primary '**' b=factor { _PyAST_BinOp(a, Pow, b, EXTRA) } diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 013917614178fde..29278d928fbb402 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -2268,6 +2268,22 @@ Traceback (most recent call last): SyntaxError: 'not' after an operator must be parenthesized +>>> 1 << 2 + not 3 +Traceback (most recent call last): +SyntaxError: 'not' after an operator must be parenthesized + +>>> 1 >> 2 * not 3 +Traceback (most recent call last): +SyntaxError: 'not' after an operator must be parenthesized + +>>> 3 * + not 3 +Traceback (most recent call last): +SyntaxError: 'not' after an operator must be parenthesized + +>>> 3 ** - not 3 +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): diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-07-06-18-00-00.gh-issue-153171.errloc1.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-06-18-00-00.gh-issue-153171.errloc1.rst new file mode 100644 index 000000000000000..aa3171b701894d7 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-07-06-18-00-00.gh-issue-153171.errloc1.rst @@ -0,0 +1,3 @@ +Improve syntax error messages for misplaced ``not`` after an operator +in contexts where a generic "invalid syntax" error was previously reported, +such as ``1 << 2 + not x`` and ``1 * + not x``. diff --git a/Parser/parser.c b/Parser/parser.c index 8f218077a8b9033..4f4fd3ed46d1f02 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -360,7 +360,7 @@ static char *soft_keywords[] = { #define invalid_tstring_replacement_field_type 1271 #define invalid_tstring_conversion_character_type 1272 #define invalid_string_tstring_concat_type 1273 -#define invalid_arithmetic_type 1274 +#define invalid_arithmetic_type 1274 // Left-recursive #define invalid_factor_type 1275 #define invalid_type_params_type 1276 #define invalid_bitwise_and_type 1277 // Left-recursive @@ -13933,7 +13933,7 @@ bitwise_and_raw(Parser *p) } // Left-recursive -// shift_expr: shift_expr '<<' sum | shift_expr '>>' sum | invalid_arithmetic | sum +// shift_expr: shift_expr '<<' sum | shift_expr '>>' sum | sum static expr_ty shift_expr_raw(Parser *); static expr_ty shift_expr_rule(Parser *p) @@ -14068,25 +14068,6 @@ shift_expr_raw(Parser *p) D(fprintf(stderr, "%*c%s shift_expr[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "shift_expr '>>' sum")); } - if (p->call_invalid_rules) { // invalid_arithmetic - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> shift_expr[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_arithmetic")); - void *invalid_arithmetic_var; - if ( - (invalid_arithmetic_var = invalid_arithmetic_rule(p)) // invalid_arithmetic - ) - { - D(fprintf(stderr, "%*c+ shift_expr[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_arithmetic")); - _res = invalid_arithmetic_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s shift_expr[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_arithmetic")); - } { // sum if (p->error_indicator) { p->level--; @@ -14113,7 +14094,7 @@ shift_expr_raw(Parser *p) } // Left-recursive -// sum: sum '+' term | sum '-' term | term +// sum: sum '+' term | sum '-' term | invalid_arithmetic | term static expr_ty sum_raw(Parser *); static expr_ty sum_rule(Parser *p) @@ -14248,6 +14229,25 @@ sum_raw(Parser *p) D(fprintf(stderr, "%*c%s sum[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "sum '-' term")); } + if (p->call_invalid_rules) { // invalid_arithmetic + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> sum[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_arithmetic")); + void *invalid_arithmetic_var; + if ( + (invalid_arithmetic_var = invalid_arithmetic_rule(p)) // invalid_arithmetic + ) + { + D(fprintf(stderr, "%*c+ sum[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_arithmetic")); + _res = invalid_arithmetic_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s sum[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_arithmetic")); + } { // term if (p->error_indicator) { p->level--; @@ -14280,7 +14280,6 @@ sum_raw(Parser *p) // | term '//' factor // | term '%' factor // | term '@' factor -// | invalid_factor // | factor static expr_ty term_raw(Parser *); static expr_ty @@ -14533,25 +14532,6 @@ term_raw(Parser *p) D(fprintf(stderr, "%*c%s term[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "term '@' factor")); } - if (p->call_invalid_rules) { // invalid_factor - if (p->error_indicator) { - p->level--; - return NULL; - } - D(fprintf(stderr, "%*c> term[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_factor")); - void *invalid_factor_var; - if ( - (invalid_factor_var = invalid_factor_rule(p)) // invalid_factor - ) - { - D(fprintf(stderr, "%*c+ term[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_factor")); - _res = invalid_factor_var; - goto done; - } - p->mark = _mark; - D(fprintf(stderr, "%*c%s term[%d-%d]: %s failed!\n", p->level, ' ', - p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_factor")); - } { // factor if (p->error_indicator) { p->level--; @@ -14577,7 +14557,7 @@ term_raw(Parser *p) return _res; } -// factor: '+' factor | '-' factor | '~' factor | power +// factor: '+' factor | '-' factor | '~' factor | power | invalid_factor static expr_ty factor_rule(Parser *p) { @@ -14730,6 +14710,25 @@ factor_rule(Parser *p) D(fprintf(stderr, "%*c%s factor[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "power")); } + if (p->call_invalid_rules) { // invalid_factor + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> factor[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "invalid_factor")); + void *invalid_factor_var; + if ( + (invalid_factor_var = invalid_factor_rule(p)) // invalid_factor + ) + { + D(fprintf(stderr, "%*c+ factor[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "invalid_factor")); + _res = invalid_factor_var; + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s factor[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_factor")); + } _res = NULL; done: _PyPegen_insert_memo(p, _mark, factor_type, _res); @@ -28410,6 +28409,7 @@ invalid_string_tstring_concat_rule(Parser *p) return _res; } +// Left-recursive // invalid_arithmetic: sum ('+' | '-' | '*' | '/' | '%' | '//' | '@') 'not' inversion static void * invalid_arithmetic_rule(Parser *p) _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
