]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-45764: improve error message when missing '(' after 'def' (GH-29484)
authorCarl Friedrich Bolz-Tereick <cfbolz@gmx.de>
Tue, 9 Nov 2021 14:03:32 +0000 (15:03 +0100)
committerGitHub <noreply@github.com>
Tue, 9 Nov 2021 14:03:32 +0000 (06:03 -0800)
to achieve this, change the grammar to expect the '(' token after 'def' NAME.

Automerge-Triggered-By: GH:pablogsal
Grammar/python.gram
Lib/test/test_syntax.py
Misc/NEWS.d/next/Core and Builtins/2021-11-09-12-19-22.bpo-45764.8RLhWL.rst [new file with mode: 0644]
Parser/parser.c

index ffe035f037101697fb22b50d3fd1e6fbaf7a7bfe..d1901a5a9a2bc71f292dad9580ebde9609b7ace4 100644 (file)
@@ -264,11 +264,11 @@ function_def[stmt_ty]:
 
 function_def_raw[stmt_ty]:
     | invalid_def_raw
-    | 'def' n=NAME '(' params=[params] ')' a=['->' z=expression { z }] &&':' tc=[func_type_comment] b=block {
+    | 'def' n=NAME &&'(' params=[params] ')' a=['->' z=expression { z }] &&':' tc=[func_type_comment] b=block {
         _PyAST_FunctionDef(n->v.Name.id,
                         (params) ? params : CHECK(arguments_ty, _PyPegen_empty_arguments(p)),
                         b, NULL, a, NEW_TYPE_COMMENT(p, tc), EXTRA) }
-    | ASYNC 'def' n=NAME '(' params=[params] ')' a=['->' z=expression { z }] &&':' tc=[func_type_comment] b=block {
+    | ASYNC 'def' n=NAME &&'(' params=[params] ')' a=['->' z=expression { z }] &&':' tc=[func_type_comment] b=block {
         CHECK_VERSION(
             stmt_ty,
             5,
index 014a30aa2a612cbff99852a6ec5264af36fee210..65d18e4941be58c237bca56a8f8aa76096fb7606 100644 (file)
@@ -898,6 +898,17 @@ leading to spurious errors.
    Traceback (most recent call last):
    SyntaxError: cannot assign to attribute here. Maybe you meant '==' instead of '='?
 
+
+Missing parens after function definition
+
+   >>> def f:
+   Traceback (most recent call last):
+   SyntaxError: expected '('
+
+   >>> async def f:
+   Traceback (most recent call last):
+   SyntaxError: expected '('
+
 Custom error messages for try blocks that are not followed by except/finally
 
    >>> try:
diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-11-09-12-19-22.bpo-45764.8RLhWL.rst b/Misc/NEWS.d/next/Core and Builtins/2021-11-09-12-19-22.bpo-45764.8RLhWL.rst
new file mode 100644 (file)
index 0000000..d30b6a4
--- /dev/null
@@ -0,0 +1,9 @@
+The parser now gives a better error message when leaving out the opening
+parenthesis ``(`` after a ``def``-statement::
+
+    >>> def f:
+      File "<stdin>", line 1
+        def f:
+             ^
+    SyntaxError: expected '('
+
index 8ef7980d66b5917adf221571edf042d4c588bcf0..b508c1ddec15cb5d0aae2f3808ef48d34653e37c 100644 (file)
@@ -4157,8 +4157,8 @@ function_def_rule(Parser *p)
 
 // function_def_raw:
 //     | invalid_def_raw
-//     | 'def' NAME '(' params? ')' ['->' expression] &&':' func_type_comment? block
-//     | ASYNC 'def' NAME '(' params? ')' ['->' expression] &&':' func_type_comment? block
+//     | 'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block
+//     | ASYNC 'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block
 static stmt_ty
 function_def_raw_rule(Parser *p)
 {
@@ -4197,12 +4197,12 @@ function_def_raw_rule(Parser *p)
         D(fprintf(stderr, "%*c%s function_def_raw[%d-%d]: %s failed!\n", p->level, ' ',
                   p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_def_raw"));
     }
-    { // 'def' NAME '(' params? ')' ['->' expression] &&':' func_type_comment? block
+    { // 'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> function_def_raw[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'def' NAME '(' params? ')' ['->' expression] &&':' func_type_comment? block"));
+        D(fprintf(stderr, "%*c> function_def_raw[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block"));
         Token * _keyword;
         Token * _literal;
         Token * _literal_1;
@@ -4217,7 +4217,7 @@ function_def_raw_rule(Parser *p)
             &&
             (n = _PyPegen_name_token(p))  // NAME
             &&
-            (_literal = _PyPegen_expect_token(p, 7))  // token='('
+            (_literal = _PyPegen_expect_forced_token(p, 7, "("))  // forced_token='('
             &&
             (params = params_rule(p), !p->error_indicator)  // params?
             &&
@@ -4232,7 +4232,7 @@ function_def_raw_rule(Parser *p)
             (b = block_rule(p))  // block
         )
         {
-            D(fprintf(stderr, "%*c+ function_def_raw[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'def' NAME '(' params? ')' ['->' expression] &&':' func_type_comment? block"));
+            D(fprintf(stderr, "%*c+ function_def_raw[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block"));
             Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
             if (_token == NULL) {
                 D(p->level--);
@@ -4252,14 +4252,14 @@ function_def_raw_rule(Parser *p)
         }
         p->mark = _mark;
         D(fprintf(stderr, "%*c%s function_def_raw[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'def' NAME '(' params? ')' ['->' expression] &&':' func_type_comment? block"));
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block"));
     }
-    { // ASYNC 'def' NAME '(' params? ')' ['->' expression] &&':' func_type_comment? block
+    { // ASYNC 'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block
         if (p->error_indicator) {
             D(p->level--);
             return NULL;
         }
-        D(fprintf(stderr, "%*c> function_def_raw[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "ASYNC 'def' NAME '(' params? ')' ['->' expression] &&':' func_type_comment? block"));
+        D(fprintf(stderr, "%*c> function_def_raw[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "ASYNC 'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block"));
         Token * _keyword;
         Token * _literal;
         Token * _literal_1;
@@ -4277,7 +4277,7 @@ function_def_raw_rule(Parser *p)
             &&
             (n = _PyPegen_name_token(p))  // NAME
             &&
-            (_literal = _PyPegen_expect_token(p, 7))  // token='('
+            (_literal = _PyPegen_expect_forced_token(p, 7, "("))  // forced_token='('
             &&
             (params = params_rule(p), !p->error_indicator)  // params?
             &&
@@ -4292,7 +4292,7 @@ function_def_raw_rule(Parser *p)
             (b = block_rule(p))  // block
         )
         {
-            D(fprintf(stderr, "%*c+ function_def_raw[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "ASYNC 'def' NAME '(' params? ')' ['->' expression] &&':' func_type_comment? block"));
+            D(fprintf(stderr, "%*c+ function_def_raw[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "ASYNC 'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block"));
             Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
             if (_token == NULL) {
                 D(p->level--);
@@ -4312,7 +4312,7 @@ function_def_raw_rule(Parser *p)
         }
         p->mark = _mark;
         D(fprintf(stderr, "%*c%s function_def_raw[%d-%d]: %s failed!\n", p->level, ' ',
-                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "ASYNC 'def' NAME '(' params? ')' ['->' expression] &&':' func_type_comment? block"));
+                  p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "ASYNC 'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block"));
     }
     _res = NULL;
   done: