mirror of https://github.com/python/cpython
bpo-46838: Syntax error improvements for function definitions (GH-31590)
This commit is contained in:
parent
deeaac49e2
commit
7d810b6a4e
|
@ -306,14 +306,16 @@ slash_with_default[SlashWithDefault*]:
|
||||||
| a=param_no_default* b=param_with_default+ '/' &')' { _PyPegen_slash_with_default(p, (asdl_arg_seq *)a, b) }
|
| a=param_no_default* b=param_with_default+ '/' &')' { _PyPegen_slash_with_default(p, (asdl_arg_seq *)a, b) }
|
||||||
|
|
||||||
star_etc[StarEtc*]:
|
star_etc[StarEtc*]:
|
||||||
|
| invalid_star_etc
|
||||||
| '*' a=param_no_default b=param_maybe_default* c=[kwds] {
|
| '*' a=param_no_default b=param_maybe_default* c=[kwds] {
|
||||||
_PyPegen_star_etc(p, a, b, c) }
|
_PyPegen_star_etc(p, a, b, c) }
|
||||||
| '*' ',' b=param_maybe_default+ c=[kwds] {
|
| '*' ',' b=param_maybe_default+ c=[kwds] {
|
||||||
_PyPegen_star_etc(p, NULL, b, c) }
|
_PyPegen_star_etc(p, NULL, b, c) }
|
||||||
| a=kwds { _PyPegen_star_etc(p, NULL, NULL, a) }
|
| a=kwds { _PyPegen_star_etc(p, NULL, NULL, a) }
|
||||||
| invalid_star_etc
|
|
||||||
|
|
||||||
kwds[arg_ty]: '**' a=param_no_default { a }
|
kwds[arg_ty]:
|
||||||
|
| invalid_kwds
|
||||||
|
| '**' a=param_no_default { a }
|
||||||
|
|
||||||
# One parameter. This *includes* a following comma and type comment.
|
# One parameter. This *includes* a following comma and type comment.
|
||||||
#
|
#
|
||||||
|
@ -339,7 +341,7 @@ param_maybe_default[NameDefaultPair*]:
|
||||||
| a=param c=default? tc=TYPE_COMMENT? &')' { _PyPegen_name_default_pair(p, a, c, tc) }
|
| a=param c=default? tc=TYPE_COMMENT? &')' { _PyPegen_name_default_pair(p, a, c, tc) }
|
||||||
param[arg_ty]: a=NAME b=annotation? { _PyAST_arg(a->v.Name.id, b, NULL, EXTRA) }
|
param[arg_ty]: a=NAME b=annotation? { _PyAST_arg(a->v.Name.id, b, NULL, EXTRA) }
|
||||||
annotation[expr_ty]: ':' a=expression { a }
|
annotation[expr_ty]: ':' a=expression { a }
|
||||||
default[expr_ty]: '=' a=expression { a }
|
default[expr_ty]: '=' a=expression { a } | invalid_default
|
||||||
|
|
||||||
# If statement
|
# If statement
|
||||||
# ------------
|
# ------------
|
||||||
|
@ -836,14 +838,16 @@ lambda_slash_with_default[SlashWithDefault*]:
|
||||||
| a=lambda_param_no_default* b=lambda_param_with_default+ '/' &':' { _PyPegen_slash_with_default(p, (asdl_arg_seq *)a, b) }
|
| a=lambda_param_no_default* b=lambda_param_with_default+ '/' &':' { _PyPegen_slash_with_default(p, (asdl_arg_seq *)a, b) }
|
||||||
|
|
||||||
lambda_star_etc[StarEtc*]:
|
lambda_star_etc[StarEtc*]:
|
||||||
|
| invalid_lambda_star_etc
|
||||||
| '*' a=lambda_param_no_default b=lambda_param_maybe_default* c=[lambda_kwds] {
|
| '*' a=lambda_param_no_default b=lambda_param_maybe_default* c=[lambda_kwds] {
|
||||||
_PyPegen_star_etc(p, a, b, c) }
|
_PyPegen_star_etc(p, a, b, c) }
|
||||||
| '*' ',' b=lambda_param_maybe_default+ c=[lambda_kwds] {
|
| '*' ',' b=lambda_param_maybe_default+ c=[lambda_kwds] {
|
||||||
_PyPegen_star_etc(p, NULL, b, c) }
|
_PyPegen_star_etc(p, NULL, b, c) }
|
||||||
| a=lambda_kwds { _PyPegen_star_etc(p, NULL, NULL, a) }
|
| a=lambda_kwds { _PyPegen_star_etc(p, NULL, NULL, a) }
|
||||||
| invalid_lambda_star_etc
|
|
||||||
|
|
||||||
lambda_kwds[arg_ty]: '**' a=lambda_param_no_default { a }
|
lambda_kwds[arg_ty]:
|
||||||
|
| invalid_lambda_kwds
|
||||||
|
| '**' a=lambda_param_no_default { a }
|
||||||
|
|
||||||
lambda_param_no_default[arg_ty]:
|
lambda_param_no_default[arg_ty]:
|
||||||
| a=lambda_param ',' { a }
|
| a=lambda_param ',' { a }
|
||||||
|
@ -1151,6 +1155,26 @@ invalid_parameters:
|
||||||
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "non-default argument follows default argument") }
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "non-default argument follows default argument") }
|
||||||
| param_no_default* a='(' param_no_default+ ','? b=')' {
|
| param_no_default* a='(' param_no_default+ ','? b=')' {
|
||||||
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "Function parameters cannot be parenthesized") }
|
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "Function parameters cannot be parenthesized") }
|
||||||
|
| a="/" ',' {
|
||||||
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "at least one argument must precede /") }
|
||||||
|
| (slash_no_default | slash_with_default) param_maybe_default* a='/' {
|
||||||
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ may appear only once") }
|
||||||
|
| (slash_no_default | slash_with_default)? param_maybe_default* '*' (',' | param_no_default) param_maybe_default* a='/' {
|
||||||
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ must be ahead of *") }
|
||||||
|
| param_maybe_default+ '/' a='*' {
|
||||||
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expected comma between / and *") }
|
||||||
|
invalid_default:
|
||||||
|
| a='=' &(')'|',') { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expected default value expression") }
|
||||||
|
invalid_star_etc:
|
||||||
|
| a='*' (')' | ',' (')' | '**')) { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "named arguments must follow bare *") }
|
||||||
|
| '*' ',' TYPE_COMMENT { RAISE_SYNTAX_ERROR("bare * has associated type comment") }
|
||||||
|
| '*' param a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "var-positional argument cannot have default value") }
|
||||||
|
| '*' (param_no_default | ',') param_maybe_default* a='*' (param_no_default | ',') {
|
||||||
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "* argument may appear only once") }
|
||||||
|
invalid_kwds:
|
||||||
|
| '**' param a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "var-keyword argument cannot have default value") }
|
||||||
|
| '**' param ',' a=param { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "arguments cannot follow var-keyword argument") }
|
||||||
|
| '**' param ',' a[Token*]=('*'|'**'|'/') { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "arguments cannot follow var-keyword argument") }
|
||||||
invalid_parameters_helper: # This is only there to avoid type errors
|
invalid_parameters_helper: # This is only there to avoid type errors
|
||||||
| a=slash_with_default { _PyPegen_singleton_seq(p, a) }
|
| a=slash_with_default { _PyPegen_singleton_seq(p, a) }
|
||||||
| param_with_default+
|
| param_with_default+
|
||||||
|
@ -1159,14 +1183,26 @@ invalid_lambda_parameters:
|
||||||
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "non-default argument follows default argument") }
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "non-default argument follows default argument") }
|
||||||
| lambda_param_no_default* a='(' ','.lambda_param+ ','? b=')' {
|
| lambda_param_no_default* a='(' ','.lambda_param+ ','? b=')' {
|
||||||
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "Lambda expression parameters cannot be parenthesized") }
|
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "Lambda expression parameters cannot be parenthesized") }
|
||||||
|
| a="/" ',' {
|
||||||
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "at least one argument must precede /") }
|
||||||
|
| (lambda_slash_no_default | lambda_slash_with_default) lambda_param_maybe_default* a='/' {
|
||||||
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ may appear only once") }
|
||||||
|
| (lambda_slash_no_default | lambda_slash_with_default)? lambda_param_maybe_default* '*' (',' | lambda_param_no_default) lambda_param_maybe_default* a='/' {
|
||||||
|
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 *") }
|
||||||
invalid_lambda_parameters_helper:
|
invalid_lambda_parameters_helper:
|
||||||
| a=lambda_slash_with_default { _PyPegen_singleton_seq(p, a) }
|
| a=lambda_slash_with_default { _PyPegen_singleton_seq(p, a) }
|
||||||
| lambda_param_with_default+
|
| lambda_param_with_default+
|
||||||
invalid_star_etc:
|
|
||||||
| a='*' (')' | ',' (')' | '**')) { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "named arguments must follow bare *") }
|
|
||||||
| '*' ',' TYPE_COMMENT { RAISE_SYNTAX_ERROR("bare * has associated type comment") }
|
|
||||||
invalid_lambda_star_etc:
|
invalid_lambda_star_etc:
|
||||||
| '*' (':' | ',' (':' | '**')) { RAISE_SYNTAX_ERROR("named arguments must follow bare *") }
|
| '*' (':' | ',' (':' | '**')) { RAISE_SYNTAX_ERROR("named arguments must follow bare *") }
|
||||||
|
| '*' lambda_param a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "var-positional argument cannot have default value") }
|
||||||
|
| '*' (lambda_param_no_default | ',') lambda_param_maybe_default* a='*' (lambda_param_no_default | ',') {
|
||||||
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "* argument may appear only once") }
|
||||||
|
invalid_lambda_kwds:
|
||||||
|
| '**' lambda_param a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "var-keyword argument cannot have default value") }
|
||||||
|
| '**' lambda_param ',' a=lambda_param { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "arguments cannot follow var-keyword argument") }
|
||||||
|
| '**' lambda_param ',' a[Token*]=('*'|'**'|'/') { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "arguments cannot follow var-keyword argument") }
|
||||||
invalid_double_type_comments:
|
invalid_double_type_comments:
|
||||||
| TYPE_COMMENT NEWLINE TYPE_COMMENT NEWLINE INDENT {
|
| TYPE_COMMENT NEWLINE TYPE_COMMENT NEWLINE INDENT {
|
||||||
RAISE_SYNTAX_ERROR("Cannot have two type comments on def") }
|
RAISE_SYNTAX_ERROR("Cannot have two type comments on def") }
|
||||||
|
|
|
@ -351,6 +351,210 @@ SyntaxError: invalid syntax
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
SyntaxError: invalid syntax
|
SyntaxError: invalid syntax
|
||||||
|
|
||||||
|
>>> def foo(/,a,b=,c):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: at least one argument must precede /
|
||||||
|
|
||||||
|
>>> def foo(a,/,/,b,c):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: / may appear only once
|
||||||
|
|
||||||
|
>>> def foo(a,/,a1,/,b,c):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: / may appear only once
|
||||||
|
|
||||||
|
>>> def foo(a=1,/,/,*b,/,c):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: / may appear only once
|
||||||
|
|
||||||
|
>>> def foo(a,/,a1=1,/,b,c):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: / may appear only once
|
||||||
|
|
||||||
|
>>> def foo(a,*b,c,/,d,e):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: / must be ahead of *
|
||||||
|
|
||||||
|
>>> def foo(a=1,*b,c=3,/,d,e):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: / must be ahead of *
|
||||||
|
|
||||||
|
>>> def foo(a,*b=3,c):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: var-positional argument cannot have default value
|
||||||
|
|
||||||
|
>>> def foo(a,*b: int=,c):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: var-positional argument cannot have default value
|
||||||
|
|
||||||
|
>>> def foo(a,**b=3):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: var-keyword argument cannot have default value
|
||||||
|
|
||||||
|
>>> def foo(a,**b: int=3):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: var-keyword argument cannot have default value
|
||||||
|
|
||||||
|
>>> def foo(a,*a, b, **c, d):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: arguments cannot follow var-keyword argument
|
||||||
|
|
||||||
|
>>> def foo(a,*a, b, **c, d=4):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: arguments cannot follow var-keyword argument
|
||||||
|
|
||||||
|
>>> def foo(a,*a, b, **c, *d):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: arguments cannot follow var-keyword argument
|
||||||
|
|
||||||
|
>>> def foo(a,*a, b, **c, **d):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: arguments cannot follow var-keyword argument
|
||||||
|
|
||||||
|
>>> def foo(a=1,/,**b,/,c):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: arguments cannot follow var-keyword argument
|
||||||
|
|
||||||
|
>>> def foo(*b,*d):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: * argument may appear only once
|
||||||
|
|
||||||
|
>>> def foo(a,*b,c,*d,*e,c):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: * argument may appear only once
|
||||||
|
|
||||||
|
>>> def foo(a,b,/,c,*b,c,*d,*e,c):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: * argument may appear only once
|
||||||
|
|
||||||
|
>>> def foo(a,b,/,c,*b,c,*d,**e):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: * argument may appear only once
|
||||||
|
|
||||||
|
>>> def foo(a=1,/*,b,c):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: expected comma between / and *
|
||||||
|
|
||||||
|
>>> def foo(a=1,d=,c):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: expected default value expression
|
||||||
|
|
||||||
|
>>> def foo(a,d=,c):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: expected default value expression
|
||||||
|
|
||||||
|
>>> def foo(a,d: int=,c):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: expected default value expression
|
||||||
|
|
||||||
|
>>> lambda /,a,b,c: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: at least one argument must precede /
|
||||||
|
|
||||||
|
>>> lambda a,/,/,b,c: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: / may appear only once
|
||||||
|
|
||||||
|
>>> lambda a,/,a1,/,b,c: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: / may appear only once
|
||||||
|
|
||||||
|
>>> lambda a=1,/,/,*b,/,c: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: / may appear only once
|
||||||
|
|
||||||
|
>>> lambda a,/,a1=1,/,b,c: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: / may appear only once
|
||||||
|
|
||||||
|
>>> lambda a,*b,c,/,d,e: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: / must be ahead of *
|
||||||
|
|
||||||
|
>>> lambda a=1,*b,c=3,/,d,e: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: / must be ahead of *
|
||||||
|
|
||||||
|
>>> lambda a=1,/*,b,c: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: expected comma between / and *
|
||||||
|
|
||||||
|
>>> lambda a,*b=3,c: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: var-positional argument cannot have default value
|
||||||
|
|
||||||
|
>>> lambda a,**b=3: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: var-keyword argument cannot have default value
|
||||||
|
|
||||||
|
>>> lambda a, *a, b, **c, d: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: arguments cannot follow var-keyword argument
|
||||||
|
|
||||||
|
>>> lambda a,*a, b, **c, d=4: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: arguments cannot follow var-keyword argument
|
||||||
|
|
||||||
|
>>> lambda a,*a, b, **c, *d: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: arguments cannot follow var-keyword argument
|
||||||
|
|
||||||
|
>>> lambda a,*a, b, **c, **d: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: arguments cannot follow var-keyword argument
|
||||||
|
|
||||||
|
>>> lambda a=1,/,**b,/,c: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: arguments cannot follow var-keyword argument
|
||||||
|
|
||||||
|
>>> lambda *b,*d: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: * argument may appear only once
|
||||||
|
|
||||||
|
>>> lambda a,*b,c,*d,*e,c: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: * argument may appear only once
|
||||||
|
|
||||||
|
>>> lambda a,b,/,c,*b,c,*d,*e,c: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: * argument may appear only once
|
||||||
|
|
||||||
|
>>> lambda a,b,/,c,*b,c,*d,**e: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: * argument may appear only once
|
||||||
|
|
||||||
|
>>> lambda a=1,d=,c: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: expected default value expression
|
||||||
|
|
||||||
|
>>> lambda a,d=,c: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: expected default value expression
|
||||||
|
|
||||||
>>> import ast; ast.parse('''
|
>>> import ast; ast.parse('''
|
||||||
... def f(
|
... def f(
|
||||||
... *, # type: int
|
... *, # type: int
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Improve syntax errors for incorrect function definitions. Patch by Pablo
|
||||||
|
Galindo
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue