summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_dictcomps.py4
-rw-r--r--Lib/test/test_generators.py4
-rw-r--r--Lib/test/test_genexps.py4
-rw-r--r--Lib/test/test_syntax.py117
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2018-11-05-21-19-05.bpo-35169._FyPI2.rst1
-rw-r--r--Python/ast.c106
6 files changed, 140 insertions, 96 deletions
diff --git a/Lib/test/test_dictcomps.py b/Lib/test/test_dictcomps.py
index 0873071..afe68a8 100644
--- a/Lib/test/test_dictcomps.py
+++ b/Lib/test/test_dictcomps.py
@@ -73,11 +73,11 @@ class DictComprehensionTest(unittest.TestCase):
self.assertEqual(v, "Local variable")
def test_illegal_assignment(self):
- with self.assertRaisesRegex(SyntaxError, "can't assign"):
+ with self.assertRaisesRegex(SyntaxError, "cannot assign"):
compile("{x: y for y, x in ((1, 2), (3, 4))} = 5", "<test>",
"exec")
- with self.assertRaisesRegex(SyntaxError, "can't assign"):
+ with self.assertRaisesRegex(SyntaxError, "cannot assign"):
compile("{x: y for y, x in ((1, 2), (3, 4))} += 5", "<test>",
"exec")
diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py
index 7a21cb7..320793c 100644
--- a/Lib/test/test_generators.py
+++ b/Lib/test/test_generators.py
@@ -1865,12 +1865,12 @@ SyntaxError: assignment to yield expression not possible
>>> def f(): (yield bar) = y
Traceback (most recent call last):
...
-SyntaxError: can't assign to yield expression
+SyntaxError: cannot assign to yield expression
>>> def f(): (yield bar) += y
Traceback (most recent call last):
...
-SyntaxError: can't assign to yield expression
+SyntaxError: cannot assign to yield expression
Now check some throw() conditions:
diff --git a/Lib/test/test_genexps.py b/Lib/test/test_genexps.py
index fb531d6..fd712bb 100644
--- a/Lib/test/test_genexps.py
+++ b/Lib/test/test_genexps.py
@@ -137,12 +137,12 @@ Verify that syntax error's are raised for genexps used as lvalues
>>> (y for y in (1,2)) = 10
Traceback (most recent call last):
...
- SyntaxError: can't assign to generator expression
+ SyntaxError: cannot assign to generator expression
>>> (y for y in (1,2)) += 10
Traceback (most recent call last):
...
- SyntaxError: can't assign to generator expression
+ SyntaxError: cannot assign to generator expression
########### Tests borrowed from or inspired by test_generators.py ############
diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py
index c5b2496..ce1de4b 100644
--- a/Lib/test/test_syntax.py
+++ b/Lib/test/test_syntax.py
@@ -33,35 +33,55 @@ SyntaxError: invalid syntax
>>> None = 1
Traceback (most recent call last):
-SyntaxError: can't assign to keyword
+SyntaxError: cannot assign to None
+
+>>> obj.True = 1
+Traceback (most recent call last):
+SyntaxError: invalid syntax
+
+>>> True = 1
+Traceback (most recent call last):
+SyntaxError: cannot assign to True
+
+>>> obj.__debug__ = 1
+Traceback (most recent call last):
+SyntaxError: cannot assign to __debug__
+
+>>> __debug__ = 1
+Traceback (most recent call last):
+SyntaxError: cannot assign to __debug__
>>> f() = 1
Traceback (most recent call last):
-SyntaxError: can't assign to function call
+SyntaxError: cannot assign to function call
>>> del f()
Traceback (most recent call last):
-SyntaxError: can't delete function call
+SyntaxError: cannot delete function call
>>> a + 1 = 2
Traceback (most recent call last):
-SyntaxError: can't assign to operator
+SyntaxError: cannot assign to operator
>>> (x for x in x) = 1
Traceback (most recent call last):
-SyntaxError: can't assign to generator expression
+SyntaxError: cannot assign to generator expression
>>> 1 = 1
Traceback (most recent call last):
-SyntaxError: can't assign to literal
+SyntaxError: cannot assign to literal
>>> "abc" = 1
Traceback (most recent call last):
-SyntaxError: can't assign to literal
+SyntaxError: cannot assign to literal
>>> b"" = 1
Traceback (most recent call last):
-SyntaxError: can't assign to literal
+SyntaxError: cannot assign to literal
+
+>>> ... = 1
+Traceback (most recent call last):
+SyntaxError: cannot assign to Ellipsis
>>> `1` = 1
Traceback (most recent call last):
@@ -74,15 +94,31 @@ them.
>>> (a, "b", c) = (1, 2, 3)
Traceback (most recent call last):
-SyntaxError: can't assign to literal
+SyntaxError: cannot assign to literal
+
+>>> (a, True, c) = (1, 2, 3)
+Traceback (most recent call last):
+SyntaxError: cannot assign to True
+
+>>> (a, __debug__, c) = (1, 2, 3)
+Traceback (most recent call last):
+SyntaxError: cannot assign to __debug__
+
+>>> (a, *True, c) = (1, 2, 3)
+Traceback (most recent call last):
+SyntaxError: cannot assign to True
+
+>>> (a, *__debug__, c) = (1, 2, 3)
+Traceback (most recent call last):
+SyntaxError: cannot assign to __debug__
>>> [a, b, c + 1] = [1, 2, 3]
Traceback (most recent call last):
-SyntaxError: can't assign to operator
+SyntaxError: cannot assign to operator
>>> a if 1 else b = 1
Traceback (most recent call last):
-SyntaxError: can't assign to conditional expression
+SyntaxError: cannot assign to conditional expression
From compiler_complex_args():
@@ -255,36 +291,45 @@ SyntaxError: invalid syntax
>>> f(lambda x: x[0] = 3)
Traceback (most recent call last):
-SyntaxError: lambda cannot contain assignment
+SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
The grammar accepts any test (basically, any expression) in the
keyword slot of a call site. Test a few different options.
>>> f(x()=2)
Traceback (most recent call last):
-SyntaxError: keyword can't be an expression
+SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
>>> f(a or b=1)
Traceback (most recent call last):
-SyntaxError: keyword can't be an expression
+SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
>>> f(x.y=1)
Traceback (most recent call last):
-SyntaxError: keyword can't be an expression
+SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
>>> f((x)=2)
Traceback (most recent call last):
-SyntaxError: keyword can't be an expression
+SyntaxError: expression cannot contain assignment, perhaps you meant "=="?
+>>> f(True=2)
+Traceback (most recent call last):
+SyntaxError: cannot assign to True
+>>> f(__debug__=1)
+Traceback (most recent call last):
+SyntaxError: cannot assign to __debug__
More set_context():
>>> (x for x in x) += 1
Traceback (most recent call last):
-SyntaxError: can't assign to generator expression
+SyntaxError: cannot assign to generator expression
>>> None += 1
Traceback (most recent call last):
-SyntaxError: can't assign to keyword
+SyntaxError: cannot assign to None
+>>> __debug__ += 1
+Traceback (most recent call last):
+SyntaxError: cannot assign to __debug__
>>> f() += 1
Traceback (most recent call last):
-SyntaxError: can't assign to function call
+SyntaxError: cannot assign to function call
Test continue in finally in weird combinations.
@@ -481,7 +526,7 @@ leading to spurious errors.
... pass
Traceback (most recent call last):
...
- SyntaxError: can't assign to function call
+ SyntaxError: cannot assign to function call
>>> if 1:
... pass
@@ -489,7 +534,7 @@ leading to spurious errors.
... x() = 1
Traceback (most recent call last):
...
- SyntaxError: can't assign to function call
+ SyntaxError: cannot assign to function call
>>> if 1:
... x() = 1
@@ -499,7 +544,7 @@ leading to spurious errors.
... pass
Traceback (most recent call last):
...
- SyntaxError: can't assign to function call
+ SyntaxError: cannot assign to function call
>>> if 1:
... pass
@@ -509,7 +554,7 @@ leading to spurious errors.
... pass
Traceback (most recent call last):
...
- SyntaxError: can't assign to function call
+ SyntaxError: cannot assign to function call
>>> if 1:
... pass
@@ -519,7 +564,7 @@ leading to spurious errors.
... x() = 1
Traceback (most recent call last):
...
- SyntaxError: can't assign to function call
+ SyntaxError: cannot assign to function call
Make sure that the old "raise X, Y[, Z]" form is gone:
>>> raise X, Y
@@ -539,21 +584,33 @@ SyntaxError: keyword argument repeated
>>> {1, 2, 3} = 42
Traceback (most recent call last):
-SyntaxError: can't assign to literal
+SyntaxError: cannot assign to set display
+
+>>> {1: 2, 3: 4} = 42
+Traceback (most recent call last):
+SyntaxError: cannot assign to dict display
+
+>>> f'{x}' = 42
+Traceback (most recent call last):
+SyntaxError: cannot assign to f-string expression
+
+>>> f'{x}-{y}' = 42
+Traceback (most recent call last):
+SyntaxError: cannot assign to f-string expression
Corner-cases that used to fail to raise the correct error:
>>> def f(*, x=lambda __debug__:0): pass
Traceback (most recent call last):
- SyntaxError: assignment to keyword
+ SyntaxError: cannot assign to __debug__
>>> def f(*args:(lambda __debug__:0)): pass
Traceback (most recent call last):
- SyntaxError: assignment to keyword
+ SyntaxError: cannot assign to __debug__
>>> def f(**kwargs:(lambda __debug__:0)): pass
Traceback (most recent call last):
- SyntaxError: assignment to keyword
+ SyntaxError: cannot assign to __debug__
>>> with (lambda *:0): pass
Traceback (most recent call last):
@@ -563,11 +620,11 @@ Corner-cases that used to crash:
>>> def f(**__debug__): pass
Traceback (most recent call last):
- SyntaxError: assignment to keyword
+ SyntaxError: cannot assign to __debug__
>>> def f(*xx, __debug__): pass
Traceback (most recent call last):
- SyntaxError: assignment to keyword
+ SyntaxError: cannot assign to __debug__
"""
diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-11-05-21-19-05.bpo-35169._FyPI2.rst b/Misc/NEWS.d/next/Core and Builtins/2018-11-05-21-19-05.bpo-35169._FyPI2.rst
new file mode 100644
index 0000000..63c28f4
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2018-11-05-21-19-05.bpo-35169._FyPI2.rst
@@ -0,0 +1 @@
+Improved error messages for forbidden assignments.
diff --git a/Python/ast.c b/Python/ast.c
index d5c7ce6..7f72aa2 100644
--- a/Python/ast.c
+++ b/Python/ast.c
@@ -647,21 +647,25 @@ new_identifier(const char *n, struct compiling *c)
#define NEW_IDENTIFIER(n) new_identifier(STR(n), c)
static int
-ast_error(struct compiling *c, const node *n, const char *errmsg)
+ast_error(struct compiling *c, const node *n, const char *errmsg, ...)
{
PyObject *value, *errstr, *loc, *tmp;
+ va_list va;
+ va_start(va, errmsg);
+ errstr = PyUnicode_FromFormatV(errmsg, va);
+ va_end(va);
+ if (!errstr) {
+ return 0;
+ }
loc = PyErr_ProgramTextObject(c->c_filename, LINENO(n));
if (!loc) {
Py_INCREF(Py_None);
loc = Py_None;
}
tmp = Py_BuildValue("(OiiN)", c->c_filename, LINENO(n), n->n_col_offset + 1, loc);
- if (!tmp)
- return 0;
- errstr = PyUnicode_FromString(errmsg);
- if (!errstr) {
- Py_DECREF(tmp);
+ if (!tmp) {
+ Py_DECREF(errstr);
return 0;
}
value = PyTuple_Pack(2, errstr, tmp);
@@ -903,6 +907,7 @@ static const char * const FORBIDDEN[] = {
"None",
"True",
"False",
+ "__debug__",
NULL,
};
@@ -911,17 +916,16 @@ forbidden_name(struct compiling *c, identifier name, const node *n,
int full_checks)
{
assert(PyUnicode_Check(name));
- if (_PyUnicode_EqualToASCIIString(name, "__debug__")) {
- ast_error(c, n, "assignment to keyword");
- return 1;
- }
- if (full_checks) {
- const char * const *p;
- for (p = FORBIDDEN; *p; p++) {
- if (_PyUnicode_EqualToASCIIString(name, *p)) {
- ast_error(c, n, "assignment to keyword");
- return 1;
- }
+ const char * const *p = FORBIDDEN;
+ if (!full_checks) {
+ /* In most cases, the parser will protect True, False, and None
+ from being assign to. */
+ p += 3;
+ }
+ for (; *p; p++) {
+ if (_PyUnicode_EqualToASCIIString(name, *p)) {
+ ast_error(c, n, "cannot assign to %U", name);
+ return 1;
}
}
return 0;
@@ -1012,22 +1016,25 @@ set_context(struct compiling *c, expr_ty e, expr_context_ty ctx, const node *n)
expr_name = "dict comprehension";
break;
case Dict_kind:
+ expr_name = "dict display";
+ break;
case Set_kind:
+ expr_name = "set display";
+ break;
case JoinedStr_kind:
case FormattedValue_kind:
- expr_name = "literal";
+ expr_name = "f-string expression";
break;
case Constant_kind: {
PyObject *value = e->v.Constant.value;
- if (value == Py_None || value == Py_False || value == Py_True) {
- expr_name = "keyword";
- }
- else if (value == Py_Ellipsis) {
- expr_name = "Ellipsis";
- }
- else {
- expr_name = "literal";
+ if (value == Py_None || value == Py_False || value == Py_True
+ || value == Py_Ellipsis)
+ {
+ return ast_error(c, n, "cannot %s %R",
+ ctx == Store ? "assign to" : "delete",
+ value);
}
+ expr_name = "literal";
break;
}
case Compare_kind:
@@ -1044,12 +1051,9 @@ set_context(struct compiling *c, expr_ty e, expr_context_ty ctx, const node *n)
}
/* Check for error string set by switch */
if (expr_name) {
- char buf[300];
- PyOS_snprintf(buf, sizeof(buf),
- "can't %s %s",
- ctx == Store ? "assign to" : "delete",
- expr_name);
- return ast_error(c, n, buf);
+ return ast_error(c, n, "cannot %s %s",
+ ctx == Store ? "assign to" : "delete",
+ expr_name);
}
/* If the LHS is a list or tuple, we need to set the assignment
@@ -2083,21 +2087,17 @@ ast_for_atom(struct compiling *c, const node *n)
else if (PyErr_ExceptionMatches(PyExc_ValueError))
errtype = "value error";
if (errtype) {
- char buf[128];
- const char *s = NULL;
PyObject *type, *value, *tback, *errstr;
PyErr_Fetch(&type, &value, &tback);
errstr = PyObject_Str(value);
- if (errstr)
- s = PyUnicode_AsUTF8(errstr);
- if (s) {
- PyOS_snprintf(buf, sizeof(buf), "(%s) %s", errtype, s);
- } else {
+ if (errstr) {
+ ast_error(c, n, "(%s) %U", errtype, errstr);
+ Py_DECREF(errstr);
+ }
+ else {
PyErr_Clear();
- PyOS_snprintf(buf, sizeof(buf), "(%s) unknown error", errtype);
+ ast_error(c, n, "(%s) unknown error", errtype);
}
- Py_XDECREF(errstr);
- ast_error(c, n, buf);
Py_DECREF(type);
Py_XDECREF(value);
Py_XDECREF(tback);
@@ -2815,18 +2815,10 @@ ast_for_call(struct compiling *c, const node *n, expr_ty func, bool allowgen)
break;
expr_node = CHILD(expr_node, 0);
}
- if (TYPE(expr_node) == lambdef) {
- // f(lambda x: x[0] = 3) ends up getting parsed with LHS
- // test = lambda x: x[0], and RHS test = 3. Issue #132313
- // points out that complaining about a keyword then is very
- // confusing.
+ if (TYPE(expr_node) != NAME) {
ast_error(c, chch,
- "lambda cannot contain assignment");
- return NULL;
- }
- else if (TYPE(expr_node) != NAME) {
- ast_error(c, chch,
- "keyword can't be an expression");
+ "expression cannot contain assignment, "
+ "perhaps you meant \"==\"?");
return NULL;
}
key = new_identifier(STR(expr_node), c);
@@ -4127,16 +4119,10 @@ warn_invalid_escape_sequence(struct compiling *c, const node *n,
NULL, NULL) < 0)
{
if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) {
- const char *s;
-
/* Replace the SyntaxWarning exception with a SyntaxError
to get a more accurate error report */
PyErr_Clear();
-
- s = PyUnicode_AsUTF8(msg);
- if (s != NULL) {
- ast_error(c, n, s);
- }
+ ast_error(c, n, "%U", msg);
}
Py_DECREF(msg);
return -1;