bpo-40334: Produce better error messages for non-parenthesized genexps (GH-20153)
The error message, generated for a non-parenthesized generator expression
in function calls, was still the generic `invalid syntax`, when the generator expression wasn't appearing as the first argument in the call. With this patch, even on input like `f(a, b, c for c in d, e)`, the correct error message gets produced.
(cherry picked from commit ae14583302
)
Co-authored-by: Lysandros Nikolaou <lisandrosnik@gmail.com>
This commit is contained in:
parent
b4d08f1eb5
commit
55c8923524
|
@ -627,6 +627,9 @@ incorrect_arguments:
|
||||||
| args ',' '*' { RAISE_SYNTAX_ERROR("iterable argument unpacking follows keyword argument unpacking") }
|
| args ',' '*' { RAISE_SYNTAX_ERROR("iterable argument unpacking follows keyword argument unpacking") }
|
||||||
| a=expression for_if_clauses ',' [args | expression for_if_clauses] {
|
| a=expression for_if_clauses ',' [args | expression for_if_clauses] {
|
||||||
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "Generator expression must be parenthesized") }
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "Generator expression must be parenthesized") }
|
||||||
|
| a=args for_if_clauses { _PyPegen_nonparen_genexp_in_call(p, a) }
|
||||||
|
| args ',' a=expression for_if_clauses {
|
||||||
|
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "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=expression '=' {
|
| a=expression '=' {
|
||||||
|
|
|
@ -216,11 +216,9 @@ SyntaxError: Generator expression must be parenthesized
|
||||||
>>> f(x for x in L, **{})
|
>>> f(x for x in L, **{})
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
SyntaxError: Generator expression must be parenthesized
|
SyntaxError: Generator expression must be parenthesized
|
||||||
|
>>> f(L, x for x in L)
|
||||||
# >>> f(L, x for x in L)
|
Traceback (most recent call last):
|
||||||
# Traceback (most recent call last):
|
SyntaxError: Generator expression must be parenthesized
|
||||||
# SyntaxError: Generator expression must be parenthesized
|
|
||||||
|
|
||||||
>>> f(x for x in L, y for y in L)
|
>>> f(x for x in L, y for y in L)
|
||||||
Traceback (most recent call last):
|
Traceback (most recent call last):
|
||||||
SyntaxError: Generator expression must be parenthesized
|
SyntaxError: Generator expression must be parenthesized
|
||||||
|
|
|
@ -11670,6 +11670,8 @@ t_atom_rule(Parser *p)
|
||||||
// incorrect_arguments:
|
// incorrect_arguments:
|
||||||
// | args ',' '*'
|
// | args ',' '*'
|
||||||
// | expression for_if_clauses ',' [args | expression for_if_clauses]
|
// | expression for_if_clauses ',' [args | expression for_if_clauses]
|
||||||
|
// | args for_if_clauses
|
||||||
|
// | args ',' expression for_if_clauses
|
||||||
// | args ',' args
|
// | args ',' args
|
||||||
static void *
|
static void *
|
||||||
incorrect_arguments_rule(Parser *p)
|
incorrect_arguments_rule(Parser *p)
|
||||||
|
@ -11731,6 +11733,54 @@ incorrect_arguments_rule(Parser *p)
|
||||||
}
|
}
|
||||||
p->mark = _mark;
|
p->mark = _mark;
|
||||||
}
|
}
|
||||||
|
{ // args for_if_clauses
|
||||||
|
if (p->error_indicator) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
expr_ty a;
|
||||||
|
asdl_seq* for_if_clauses_var;
|
||||||
|
if (
|
||||||
|
(a = args_rule(p)) // args
|
||||||
|
&&
|
||||||
|
(for_if_clauses_var = for_if_clauses_rule(p)) // for_if_clauses
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_res = _PyPegen_nonparen_genexp_in_call ( p , a );
|
||||||
|
if (_res == NULL && PyErr_Occurred()) {
|
||||||
|
p->error_indicator = 1;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
p->mark = _mark;
|
||||||
|
}
|
||||||
|
{ // args ',' expression for_if_clauses
|
||||||
|
if (p->error_indicator) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Token * _literal;
|
||||||
|
expr_ty a;
|
||||||
|
expr_ty args_var;
|
||||||
|
asdl_seq* for_if_clauses_var;
|
||||||
|
if (
|
||||||
|
(args_var = args_rule(p)) // args
|
||||||
|
&&
|
||||||
|
(_literal = _PyPegen_expect_token(p, 12)) // token=','
|
||||||
|
&&
|
||||||
|
(a = expression_rule(p)) // expression
|
||||||
|
&&
|
||||||
|
(for_if_clauses_var = for_if_clauses_rule(p)) // for_if_clauses
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_res = RAISE_SYNTAX_ERROR_KNOWN_LOCATION ( a , "Generator expression must be parenthesized" );
|
||||||
|
if (_res == NULL && PyErr_Occurred()) {
|
||||||
|
p->error_indicator = 1;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
p->mark = _mark;
|
||||||
|
}
|
||||||
{ // args ',' args
|
{ // args ',' args
|
||||||
if (p->error_indicator) {
|
if (p->error_indicator) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -2100,3 +2100,24 @@ void *_PyPegen_arguments_parsing_error(Parser *p, expr_ty e) {
|
||||||
|
|
||||||
return RAISE_SYNTAX_ERROR(msg);
|
return RAISE_SYNTAX_ERROR(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
_PyPegen_nonparen_genexp_in_call(Parser *p, expr_ty args)
|
||||||
|
{
|
||||||
|
/* The rule that calls this function is 'args for_if_clauses'.
|
||||||
|
For the input f(L, x for x in y), L and x are in args and
|
||||||
|
the for is parsed as a for_if_clause. We have to check if
|
||||||
|
len <= 1, so that input like dict((a, b) for a, b in x)
|
||||||
|
gets successfully parsed and then we pass the last
|
||||||
|
argument (x in the above example) as the location of the
|
||||||
|
error */
|
||||||
|
Py_ssize_t len = asdl_seq_LEN(args->v.Call.args);
|
||||||
|
if (len <= 1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RAISE_SYNTAX_ERROR_KNOWN_LOCATION(
|
||||||
|
(expr_ty) asdl_seq_GET(args->v.Call.args, len - 1),
|
||||||
|
"Generator expression must be parenthesized"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -152,7 +152,7 @@ RAISE_ERROR_KNOWN_LOCATION(Parser *p, PyObject *errtype, int lineno,
|
||||||
#define RAISE_SYNTAX_ERROR(msg, ...) _PyPegen_raise_error(p, PyExc_SyntaxError, msg, ##__VA_ARGS__)
|
#define RAISE_SYNTAX_ERROR(msg, ...) _PyPegen_raise_error(p, PyExc_SyntaxError, msg, ##__VA_ARGS__)
|
||||||
#define RAISE_INDENTATION_ERROR(msg, ...) _PyPegen_raise_error(p, PyExc_IndentationError, msg, ##__VA_ARGS__)
|
#define RAISE_INDENTATION_ERROR(msg, ...) _PyPegen_raise_error(p, PyExc_IndentationError, msg, ##__VA_ARGS__)
|
||||||
#define RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, msg, ...) \
|
#define RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, msg, ...) \
|
||||||
RAISE_ERROR_KNOWN_LOCATION(p, PyExc_SyntaxError, a->lineno, a->col_offset, msg, ##__VA_ARGS__)
|
RAISE_ERROR_KNOWN_LOCATION(p, PyExc_SyntaxError, (a)->lineno, (a)->col_offset, msg, ##__VA_ARGS__)
|
||||||
|
|
||||||
Py_LOCAL_INLINE(void *)
|
Py_LOCAL_INLINE(void *)
|
||||||
CHECK_CALL(Parser *p, void *result)
|
CHECK_CALL(Parser *p, void *result)
|
||||||
|
@ -262,6 +262,7 @@ mod_ty _PyPegen_make_module(Parser *, asdl_seq *);
|
||||||
// Error reporting helpers
|
// Error reporting helpers
|
||||||
expr_ty _PyPegen_get_invalid_target(expr_ty e);
|
expr_ty _PyPegen_get_invalid_target(expr_ty e);
|
||||||
void *_PyPegen_arguments_parsing_error(Parser *, expr_ty);
|
void *_PyPegen_arguments_parsing_error(Parser *, expr_ty);
|
||||||
|
void *_PyPegen_nonparen_genexp_in_call(Parser *p, expr_ty args);
|
||||||
|
|
||||||
|
|
||||||
void *_PyPegen_parse(Parser *);
|
void *_PyPegen_parse(Parser *);
|
||||||
|
|
Loading…
Reference in New Issue