mirror of https://github.com/python/cpython
bpo-47212: Improve error messages for un-parenthesized generator expressions (GH-32302)
This commit is contained in:
parent
f1606a5ba5
commit
aa0f056a00
|
@ -1073,12 +1073,12 @@ func_type_comment[Token*]:
|
||||||
invalid_arguments:
|
invalid_arguments:
|
||||||
| a=args ',' '*' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "iterable argument unpacking follows keyword argument unpacking") }
|
| a=args ',' '*' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "iterable argument unpacking follows keyword argument unpacking") }
|
||||||
| a=expression b=for_if_clauses ',' [args | expression for_if_clauses] {
|
| a=expression b=for_if_clauses ',' [args | expression for_if_clauses] {
|
||||||
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, PyPegen_last_item(b, comprehension_ty)->target, "Generator expression must be parenthesized") }
|
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, _PyPegen_get_last_comprehension_item(PyPegen_last_item(b, comprehension_ty)), "Generator expression must be parenthesized") }
|
||||||
| a=NAME b='=' expression for_if_clauses {
|
| a=NAME b='=' expression for_if_clauses {
|
||||||
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "invalid syntax. Maybe you meant '==' or ':=' instead of '='?")}
|
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, b, "invalid syntax. Maybe you meant '==' or ':=' instead of '='?")}
|
||||||
| a=args b=for_if_clauses { _PyPegen_nonparen_genexp_in_call(p, a, b) }
|
| a=args b=for_if_clauses { _PyPegen_nonparen_genexp_in_call(p, a, b) }
|
||||||
| args ',' a=expression b=for_if_clauses {
|
| args ',' a=expression b=for_if_clauses {
|
||||||
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, asdl_seq_GET(b, b->size-1)->target, "Generator expression must be parenthesized") }
|
RAISE_SYNTAX_ERROR_KNOWN_RANGE(a, _PyPegen_get_last_comprehension_item(PyPegen_last_item(b, comprehension_ty)), "Generator expression must be parenthesized") }
|
||||||
| a=args ',' args { _PyPegen_arguments_parsing_error(p, a) }
|
| a=args ',' args { _PyPegen_arguments_parsing_error(p, a) }
|
||||||
invalid_kwarg:
|
invalid_kwarg:
|
||||||
| a[Token*]=('True'|'False'|'None') b='=' {
|
| a[Token*]=('True'|'False'|'None') b='=' {
|
||||||
|
@ -1257,7 +1257,7 @@ invalid_finally_stmt:
|
||||||
invalid_except_stmt_indent:
|
invalid_except_stmt_indent:
|
||||||
| a='except' expression ['as' NAME ] ':' NEWLINE !INDENT {
|
| a='except' expression ['as' NAME ] ':' NEWLINE !INDENT {
|
||||||
RAISE_INDENTATION_ERROR("expected an indented block after 'except' statement on line %d", a->lineno) }
|
RAISE_INDENTATION_ERROR("expected an indented block after 'except' statement on line %d", a->lineno) }
|
||||||
| a='except' ':' NEWLINE !INDENT { RAISE_SYNTAX_ERROR("expected an indented block after except statement on line %d", a->lineno) }
|
| a='except' ':' NEWLINE !INDENT { RAISE_INDENTATION_ERROR("expected an indented block after 'except' statement on line %d", a->lineno) }
|
||||||
invalid_except_star_stmt_indent:
|
invalid_except_star_stmt_indent:
|
||||||
| a='except' '*' expression ['as' NAME ] ':' NEWLINE !INDENT {
|
| a='except' '*' expression ['as' NAME ] ':' NEWLINE !INDENT {
|
||||||
RAISE_INDENTATION_ERROR("expected an indented block after 'except*' statement on line %d", a->lineno) }
|
RAISE_INDENTATION_ERROR("expected an indented block after 'except*' statement on line %d", a->lineno) }
|
||||||
|
|
|
@ -198,12 +198,17 @@ class ExceptionTests(unittest.TestCase):
|
||||||
s = '''if True:\n print()\n\texec "mixed tabs and spaces"'''
|
s = '''if True:\n print()\n\texec "mixed tabs and spaces"'''
|
||||||
ckmsg(s, "inconsistent use of tabs and spaces in indentation", TabError)
|
ckmsg(s, "inconsistent use of tabs and spaces in indentation", TabError)
|
||||||
|
|
||||||
def check(self, src, lineno, offset, encoding='utf-8'):
|
def check(self, src, lineno, offset, end_lineno=None, end_offset=None, encoding='utf-8'):
|
||||||
with self.subTest(source=src, lineno=lineno, offset=offset):
|
with self.subTest(source=src, lineno=lineno, offset=offset):
|
||||||
with self.assertRaises(SyntaxError) as cm:
|
with self.assertRaises(SyntaxError) as cm:
|
||||||
compile(src, '<fragment>', 'exec')
|
compile(src, '<fragment>', 'exec')
|
||||||
self.assertEqual(cm.exception.lineno, lineno)
|
self.assertEqual(cm.exception.lineno, lineno)
|
||||||
self.assertEqual(cm.exception.offset, offset)
|
self.assertEqual(cm.exception.offset, offset)
|
||||||
|
if end_lineno is not None:
|
||||||
|
self.assertEqual(cm.exception.end_lineno, end_lineno)
|
||||||
|
if end_offset is not None:
|
||||||
|
self.assertEqual(cm.exception.end_offset, end_offset)
|
||||||
|
|
||||||
if cm.exception.text is not None:
|
if cm.exception.text is not None:
|
||||||
if not isinstance(src, str):
|
if not isinstance(src, str):
|
||||||
src = src.decode(encoding, 'replace')
|
src = src.decode(encoding, 'replace')
|
||||||
|
@ -235,6 +240,10 @@ class ExceptionTests(unittest.TestCase):
|
||||||
check('match ...:\n case {**rest, "key": value}:\n ...', 2, 19)
|
check('match ...:\n case {**rest, "key": value}:\n ...', 2, 19)
|
||||||
check("[a b c d e f]", 1, 2)
|
check("[a b c d e f]", 1, 2)
|
||||||
check("for x yfff:", 1, 7)
|
check("for x yfff:", 1, 7)
|
||||||
|
check("f(a for a in b, c)", 1, 3, 1, 15)
|
||||||
|
check("f(a for a in b if a, c)", 1, 3, 1, 20)
|
||||||
|
check("f(a, b for b in c)", 1, 6, 1, 18)
|
||||||
|
check("f(a, b for b in c, d)", 1, 6, 1, 18)
|
||||||
|
|
||||||
# Errors thrown by compile.c
|
# Errors thrown by compile.c
|
||||||
check('class foo:return 1', 1, 11)
|
check('class foo:return 1', 1, 11)
|
||||||
|
|
|
@ -1307,6 +1307,13 @@ Specialized indentation errors:
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
IndentationError: expected an indented block after 'try' statement on line 1
|
IndentationError: expected an indented block after 'try' statement on line 1
|
||||||
|
|
||||||
|
>>> try:
|
||||||
|
... something()
|
||||||
|
... except:
|
||||||
|
... pass
|
||||||
|
Traceback (most recent call last):
|
||||||
|
IndentationError: expected an indented block after 'except' statement on line 3
|
||||||
|
|
||||||
>>> try:
|
>>> try:
|
||||||
... something()
|
... something()
|
||||||
... except A:
|
... except A:
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Raise :exc:`IndentationError` instead of :exc:`SyntaxError` for a bare
|
||||||
|
``except`` with no following indent. Improve :exc:`SyntaxError` locations for
|
||||||
|
an un-parenthesized generator used as arguments. Patch by Matthieu Dartiailh.
|
|
@ -1145,7 +1145,7 @@ _PyPegen_get_expr_name(expr_ty e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline expr_ty
|
expr_ty
|
||||||
_PyPegen_get_last_comprehension_item(comprehension_ty comprehension) {
|
_PyPegen_get_last_comprehension_item(comprehension_ty comprehension) {
|
||||||
if (comprehension->ifs == NULL || asdl_seq_LEN(comprehension->ifs) == 0) {
|
if (comprehension->ifs == NULL || asdl_seq_LEN(comprehension->ifs) == 0) {
|
||||||
return comprehension->iter;
|
return comprehension->iter;
|
||||||
|
|
|
@ -18968,7 +18968,7 @@ invalid_arguments_rule(Parser *p)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]"));
|
D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "expression for_if_clauses ',' [args | expression for_if_clauses]"));
|
||||||
_res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , PyPegen_last_item ( b , comprehension_ty ) -> target , "Generator expression must be parenthesized" );
|
_res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , _PyPegen_get_last_comprehension_item ( PyPegen_last_item ( b , comprehension_ty ) ) , "Generator expression must be parenthesized" );
|
||||||
if (_res == NULL && PyErr_Occurred()) {
|
if (_res == NULL && PyErr_Occurred()) {
|
||||||
p->error_indicator = 1;
|
p->error_indicator = 1;
|
||||||
p->level--;
|
p->level--;
|
||||||
|
@ -19061,7 +19061,7 @@ invalid_arguments_rule(Parser *p)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' expression for_if_clauses"));
|
D(fprintf(stderr, "%*c+ invalid_arguments[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "args ',' expression for_if_clauses"));
|
||||||
_res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , asdl_seq_GET ( b , b -> size - 1 ) -> target , "Generator expression must be parenthesized" );
|
_res = RAISE_SYNTAX_ERROR_KNOWN_RANGE ( a , _PyPegen_get_last_comprehension_item ( PyPegen_last_item ( b , comprehension_ty ) ) , "Generator expression must be parenthesized" );
|
||||||
if (_res == NULL && PyErr_Occurred()) {
|
if (_res == NULL && PyErr_Occurred()) {
|
||||||
p->error_indicator = 1;
|
p->error_indicator = 1;
|
||||||
p->level--;
|
p->level--;
|
||||||
|
@ -22190,7 +22190,7 @@ invalid_except_stmt_indent_rule(Parser *p)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
D(fprintf(stderr, "%*c+ invalid_except_stmt_indent[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except' ':' NEWLINE !INDENT"));
|
D(fprintf(stderr, "%*c+ invalid_except_stmt_indent[%d-%d]: %s succeeded!\n", p->level, ' ', _mark, p->mark, "'except' ':' NEWLINE !INDENT"));
|
||||||
_res = RAISE_SYNTAX_ERROR ( "expected an indented block after except statement on line %d" , a -> lineno );
|
_res = RAISE_INDENTATION_ERROR ( "expected an indented block after 'except' statement on line %d" , a -> lineno );
|
||||||
if (_res == NULL && PyErr_Occurred()) {
|
if (_res == NULL && PyErr_Occurred()) {
|
||||||
p->error_indicator = 1;
|
p->error_indicator = 1;
|
||||||
p->level--;
|
p->level--;
|
||||||
|
|
|
@ -324,6 +324,7 @@ int _PyPegen_check_barry_as_flufl(Parser *, Token *);
|
||||||
int _PyPegen_check_legacy_stmt(Parser *p, expr_ty t);
|
int _PyPegen_check_legacy_stmt(Parser *p, expr_ty t);
|
||||||
mod_ty _PyPegen_make_module(Parser *, asdl_stmt_seq *);
|
mod_ty _PyPegen_make_module(Parser *, asdl_stmt_seq *);
|
||||||
void *_PyPegen_arguments_parsing_error(Parser *, expr_ty);
|
void *_PyPegen_arguments_parsing_error(Parser *, expr_ty);
|
||||||
|
expr_ty _PyPegen_get_last_comprehension_item(comprehension_ty comprehension);
|
||||||
void *_PyPegen_nonparen_genexp_in_call(Parser *p, expr_ty args, asdl_comprehension_seq *comprehensions);
|
void *_PyPegen_nonparen_genexp_in_call(Parser *p, expr_ty args, asdl_comprehension_seq *comprehensions);
|
||||||
|
|
||||||
// Parser API
|
// Parser API
|
||||||
|
|
Loading…
Reference in New Issue