diff --git a/Grammar/python.gram b/Grammar/python.gram index f3ef990923eec3..aaaf92e4a4e70b 100644 --- a/Grammar/python.gram +++ b/Grammar/python.gram @@ -1316,6 +1316,8 @@ invalid_parameters: RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ must be ahead of *") } | param_maybe_default+ '/' a='*' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expected comma between / and *") } + | a=NAME b=NAME { + RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "expected comma between parameters") } invalid_default: | a='=' &(')'|',') { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expected default value expression") } invalid_star_etc: @@ -1344,6 +1346,8 @@ invalid_lambda_parameters: RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ must be ahead of *") } | lambda_param_maybe_default+ '/' a='*' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expected comma between / and *") } + | a=NAME b=NAME { + RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "expected comma between parameters") } invalid_lambda_parameters_helper: | a=lambda_slash_with_default { _PyPegen_singleton_seq(p, a) } | lambda_param_with_default+ diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py index 0ee17849e28121..1d05cef9b3a4b4 100644 --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -521,6 +521,16 @@ Traceback (most recent call last): SyntaxError: expected comma between / and * +>>> def foo(a b): +... pass +Traceback (most recent call last): +SyntaxError: expected comma between parameters + +>>> def foo(a d=1): +... pass +Traceback (most recent call last): +SyntaxError: expected comma between parameters + >>> def foo(a=1,d=,c): ... pass Traceback (most recent call last): @@ -568,6 +578,10 @@ Traceback (most recent call last): SyntaxError: expected comma between / and * +>>> lambda a b: None +Traceback (most recent call last): +SyntaxError: expected comma between parameters + >>> lambda a,*b=3,c: None Traceback (most recent call last): SyntaxError: var-positional argument cannot have default value diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-10-20-34-00.gh-issue-133850.asdsd.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-10-20-34-00.gh-issue-133850.asdsd.rst new file mode 100644 index 00000000000000..046aada27038a7 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-10-20-34-00.gh-issue-133850.asdsd.rst @@ -0,0 +1 @@ +Improved error message for missing comma between parameters in function/lambda definition. diff --git a/Parser/parser.c b/Parser/parser.c index 509fac7df6e371..a7f1463a686993 100644 --- a/Parser/parser.c +++ b/Parser/parser.c @@ -22163,6 +22163,7 @@ invalid_dict_comprehension_rule(Parser *p) // | param_no_default* '(' param_no_default+ ','? ')' // | [(slash_no_default | slash_with_default)] param_maybe_default* '*' (',' | param_no_default) param_maybe_default* '/' // | param_maybe_default+ '/' '*' +// | NAME NAME static void * invalid_parameters_rule(Parser *p) { @@ -22373,6 +22374,33 @@ invalid_parameters_rule(Parser *p) D(fprintf(stderr, "%*c%s invalid_parameters[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "param_maybe_default+ '/' '*'")); } + { // NAME NAME + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME NAME")); + expr_ty a; + expr_ty b; + if ( + (a = _PyPegen_name_token(p)) // NAME + && + (b = _PyPegen_name_token(p)) // NAME + ) + { + D(fprintf(stderr, "%*c+ invalid_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME NAME")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "expected comma between parameters" ); + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_parameters[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NAME NAME")); + } _res = NULL; done: p->level--; @@ -22754,6 +22782,7 @@ invalid_parameters_helper_rule(Parser *p) // | lambda_param_no_default* '(' ','.lambda_param+ ','? ')' // | [(lambda_slash_no_default | lambda_slash_with_default)] lambda_param_maybe_default* '*' (',' | lambda_param_no_default) lambda_param_maybe_default* '/' // | lambda_param_maybe_default+ '/' '*' +// | NAME NAME static void * invalid_lambda_parameters_rule(Parser *p) { @@ -22964,6 +22993,33 @@ invalid_lambda_parameters_rule(Parser *p) D(fprintf(stderr, "%*c%s invalid_lambda_parameters[%d-%d]: %s failed!\n", p->level, ' ', p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "lambda_param_maybe_default+ '/' '*'")); } + { // NAME NAME + if (p->error_indicator) { + p->level--; + return NULL; + } + D(fprintf(stderr, "%*c> invalid_lambda_parameters[%d-%d]: %s\n", p->level, ' ', _mark, p->mark, "NAME NAME")); + expr_ty a; + expr_ty b; + if ( + (a = _PyPegen_name_token(p)) // NAME + && + (b = _PyPegen_name_token(p)) // NAME + ) + { + D(fprintf(stderr, "%*c+ invalid_lambda_parameters[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "NAME NAME")); + _res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , b , "expected comma between parameters" ); + if (_res == NULL && PyErr_Occurred()) { + p->error_indicator = 1; + p->level--; + return NULL; + } + goto done; + } + p->mark = _mark; + D(fprintf(stderr, "%*c%s invalid_lambda_parameters[%d-%d]: %s failed!\n", p->level, ' ', + p->error_indicator ? "ERROR!" : "-", _mark, p->mark, "NAME NAME")); + } _res = NULL; done: p->level--;