mirror of https://github.com/python/cpython
gh-91210: Improve error message when non-default param follows default (GH-95933)
- Improve error message when parameter without a default follows one with a default - Show same error message when positional-only params precede the default/non-default sequence
This commit is contained in:
parent
78359b1d45
commit
7e36abbb78
|
@ -1162,14 +1162,14 @@ invalid_dict_comprehension:
|
||||||
| '{' a='**' bitwise_or for_if_clauses '}' {
|
| '{' a='**' bitwise_or for_if_clauses '}' {
|
||||||
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "dict unpacking cannot be used in dict comprehension") }
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "dict unpacking cannot be used in dict comprehension") }
|
||||||
invalid_parameters:
|
invalid_parameters:
|
||||||
| param_no_default* invalid_parameters_helper a=param_no_default {
|
|
||||||
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "non-default argument follows default argument") }
|
|
||||||
| param_no_default* a='(' param_no_default+ ','? b=')' {
|
|
||||||
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "Function parameters cannot be parenthesized") }
|
|
||||||
| a="/" ',' {
|
| a="/" ',' {
|
||||||
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "at least one argument must precede /") }
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "at least one argument must precede /") }
|
||||||
| (slash_no_default | slash_with_default) param_maybe_default* a='/' {
|
| (slash_no_default | slash_with_default) param_maybe_default* a='/' {
|
||||||
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ may appear only once") }
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ may appear only once") }
|
||||||
|
| slash_no_default? param_no_default* invalid_parameters_helper a=param_no_default {
|
||||||
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "parameter without a default follows parameter with a default") }
|
||||||
|
| param_no_default* a='(' param_no_default+ ','? b=')' {
|
||||||
|
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "Function parameters cannot be parenthesized") }
|
||||||
| (slash_no_default | slash_with_default)? param_maybe_default* '*' (',' | param_no_default) param_maybe_default* a='/' {
|
| (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 *") }
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ must be ahead of *") }
|
||||||
| param_maybe_default+ '/' a='*' {
|
| param_maybe_default+ '/' a='*' {
|
||||||
|
@ -1190,14 +1190,14 @@ 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+
|
||||||
invalid_lambda_parameters:
|
invalid_lambda_parameters:
|
||||||
| lambda_param_no_default* invalid_lambda_parameters_helper a=lambda_param_no_default {
|
|
||||||
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "non-default argument follows default argument") }
|
|
||||||
| lambda_param_no_default* a='(' ','.lambda_param+ ','? b=')' {
|
|
||||||
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "Lambda expression parameters cannot be parenthesized") }
|
|
||||||
| a="/" ',' {
|
| a="/" ',' {
|
||||||
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "at least one argument must precede /") }
|
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='/' {
|
| (lambda_slash_no_default | lambda_slash_with_default) lambda_param_maybe_default* a='/' {
|
||||||
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ may appear only once") }
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ may appear only once") }
|
||||||
|
| lambda_slash_no_default? lambda_param_no_default* invalid_lambda_parameters_helper a=lambda_param_no_default {
|
||||||
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "parameter without a default follows parameter with a default") }
|
||||||
|
| lambda_param_no_default* a='(' ','.lambda_param+ ','? b=')' {
|
||||||
|
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "Lambda expression parameters cannot be parenthesized") }
|
||||||
| (lambda_slash_no_default | lambda_slash_with_default)? lambda_param_maybe_default* '*' (',' | lambda_param_no_default) lambda_param_maybe_default* a='/' {
|
| (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 *") }
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ must be ahead of *") }
|
||||||
| lambda_param_maybe_default+ '/' a='*' {
|
| lambda_param_maybe_default+ '/' a='*' {
|
||||||
|
|
|
@ -23,10 +23,11 @@ class PositionalOnlyTestCase(unittest.TestCase):
|
||||||
compile(codestr + "\n", "<test>", "single")
|
compile(codestr + "\n", "<test>", "single")
|
||||||
|
|
||||||
def test_invalid_syntax_errors(self):
|
def test_invalid_syntax_errors(self):
|
||||||
check_syntax_error(self, "def f(a, b = 5, /, c): pass", "non-default argument follows default argument")
|
check_syntax_error(self, "def f(a, b = 5, /, c): pass", "parameter without a default follows parameter with a default")
|
||||||
check_syntax_error(self, "def f(a = 5, b, /, c): pass", "non-default argument follows default argument")
|
check_syntax_error(self, "def f(a = 5, b, /, c): pass", "parameter without a default follows parameter with a default")
|
||||||
check_syntax_error(self, "def f(a = 5, b=1, /, c, *, d=2): pass", "non-default argument follows default argument")
|
check_syntax_error(self, "def f(a = 5, b=1, /, c, *, d=2): pass", "parameter without a default follows parameter with a default")
|
||||||
check_syntax_error(self, "def f(a = 5, b, /): pass", "non-default argument follows default argument")
|
check_syntax_error(self, "def f(a = 5, b, /): pass", "parameter without a default follows parameter with a default")
|
||||||
|
check_syntax_error(self, "def f(a, /, b = 5, c): pass", "parameter without a default follows parameter with a default")
|
||||||
check_syntax_error(self, "def f(*args, /): pass")
|
check_syntax_error(self, "def f(*args, /): pass")
|
||||||
check_syntax_error(self, "def f(*args, a, /): pass")
|
check_syntax_error(self, "def f(*args, a, /): pass")
|
||||||
check_syntax_error(self, "def f(**kwargs, /): pass")
|
check_syntax_error(self, "def f(**kwargs, /): pass")
|
||||||
|
@ -44,10 +45,11 @@ class PositionalOnlyTestCase(unittest.TestCase):
|
||||||
check_syntax_error(self, "def f(a, *, c, /, d, e): pass")
|
check_syntax_error(self, "def f(a, *, c, /, d, e): pass")
|
||||||
|
|
||||||
def test_invalid_syntax_errors_async(self):
|
def test_invalid_syntax_errors_async(self):
|
||||||
check_syntax_error(self, "async def f(a, b = 5, /, c): pass", "non-default argument follows default argument")
|
check_syntax_error(self, "async def f(a, b = 5, /, c): pass", "parameter without a default follows parameter with a default")
|
||||||
check_syntax_error(self, "async def f(a = 5, b, /, c): pass", "non-default argument follows default argument")
|
check_syntax_error(self, "async def f(a = 5, b, /, c): pass", "parameter without a default follows parameter with a default")
|
||||||
check_syntax_error(self, "async def f(a = 5, b=1, /, c, d=2): pass", "non-default argument follows default argument")
|
check_syntax_error(self, "async def f(a = 5, b=1, /, c, d=2): pass", "parameter without a default follows parameter with a default")
|
||||||
check_syntax_error(self, "async def f(a = 5, b, /): pass", "non-default argument follows default argument")
|
check_syntax_error(self, "async def f(a = 5, b, /): pass", "parameter without a default follows parameter with a default")
|
||||||
|
check_syntax_error(self, "async def f(a, /, b = 5, c): pass", "parameter without a default follows parameter with a default")
|
||||||
check_syntax_error(self, "async def f(*args, /): pass")
|
check_syntax_error(self, "async def f(*args, /): pass")
|
||||||
check_syntax_error(self, "async def f(*args, a, /): pass")
|
check_syntax_error(self, "async def f(*args, a, /): pass")
|
||||||
check_syntax_error(self, "async def f(**kwargs, /): pass")
|
check_syntax_error(self, "async def f(**kwargs, /): pass")
|
||||||
|
@ -231,9 +233,11 @@ class PositionalOnlyTestCase(unittest.TestCase):
|
||||||
self.assertEqual(x(1, 2), 3)
|
self.assertEqual(x(1, 2), 3)
|
||||||
|
|
||||||
def test_invalid_syntax_lambda(self):
|
def test_invalid_syntax_lambda(self):
|
||||||
check_syntax_error(self, "lambda a, b = 5, /, c: None", "non-default argument follows default argument")
|
check_syntax_error(self, "lambda a, b = 5, /, c: None", "parameter without a default follows parameter with a default")
|
||||||
check_syntax_error(self, "lambda a = 5, b, /, c: None", "non-default argument follows default argument")
|
check_syntax_error(self, "lambda a = 5, b, /, c: None", "parameter without a default follows parameter with a default")
|
||||||
check_syntax_error(self, "lambda a = 5, b, /: None", "non-default argument follows default argument")
|
check_syntax_error(self, "lambda a = 5, b=1, /, c, *, d=2: None", "parameter without a default follows parameter with a default")
|
||||||
|
check_syntax_error(self, "lambda a = 5, b, /: None", "parameter without a default follows parameter with a default")
|
||||||
|
check_syntax_error(self, "lambda a, /, b = 5, c: None", "parameter without a default follows parameter with a default")
|
||||||
check_syntax_error(self, "lambda *args, /: None")
|
check_syntax_error(self, "lambda *args, /: None")
|
||||||
check_syntax_error(self, "lambda *args, a, /: None")
|
check_syntax_error(self, "lambda *args, a, /: None")
|
||||||
check_syntax_error(self, "lambda **kwargs, /: None")
|
check_syntax_error(self, "lambda **kwargs, /: None")
|
||||||
|
|
|
@ -334,7 +334,12 @@ From ast_for_arguments():
|
||||||
>>> def f(x, y=1, z):
|
>>> def f(x, y=1, z):
|
||||||
... pass
|
... pass
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
SyntaxError: non-default argument follows default argument
|
SyntaxError: parameter without a default follows parameter with a default
|
||||||
|
|
||||||
|
>>> def f(x, /, y=1, z):
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: parameter without a default follows parameter with a default
|
||||||
|
|
||||||
>>> def f(x, None):
|
>>> def f(x, None):
|
||||||
... pass
|
... pass
|
||||||
|
@ -555,6 +560,14 @@ SyntaxError: expected default value expression
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
SyntaxError: expected default value expression
|
SyntaxError: expected default value expression
|
||||||
|
|
||||||
|
>>> lambda a,d=3,c: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: parameter without a default follows parameter with a default
|
||||||
|
|
||||||
|
>>> lambda a,/,d=3,c: None
|
||||||
|
Traceback (most recent call last):
|
||||||
|
SyntaxError: parameter without a default follows parameter with a default
|
||||||
|
|
||||||
>>> import ast; ast.parse('''
|
>>> import ast; ast.parse('''
|
||||||
... def f(
|
... def f(
|
||||||
... *, # type: int
|
... *, # type: int
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Improve error message when a parameter without a default value follows one with a default value, and show the same message, even when the non-default/default sequence is preceded by positional-only parameters.
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue