GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — bpo-40334: Produce better error messages on invalid targets by lysnikolaou · Pull Request #20106 · python/cpython

bpo-40334: Produce better error messages on invalid targets #20106

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Sign up for GitHub

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
pablogsal merged 24 commits into python:master from lysnikolaou:targets-errors
Jun 18, 2020

Uh oh!

There was an error while loading. Please reload this page.

Merged

bpo-40334: Produce better error messages on invalid targets #20106

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0dfaa22
bpo-40334: Produce better error messages on invalid targets
lysnikolaou May 15, 2020
d4a9dd6
Fix test_exceptions
lysnikolaou May 15, 2020
72a1339
📜🤖 Added by blurb_it.
blurb-it[bot] May 15, 2020
1b6cf0b
Produce the same error messages with the old parser for with and for …
lysnikolaou May 15, 2020
1ad3819
Add invalid_group rule
lysnikolaou May 15, 2020
6f30315
Update Grammar/python.gram
lysnikolaou May 16, 2020
f750aba
Update Parser/pegen/pegen.c
lysnikolaou May 16, 2020
132178f
Address Guido's feedback
lysnikolaou May 16, 2020
17cc4bc
Inline _PyPegen_get_invalid_for_target
lysnikolaou May 17, 2020
def0f3d
Revert inlining
lysnikolaou May 17, 2020
ee3524b
Merge remote-tracking branch 'upstream/master' into targets-errors
pablogsal May 17, 2020
4959a12
Fix searching for invalid for targets, by visiting Compare nodes in _…
lysnikolaou May 17, 2020
dd1d8c8
Call RAISE_SYNTAX_ERROR instead of returning NULL, when no invalid fo…
lysnikolaou May 17, 2020
24d63fb
Merge branch 'master' into targets-errors
lysnikolaou May 18, 2020
c1cf8e6
Replace is_del and is_for args with an enum
lysnikolaou May 18, 2020
06798ff
Only return an invalid target, when the comparison is an In comparison
lysnikolaou May 20, 2020
ef7fe3a
Fix bug
lysnikolaou May 20, 2020
efabcc9
Merge branch 'master' into targets-errors
lysnikolaou May 23, 2020
6d41c55
Merge branch 'master' into targets-errors
lysnikolaou May 26, 2020
82486c1
Minor refactoring
lysnikolaou May 26, 2020
705d55b
Merge branch 'master' into targets-errors
lysnikolaou Jun 10, 2020
f0f934d
Merge branch 'master' into targets-errors
lysnikolaou Jun 10, 2020
ede107e
Merge branch 'master' into targets-errors
lysnikolaou Jun 18, 2020
55b5327
Add CHECK* checks to GET_INVALID_*TARGET macros
lysnikolaou Jun 18, 2020
File filter
Filter by extension

Clear filters
Conversations
Failed to load comments.
Loading

Uh oh!

There was an error while loading. Please reload this page.

Jump to
Jump to file
Failed to load files.
Loading

Uh oh!

There was an error while loading. Please reload this page.

Diff view
Diff view

There are no files selected for viewing

77 changes: 52 additions & 25 deletions 77 Grammar/python.gram
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ assignment[stmt_ty]:
CHECK_VERSION(6, "Variable annotations syntax is", _Py_AnnAssign(a, b, c, 0, EXTRA)) }
| a=(z=star_targets '=' { z })+ b=(yield_expr | star_expressions) !'=' tc=[TYPE_COMMENT] {
_Py_Assign(a, b, NEW_TYPE_COMMENT(p, tc), EXTRA) }
| a=single_target b=augassign c=(yield_expr | star_expressions) {
| a=single_target b=augassign ~ c=(yield_expr | star_expressions) {
_Py_AugAssign(a, b->kind, c, EXTRA) }
| invalid_assignment

Expand Down Expand Up @@ -121,7 +121,9 @@ yield_stmt[stmt_ty]: y=yield_expr { _Py_Expr(y, EXTRA) }

assert_stmt[stmt_ty]: 'assert' a=expression b=[',' z=expression { z }] { _Py_Assert(a, b, EXTRA) }

del_stmt[stmt_ty]: 'del' a=del_targets { _Py_Delete(a, EXTRA) }
del_stmt[stmt_ty]:
| 'del' a=del_targets &(';' | NEWLINE) { _Py_Delete(a, EXTRA) }
| invalid_del_stmt

import_stmt[stmt_ty]: import_name | import_from
import_name[stmt_ty]: 'import' a=dotted_as_names { _Py_Import(a, EXTRA) }
Expand Down Expand Up @@ -164,10 +166,11 @@ while_stmt[stmt_ty]:
| 'while' a=named_expression ':' b=block c=[else_block] { _Py_While(a, b, c, EXTRA) }

for_stmt[stmt_ty]:
| 'for' t=star_targets 'in' ex=star_expressions ':' tc=[TYPE_COMMENT] b=block el=[else_block] {
| 'for' t=star_targets 'in' ~ ex=star_expressions ':' tc=[TYPE_COMMENT] b=block el=[else_block] {
_Py_For(t, ex, b, el, NEW_TYPE_COMMENT(p, tc), EXTRA) }
| ASYNC 'for' t=star_targets 'in' ex=star_expressions ':' tc=[TYPE_COMMENT] b=block el=[else_block] {
| ASYNC 'for' t=star_targets 'in' ~ ex=star_expressions ':' tc=[TYPE_COMMENT] b=block el=[else_block] {
CHECK_VERSION(5, "Async for loops are", _Py_AsyncFor(t, ex, b, el, NEW_TYPE_COMMENT(p, tc), EXTRA)) }
| invalid_for_target

with_stmt[stmt_ty]:
| 'with' '(' a=','.with_item+ ','? ')' ':' b=block {
Expand All @@ -179,7 +182,9 @@ with_stmt[stmt_ty]:
| ASYNC 'with' a=','.with_item+ ':' tc=[TYPE_COMMENT] b=block {
CHECK_VERSION(5, "Async with statements are", _Py_AsyncWith(a, b, NEW_TYPE_COMMENT(p, tc), EXTRA)) }
with_item[withitem_ty]:
| e=expression o=['as' t=target { t }] { _Py_withitem(e, o, p->arena) }
| e=expression 'as' t=target &(',' | ')' | ':') { _Py_withitem(e, t, p->arena) }
| invalid_with_item
| e=expression { _Py_withitem(e, NULL, p->arena) }

try_stmt[stmt_ty]:
| 'try' ':' b=block f=finally_block { _Py_Try(b, NULL, NULL, f, EXTRA) }
Expand Down Expand Up @@ -311,7 +316,7 @@ star_named_expression[expr_ty]:
| '*' a=bitwise_or { _Py_Starred(a, Load, EXTRA) }
| named_expression
named_expression[expr_ty]:
| a=NAME ':=' b=expression { _Py_NamedExpr(CHECK(_PyPegen_set_expr_context(p, a, Store)), b, EXTRA) }
| a=NAME ':=' ~ b=expression { _Py_NamedExpr(CHECK(_PyPegen_set_expr_context(p, a, Store)), b, EXTRA) }
| expression !':='
| invalid_named_expression

Expand Down Expand Up @@ -487,18 +492,20 @@ strings[expr_ty] (memo): a=STRING+ { _PyPegen_concatenate_strings(p, a) }
list[expr_ty]:
| '[' a=[star_named_expressions] ']' { _Py_List(a, Load, EXTRA) }
listcomp[expr_ty]:
| '[' a=named_expression b=for_if_clauses ']' { _Py_ListComp(a, b, EXTRA) }
| '[' a=named_expression ~ b=for_if_clauses ']' { _Py_ListComp(a, b, EXTRA) }
| invalid_comprehension
tuple[expr_ty]:
| '(' a=[y=star_named_expression ',' z=[star_named_expressions] { _PyPegen_seq_insert_in_front(p, y, z) } ] ')' {
_Py_Tuple(a, Load, EXTRA) }
group[expr_ty]: '(' a=(yield_expr | named_expression) ')' { a }
group[expr_ty]:
| '(' a=(yield_expr | named_expression) ')' { a }
| invalid_group
genexp[expr_ty]:
| '(' a=expression b=for_if_clauses ')' { _Py_GeneratorExp(a, b, EXTRA) }
| '(' a=expression ~ b=for_if_clauses ')' { _Py_GeneratorExp(a, b, EXTRA) }
| invalid_comprehension
set[expr_ty]: '{' a=expressions_list '}' { _Py_Set(a, EXTRA) }
setcomp[expr_ty]:
| '{' a=expression b=for_if_clauses '}' { _Py_SetComp(a, b, EXTRA) }
| '{' a=expression ~ b=for_if_clauses '}' { _Py_SetComp(a, b, EXTRA) }
| invalid_comprehension
dict[expr_ty]:
| '{' a=[double_starred_kvpairs] '}' {
Expand All @@ -514,10 +521,11 @@ kvpair[KeyValuePair*]: a=expression ':' b=expression { _PyPegen_key_value_pair(p
for_if_clauses[asdl_seq*]:
| for_if_clause+
for_if_clause[comprehension_ty]:
| ASYNC 'for' a=star_targets 'in' b=disjunction c=('if' z=disjunction { z })* {
| ASYNC 'for' a=star_targets 'in' ~ b=disjunction c=('if' z=disjunction { z })* {
CHECK_VERSION(6, "Async comprehensions are", _Py_comprehension(a, b, c, 1, p->arena)) }
| 'for' a=star_targets 'in' b=disjunction c=('if' z=disjunction { z })* {
| 'for' a=star_targets 'in' ~ b=disjunction c=('if' z=disjunction { z })* {
_Py_comprehension(a, b, c, 0, p->arena) }
| invalid_for_target

yield_expr[expr_ty]:
| 'yield' 'from' a=expression { _Py_YieldFrom(a, EXTRA) }
Expand Down Expand Up @@ -587,19 +595,15 @@ single_subscript_attribute_target[expr_ty]:
| a=t_primary '[' b=slices ']' !t_lookahead { _Py_Subscript(a, b, Store, EXTRA) }

del_targets[asdl_seq*]: a=','.del_target+ [','] { a }
# The lookaheads to del_target_end ensure that we don't match expressions where a prefix of the
# expression matches our rule, thereby letting these cases fall through to invalid_del_target.
del_target[expr_ty] (memo):
| a=t_primary '.' b=NAME &del_target_end { _Py_Attribute(a, b->v.Name.id, Del, EXTRA) }
| a=t_primary '[' b=slices ']' &del_target_end { _Py_Subscript(a, b, Del, EXTRA) }
| a=t_primary '.' b=NAME !t_lookahead { _Py_Attribute(a, b->v.Name.id, Del, EXTRA) }
| a=t_primary '[' b=slices ']' !t_lookahead { _Py_Subscript(a, b, Del, EXTRA) }
| del_t_atom
del_t_atom[expr_ty]:
| a=NAME &del_target_end { _PyPegen_set_expr_context(p, a, Del) }
| a=NAME { _PyPegen_set_expr_context(p, a, Del) }
| '(' a=del_target ')' { _PyPegen_set_expr_context(p, a, Del) }
| '(' a=[del_targets] ')' { _Py_Tuple(a, Del, EXTRA) }
| '[' a=[del_targets] ']' { _Py_List(a, Del, EXTRA) }
Comment thread
lysnikolaou marked this conversation as resolved.
Resolved

Uh oh!

There was an error while loading. Please reload this page.

| invalid_del_target
del_target_end: ')' | ']' | ',' | ';' | NEWLINE

targets[asdl_seq*]: a=','.target+ [','] { a }
target[expr_ty] (memo):
Expand Down Expand Up @@ -650,16 +654,23 @@ invalid_assignment:
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "illegal target for annotation") }
| (star_targets '=')* a=star_expressions '=' {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
_PyPegen_get_invalid_target(a),
"cannot assign to %s", _PyPegen_get_expr_name(_PyPegen_get_invalid_target(a))) }
GET_INVALID_TARGET(a),
"cannot assign to %s", _PyPegen_get_expr_name(GET_INVALID_TARGET(a))) }
| (star_targets '=')* a=yield_expr '=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "assignment to yield expression not possible") }
| a=star_expressions augassign (yield_expr | star_expressions) {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
a,
"'%s' is an illegal expression for augmented assignment",
_PyPegen_get_expr_name(a)
)}

invalid_del_stmt:
| 'del' a=star_expressions {
GET_INVALID_DEL_TARGET(a) != NULL ?
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
GET_INVALID_DEL_TARGET(a),
"cannot delete %s", _PyPegen_get_expr_name(GET_INVALID_DEL_TARGET(a))
) :
RAISE_SYNTAX_ERROR("invalid syntax") }
invalid_block:
| NEWLINE !INDENT { RAISE_INDENTATION_ERROR("expected an indented block") }
invalid_comprehension:
Expand All @@ -682,9 +693,25 @@ invalid_lambda_star_etc:
invalid_double_type_comments:
| TYPE_COMMENT NEWLINE TYPE_COMMENT NEWLINE INDENT {
RAISE_SYNTAX_ERROR("Cannot have two type comments on def") }
invalid_del_target:
| a=star_expression &del_target_end {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "cannot delete %s", _PyPegen_get_expr_name(a)) }
invalid_with_item:
| expression 'as' a=expression {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
GET_INVALID_TARGET(a),
"cannot assign to %s", _PyPegen_get_expr_name(GET_INVALID_TARGET(a))
) }

invalid_for_target:
| ASYNC? 'for' a=star_expressions {
GET_INVALID_FOR_TARGET(a) != NULL ?
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
GET_INVALID_FOR_TARGET(a),
"cannot assign to %s", _PyPegen_get_expr_name(GET_INVALID_FOR_TARGET(a))
) :
RAISE_SYNTAX_ERROR("invalid syntax") }

invalid_group:
| '(' a=starred_expression ')' {
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "can't use starred expression here") }
invalid_import_from_targets:
| import_from_as_names ',' {
RAISE_SYNTAX_ERROR("trailing comma not allowed without surrounding parentheses") }
2 changes: 1 addition & 1 deletion 2 Lib/test/test_exceptions.py
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,9 @@ def baz():
check('def f():\n x, y: int', 2, 3)
check('[*x for x in xs]', 1, 2)
check('foo(x for x in range(10), 100)', 1, 5)
check('for 1 in []: pass', 1, 5)
check('(yield i) = 2', 1, 2)
check('def f(*):\n pass', 1, 8)
check('for 1 in []: pass', 1, 7)

@cpython_only
def testSettingException(self):
Expand Down
61 changes: 60 additions & 1 deletion 61 Lib/test/test_syntax.py
Show comments
View file
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,65 @@
Traceback (most recent call last):
SyntaxError: 'list' is an illegal expression for augmented assignment

Invalid targets in `for` loops and `with` statements should also
produce a specialized error message

>>> for a() in b: pass
Traceback (most recent call last):
SyntaxError: cannot assign to function call

>>> for (a, b()) in b: pass
Traceback (most recent call last):
SyntaxError: cannot assign to function call

>>> for [a, b()] in b: pass
Traceback (most recent call last):
SyntaxError: cannot assign to function call

>>> for (*a, b, c+1) in b: pass
Traceback (most recent call last):
SyntaxError: cannot assign to operator

>>> for (x, *(y, z.d())) in b: pass
Traceback (most recent call last):
SyntaxError: cannot assign to function call

>>> for a, b() in c: pass
Traceback (most recent call last):
SyntaxError: cannot assign to function call

>>> for a, b, (c + 1, d()): pass
Traceback (most recent call last):
SyntaxError: cannot assign to operator

>>> for i < (): pass
Traceback (most recent call last):
SyntaxError: invalid syntax

>>> with a as b(): pass
Traceback (most recent call last):
SyntaxError: cannot assign to function call

>>> with a as (b, c()): pass
Traceback (most recent call last):
SyntaxError: cannot assign to function call

>>> with a as [b, c()]: pass
Traceback (most recent call last):
SyntaxError: cannot assign to function call

>>> with a as (*b, c, d+1): pass
Traceback (most recent call last):
SyntaxError: cannot assign to operator

>>> with a as (x, *(y, z.d())): pass
Traceback (most recent call last):
SyntaxError: cannot assign to function call

>>> with a as b, c as d(): pass
Traceback (most recent call last):
SyntaxError: cannot assign to function call

>>> p = p =
Traceback (most recent call last):
SyntaxError: invalid syntax
Expand Down Expand Up @@ -739,7 +798,7 @@ def test_assign_del(self):
self._check_error("del (1, 2)", "delete literal")
self._check_error("del None", "delete None")
self._check_error("del *x", "delete starred")
self._check_error("del (*x)", "delete starred")
self._check_error("del (*x)", "use starred expression")
self._check_error("del (*x,)", "delete starred")
self._check_error("del [*x,]", "delete starred")
self._check_error("del f()", "delete function call")
Expand Down
Loading
Oops, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Add this suggestion to a batch that can be applied as a single commit. This suggestion is invalid because no changes were made to the code. Suggestions cannot be applied while the pull request is closed. Suggestions cannot be applied while viewing a subset of changes. Only one suggestion per line can be applied in a batch. Add this suggestion to a batch that can be applied as a single commit. Applying suggestions on deleted lines is not supported. You must change the existing code in this line in order to create a valid suggestion. Outdated suggestions cannot be applied. This suggestion has been applied or marked resolved. Suggestions cannot be applied from pending reviews. Suggestions cannot be applied on multi-line comments. Suggestions cannot be applied while the pull request is queued to merge. Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.