mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
gh-129515: Clarify syntax error messages for conditional expressions (#129880)
Co-authored-by: Pablo Galindo Salgado <Pablogsal@gmail.com> Co-authored-by: Stan Ulbrych <89152624+StanFromIreland@users.noreply.github.com> Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
This commit is contained in:
parent
51d4bf1e0e
commit
bcc9a5dddb
5 changed files with 2015 additions and 1621 deletions
|
@ -175,6 +175,31 @@ Improved error messages
|
|||
ValueError: too many values to unpack (expected 3, got 4)
|
||||
|
||||
|
||||
* If a statement (:keyword:`pass`, :keyword:`del`, :keyword:`return`,
|
||||
:keyword:`yield`, :keyword:`raise`, :keyword:`break`, :keyword:`continue`,
|
||||
:keyword:`assert`, :keyword:`import`, :keyword:`from`) is passed to the
|
||||
:ref:`if_expr` after :keyword:`else`, or one of :keyword:`pass`,
|
||||
:keyword:`break`, or :keyword:`continue` is passed before :keyword:`if`, then the
|
||||
error message highlights where the :token:`~python-grammar:expression` is
|
||||
required. (Contributed by Sergey Miryanov in :gh:`129515`.)
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> x = 1 if True else pass
|
||||
Traceback (most recent call last):
|
||||
File "<string>", line 1
|
||||
x = 1 if True else pass
|
||||
^^^^
|
||||
SyntaxError: expected expression after 'else', but statement is given
|
||||
|
||||
>>> x = continue if True else break
|
||||
Traceback (most recent call last):
|
||||
File "<string>", line 1
|
||||
x = continue if True else break
|
||||
^^^^^^^^
|
||||
SyntaxError: expected expression before 'if', but statement is given
|
||||
|
||||
|
||||
* When incorrectly closed strings are detected, the error message suggests
|
||||
that the string may be intended to be part of the string. (Contributed by
|
||||
Pablo Galindo in :gh:`88535`.)
|
||||
|
|
|
@ -117,12 +117,12 @@ simple_stmt[stmt_ty] (memo):
|
|||
| &'return' return_stmt
|
||||
| &('import' | 'from') import_stmt
|
||||
| &'raise' raise_stmt
|
||||
| 'pass' { _PyAST_Pass(EXTRA) }
|
||||
| &'pass' pass_stmt
|
||||
| &'del' del_stmt
|
||||
| &'yield' yield_stmt
|
||||
| &'assert' assert_stmt
|
||||
| 'break' { _PyAST_Break(EXTRA) }
|
||||
| 'continue' { _PyAST_Continue(EXTRA) }
|
||||
| &'break' break_stmt
|
||||
| &'continue' continue_stmt
|
||||
| &'global' global_stmt
|
||||
| &'nonlocal' nonlocal_stmt
|
||||
|
||||
|
@ -181,6 +181,15 @@ raise_stmt[stmt_ty]:
|
|||
| 'raise' a=expression b=['from' z=expression { z }] { _PyAST_Raise(a, b, EXTRA) }
|
||||
| 'raise' { _PyAST_Raise(NULL, NULL, EXTRA) }
|
||||
|
||||
pass_stmt[stmt_ty]:
|
||||
| 'pass' { _PyAST_Pass(EXTRA) }
|
||||
|
||||
break_stmt[stmt_ty]:
|
||||
| 'break' { _PyAST_Break(EXTRA) }
|
||||
|
||||
continue_stmt[stmt_ty]:
|
||||
| 'continue' { _PyAST_Continue(EXTRA) }
|
||||
|
||||
global_stmt[stmt_ty]: 'global' a[asdl_expr_seq*]=','.NAME+ {
|
||||
_PyAST_Global(CHECK(asdl_identifier_seq*, _PyPegen_map_names_to_ids(p, a)), EXTRA) }
|
||||
|
||||
|
@ -1187,6 +1196,10 @@ invalid_expression:
|
|||
_PyPegen_check_legacy_stmt(p, a) ? NULL : p->tokens[p->mark-1]->level == 0 ? NULL :
|
||||
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "invalid syntax. Perhaps you forgot a comma?") }
|
||||
| a=disjunction 'if' b=disjunction !('else'|':') { RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "expected 'else' after 'if' expression") }
|
||||
| a=disjunction 'if' b=disjunction 'else' !expression {
|
||||
RAISE_SYNTAX_ERROR_ON_NEXT_TOKEN("expected expression after 'else', but statement is given") }
|
||||
| a[stmt_ty]=(pass_stmt|break_stmt|continue_stmt) 'if' b=disjunction 'else' c=simple_stmt {
|
||||
RAISE_SYNTAX_ERROR_KNOWN_LOCATION (a, "expected expression before 'if', but statement is given") }
|
||||
| a='lambda' [lambda_params] b=':' &FSTRING_MIDDLE {
|
||||
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "f-string: lambda expressions are not allowed without parentheses") }
|
||||
|
||||
|
|
|
@ -168,6 +168,18 @@ SyntaxError: expected 'else' after 'if' expression
|
|||
Traceback (most recent call last):
|
||||
SyntaxError: expected 'else' after 'if' expression
|
||||
|
||||
>>> x = 1 if 1 else pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: expected expression after 'else', but statement is given
|
||||
|
||||
>>> x = pass if 1 else 1
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: expected expression before 'if', but statement is given
|
||||
|
||||
>>> x = pass if 1 else pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: expected expression before 'if', but statement is given
|
||||
|
||||
>>> if True:
|
||||
... print("Hello"
|
||||
...
|
||||
|
@ -2863,6 +2875,44 @@ while 1:
|
|||
end_offset=15 + len("obj.attr"),
|
||||
)
|
||||
|
||||
def test_ifexp_else_stmt(self):
|
||||
msg = "expected expression after 'else', but statement is given"
|
||||
|
||||
for stmt in [
|
||||
"pass",
|
||||
"return",
|
||||
"return 2",
|
||||
"raise Exception('a')",
|
||||
"del a",
|
||||
"yield 2",
|
||||
"assert False",
|
||||
"break",
|
||||
"continue",
|
||||
"import",
|
||||
"import ast",
|
||||
"from",
|
||||
"from ast import *"
|
||||
]:
|
||||
self._check_error(f"x = 1 if 1 else {stmt}", msg)
|
||||
|
||||
def test_ifexp_body_stmt_else_expression(self):
|
||||
msg = "expected expression before 'if', but statement is given"
|
||||
|
||||
for stmt in [
|
||||
"pass",
|
||||
"break",
|
||||
"continue"
|
||||
]:
|
||||
self._check_error(f"x = {stmt} if 1 else 1", msg)
|
||||
|
||||
def test_ifexp_body_stmt_else_stmt(self):
|
||||
msg = "expected expression before 'if', but statement is given"
|
||||
for lhs_stmt, rhs_stmt in [
|
||||
("pass", "pass"),
|
||||
("break", "pass"),
|
||||
("continue", "import ast")
|
||||
]:
|
||||
self._check_error(f"x = {lhs_stmt} if 1 else {rhs_stmt}", msg)
|
||||
|
||||
def load_tests(loader, tests, pattern):
|
||||
tests.addTest(doctest.DocTestSuite())
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Clarify syntax error messages for conditional expressions when a statement
|
||||
is specified before an :keyword:`if` or after an :keyword:`else` keyword.
|
3540
Parser/parser.c
generated
3540
Parser/parser.c
generated
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue