Skip to content

Commit 2819e98

Browse files
authored
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
1 parent f4c0348 commit 2819e98

File tree

4 files changed

+34
-14
lines changed

4 files changed

+34
-14
lines changed

Grammar/python.gram

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,11 +264,11 @@ function_def[stmt_ty]:
264264

265265
function_def_raw[stmt_ty]:
266266
| invalid_def_raw
267-
| 'def' n=NAME '(' params=[params] ')' a=['->' z=expression { z }] &&':' tc=[func_type_comment] b=block {
267+
| 'def' n=NAME &&'(' params=[params] ')' a=['->' z=expression { z }] &&':' tc=[func_type_comment] b=block {
268268
_PyAST_FunctionDef(n->v.Name.id,
269269
(params) ? params : CHECK(arguments_ty, _PyPegen_empty_arguments(p)),
270270
b, NULL, a, NEW_TYPE_COMMENT(p, tc), EXTRA) }
271-
| ASYNC 'def' n=NAME '(' params=[params] ')' a=['->' z=expression { z }] &&':' tc=[func_type_comment] b=block {
271+
| ASYNC 'def' n=NAME &&'(' params=[params] ')' a=['->' z=expression { z }] &&':' tc=[func_type_comment] b=block {
272272
CHECK_VERSION(
273273
stmt_ty,
274274
5,

Lib/test/test_syntax.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,17 @@
898898
Traceback (most recent call last):
899899
SyntaxError: cannot assign to attribute here. Maybe you meant '==' instead of '='?
900900
901+
902+
Missing parens after function definition
903+
904+
>>> def f:
905+
Traceback (most recent call last):
906+
SyntaxError: expected '('
907+
908+
>>> async def f:
909+
Traceback (most recent call last):
910+
SyntaxError: expected '('
911+
901912
Custom error messages for try blocks that are not followed by except/finally
902913
903914
>>> try:
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
The parser now gives a better error message when leaving out the opening
2+
parenthesis ``(`` after a ``def``-statement::
3+
4+
>>> def f:
5+
File "<stdin>", line 1
6+
def f:
7+
^
8+
SyntaxError: expected '('
9+

Parser/parser.c

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4157,8 +4157,8 @@ function_def_rule(Parser *p)
41574157

41584158
// function_def_raw:
41594159
// | invalid_def_raw
4160-
// | 'def' NAME '(' params? ')' ['->' expression] &&':' func_type_comment? block
4161-
// | ASYNC 'def' NAME '(' params? ')' ['->' expression] &&':' func_type_comment? block
4160+
// | 'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block
4161+
// | ASYNC 'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block
41624162
static stmt_ty
41634163
function_def_raw_rule(Parser *p)
41644164
{
@@ -4197,12 +4197,12 @@ function_def_raw_rule(Parser *p)
41974197
D(fprintf(stderr, "%*c%s function_def_raw[%d-%d]: %s failed!\n", p->level, ' ',
41984198
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "invalid_def_raw"));
41994199
}
4200-
{ // 'def' NAME '(' params? ')' ['->' expression] &&':' func_type_comment? block
4200+
{ // 'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block
42014201
if (p->error_indicator) {
42024202
D(p->level--);
42034203
return NULL;
42044204
}
4205-
D(fprintf(stderr, "%*c> function_def_raw[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'def' NAME '(' params? ')' ['->' expression] &&':' func_type_comment? block"));
4205+
D(fprintf(stderr, "%*c> function_def_raw[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block"));
42064206
Token * _keyword;
42074207
Token * _literal;
42084208
Token * _literal_1;
@@ -4217,7 +4217,7 @@ function_def_raw_rule(Parser *p)
42174217
&&
42184218
(n = _PyPegen_name_token(p)) // NAME
42194219
&&
4220-
(_literal = _PyPegen_expect_token(p, 7)) // token='('
4220+
(_literal = _PyPegen_expect_forced_token(p, 7, "(")) // forced_token='('
42214221
&&
42224222
(params = params_rule(p), !p->error_indicator) // params?
42234223
&&
@@ -4232,7 +4232,7 @@ function_def_raw_rule(Parser *p)
42324232
(b = block_rule(p)) // block
42334233
)
42344234
{
4235-
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"));
4235+
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"));
42364236
Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
42374237
if (_token == NULL) {
42384238
D(p->level--);
@@ -4252,14 +4252,14 @@ function_def_raw_rule(Parser *p)
42524252
}
42534253
p->mark = _mark;
42544254
D(fprintf(stderr, "%*c%s function_def_raw[%d-%d]: %s failed!\n", p->level, ' ',
4255-
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'def' NAME '(' params? ')' ['->' expression] &&':' func_type_comment? block"));
4255+
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block"));
42564256
}
4257-
{ // ASYNC 'def' NAME '(' params? ')' ['->' expression] &&':' func_type_comment? block
4257+
{ // ASYNC 'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block
42584258
if (p->error_indicator) {
42594259
D(p->level--);
42604260
return NULL;
42614261
}
4262-
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"));
4262+
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"));
42634263
Token * _keyword;
42644264
Token * _literal;
42654265
Token * _literal_1;
@@ -4277,7 +4277,7 @@ function_def_raw_rule(Parser *p)
42774277
&&
42784278
(n = _PyPegen_name_token(p)) // NAME
42794279
&&
4280-
(_literal = _PyPegen_expect_token(p, 7)) // token='('
4280+
(_literal = _PyPegen_expect_forced_token(p, 7, "(")) // forced_token='('
42814281
&&
42824282
(params = params_rule(p), !p->error_indicator) // params?
42834283
&&
@@ -4292,7 +4292,7 @@ function_def_raw_rule(Parser *p)
42924292
(b = block_rule(p)) // block
42934293
)
42944294
{
4295-
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"));
4295+
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"));
42964296
Token *_token = _PyPegen_get_last_nonnwhitespace_token(p);
42974297
if (_token == NULL) {
42984298
D(p->level--);
@@ -4312,7 +4312,7 @@ function_def_raw_rule(Parser *p)
43124312
}
43134313
p->mark = _mark;
43144314
D(fprintf(stderr, "%*c%s function_def_raw[%d-%d]: %s failed!\n", p->level, ' ',
4315-
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "ASYNC 'def' NAME '(' params? ')' ['->' expression] &&':' func_type_comment? block"));
4315+
p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "ASYNC 'def' NAME &&'(' params? ')' ['->' expression] &&':' func_type_comment? block"));
43164316
}
43174317
_res = NULL;
43184318
done:

0 commit comments

Comments
 (0)