mirror of https://github.com/python/cpython
bpo-45759: Better error messages for non-matching 'elif'/'else' statements (#29513)
This commit is contained in:
parent
56e59a49ae
commit
1c8f912ebd
|
@ -124,6 +124,7 @@ simple_stmt[stmt_ty] (memo):
|
|||
| &'nonlocal' nonlocal_stmt
|
||||
|
||||
compound_stmt[stmt_ty]:
|
||||
| invalid_compound_stmt
|
||||
| &('def' | '@' | 'async') function_def
|
||||
| &'if' if_stmt
|
||||
| &('class' | '@') class_def
|
||||
|
@ -1298,6 +1299,10 @@ invalid_import_from_targets:
|
|||
| import_from_as_names ',' NEWLINE {
|
||||
RAISE_SYNTAX_ERROR("trailing comma not allowed without surrounding parentheses") }
|
||||
|
||||
invalid_compound_stmt:
|
||||
| a='elif' named_expression ':' { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "'elif' must match an if-statement here") }
|
||||
| a='else' ':' { RAISE_SYNTAX_ERROR_STARTING_FROM(a, "'else' must match a valid statement here") }
|
||||
|
||||
invalid_with_stmt:
|
||||
| ['async'] 'with' ','.(expression ['as' star_target])+ NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") }
|
||||
| ['async'] 'with' '(' ','.(expressions ['as' star_target])+ ','? ')' NEWLINE { RAISE_SYNTAX_ERROR("expected ':'") }
|
||||
|
|
|
@ -1752,6 +1752,28 @@ Corner-cases that used to crash:
|
|||
Traceback (most recent call last):
|
||||
SyntaxError: positional patterns follow keyword patterns
|
||||
|
||||
Non-matching 'elif'/'else' statements:
|
||||
|
||||
>>> if a == b:
|
||||
... ...
|
||||
... elif a == c:
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: 'elif' must match an if-statement here
|
||||
|
||||
>>> if x == y:
|
||||
... ...
|
||||
... else:
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: 'else' must match a valid statement here
|
||||
|
||||
>>> elif m == n:
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: 'elif' must match an if-statement here
|
||||
|
||||
>>> else:
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: 'else' must match a valid statement here
|
||||
|
||||
Uses of the star operator which should fail:
|
||||
|
||||
A[:*b]
|
||||
|
@ -2006,8 +2028,8 @@ class SyntaxTestCase(unittest.TestCase):
|
|||
lineno=None, offset=None, end_lineno=None, end_offset=None):
|
||||
"""Check that compiling code raises SyntaxError with errtext.
|
||||
|
||||
errtest is a regular expression that must be present in the
|
||||
test of the exception raised. If subclass is specified it
|
||||
errtext is a regular expression that must be present in the
|
||||
test of the exception raised. If subclass is specified, it
|
||||
is the expected subclass of SyntaxError (e.g. IndentationError).
|
||||
"""
|
||||
try:
|
||||
|
@ -2031,6 +2053,22 @@ class SyntaxTestCase(unittest.TestCase):
|
|||
else:
|
||||
self.fail("compile() did not raise SyntaxError")
|
||||
|
||||
def _check_noerror(self, code,
|
||||
errtext="compile() raised unexpected SyntaxError",
|
||||
filename="<testcase>", mode="exec", subclass=None):
|
||||
"""Check that compiling code does not raise a SyntaxError.
|
||||
|
||||
errtext is the message passed to self.fail if there is
|
||||
a SyntaxError. If the subclass parameter is specified,
|
||||
it is the subclass of SyntaxError (e.g. IndentationError)
|
||||
that the raised error is checked against.
|
||||
"""
|
||||
try:
|
||||
compile(code, filename, mode)
|
||||
except SyntaxError as err:
|
||||
if (not subclass) or isinstance(err, subclass):
|
||||
self.fail(errtext)
|
||||
|
||||
def test_expression_with_assignment(self):
|
||||
self._check_error(
|
||||
"print(end1 + end2 = ' ')",
|
||||
|
@ -2372,6 +2410,25 @@ while 1:
|
|||
"""
|
||||
self._check_error(source, "too many statically nested blocks")
|
||||
|
||||
def test_syntax_error_non_matching_elif_else_statements(self):
|
||||
# Check bpo-45759: 'elif' statements that doesn't match an
|
||||
# if-statement or 'else' statements that doesn't match any
|
||||
# valid else-able statement (e.g. 'while')
|
||||
self._check_error(
|
||||
"elif m == n:\n ...",
|
||||
"'elif' must match an if-statement here")
|
||||
self._check_error(
|
||||
"else:\n ...",
|
||||
"'else' must match a valid statement here")
|
||||
self._check_noerror("if a == b:\n ...\nelif a == c:\n ...")
|
||||
self._check_noerror("if x == y:\n ...\nelse:\n ...")
|
||||
self._check_error(
|
||||
"else = 123",
|
||||
"invalid syntax")
|
||||
self._check_error(
|
||||
"elif 55 = 123",
|
||||
"cannot assign to literal here")
|
||||
|
||||
@support.cpython_only
|
||||
def test_error_on_parser_stack_overflow(self):
|
||||
source = "-" * 100000 + "4"
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Improved error messages for ``elif``/``else`` statements not matching any valid statements. Patch by Jeremiah Vivian.
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue