bpo-45764: improve error message when missing '(' after 'def' (GH-29484)

to achieve this, change the grammar to expect the '(' token after 'def' NAME.

Automerge-Triggered-By: GH:pablogsal
This commit is contained in:
Carl Friedrich Bolz-Tereick 2021-11-09 15:03:32 +01:00 committed by GitHub
parent f4c03484da
commit 2819e98d10
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 34 additions and 14 deletions

View file

@ -264,11 +264,11 @@ function_def[stmt_ty]:
function_def_raw[stmt_ty]: function_def_raw[stmt_ty]:
| invalid_def_raw | 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, _PyAST_FunctionDef(n->v.Name.id,
(params) ? params : CHECK(arguments_ty, _PyPegen_empty_arguments(p)), (params) ? params : CHECK(arguments_ty, _PyPegen_empty_arguments(p)),
b, NULL, a, NEW_TYPE_COMMENT(p, tc), EXTRA) } 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( CHECK_VERSION(
stmt_ty, stmt_ty,
5, 5,

View file

@ -898,6 +898,17 @@ leading to spurious errors.
Traceback (most recent call last): Traceback (most recent call last):
SyntaxError: cannot assign to attribute here. Maybe you meant '==' instead of '='? 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 Custom error messages for try blocks that are not followed by except/finally
>>> try: >>> try:

View file

@ -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 '('

View file

@ -4157,8 +4157,8 @@ function_def_rule(Parser *p)
// function_def_raw: // function_def_raw:
// | invalid_def_raw // | invalid_def_raw
// | '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 // | ASYNC 'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block
static stmt_ty static stmt_ty
function_def_raw_rule(Parser *p) 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, ' ', 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")); 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) { if (p->error_indicator) {
D(p->level--); D(p->level--);
return NULL; 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 * _keyword;
Token * _literal; Token * _literal;
Token * _literal_1; Token * _literal_1;
@ -4217,7 +4217,7 @@ function_def_raw_rule(Parser *p)
&& &&
(n = _PyPegen_name_token(p)) // NAME (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? (params = params_rule(p), !p->error_indicator) // params?
&& &&
@ -4232,7 +4232,7 @@ function_def_raw_rule(Parser *p)
(b = block_rule(p)) // block (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); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
if (_token == NULL) { if (_token == NULL) {
D(p->level--); D(p->level--);
@ -4252,14 +4252,14 @@ function_def_raw_rule(Parser *p)
} }
p->mark = _mark; p->mark = _mark;
D(fprintf(stderr, "%*c%s function_def_raw[%d-%d]: %s failed!\n", p->level, ' ', 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) { if (p->error_indicator) {
D(p->level--); D(p->level--);
return NULL; 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 * _keyword;
Token * _literal; Token * _literal;
Token * _literal_1; Token * _literal_1;
@ -4277,7 +4277,7 @@ function_def_raw_rule(Parser *p)
&& &&
(n = _PyPegen_name_token(p)) // NAME (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? (params = params_rule(p), !p->error_indicator) // params?
&& &&
@ -4292,7 +4292,7 @@ function_def_raw_rule(Parser *p)
(b = block_rule(p)) // block (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); Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
if (_token == NULL) { if (_token == NULL) {
D(p->level--); D(p->level--);
@ -4312,7 +4312,7 @@ function_def_raw_rule(Parser *p)
} }
p->mark = _mark; p->mark = _mark;
D(fprintf(stderr, "%*c%s function_def_raw[%d-%d]: %s failed!\n", p->level, ' ', 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; _res = NULL;
done: done: