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]

Reply via email to