mirror of
https://github.com/python/cpython.git
synced 2025-10-17 12:18:23 +00:00
bpo-15248: Emit a compiler warning when missed a comma before tuple or list. (GH-11757)
This commit is contained in:
parent
a16ab00c0b
commit
62e4481238
4 changed files with 239 additions and 6 deletions
|
@ -115,6 +115,16 @@ Other Language Changes
|
||||||
a :exc:`SyntaxWarning` instead.
|
a :exc:`SyntaxWarning` instead.
|
||||||
(Contributed by Serhiy Storchaka in :issue:`32912`.)
|
(Contributed by Serhiy Storchaka in :issue:`32912`.)
|
||||||
|
|
||||||
|
* The compiler now produces a :exc:`SyntaxWarning` in some cases when a comma
|
||||||
|
is missed before tuple or list. For example::
|
||||||
|
|
||||||
|
data = [
|
||||||
|
(1, 2, 3) # oops, missing comma!
|
||||||
|
(4, 5, 6)
|
||||||
|
]
|
||||||
|
|
||||||
|
(Contributed by Serhiy Storchaka in :issue:`15248`.)
|
||||||
|
|
||||||
* Arithmetic operations between subclasses of :class:`datetime.date` or
|
* Arithmetic operations between subclasses of :class:`datetime.date` or
|
||||||
:class:`datetime.datetime` and :class:`datetime.timedelta` objects now return
|
:class:`datetime.datetime` and :class:`datetime.timedelta` objects now return
|
||||||
an instance of the subclass, rather than the base class. This also affects
|
an instance of the subclass, rather than the base class. This also affects
|
||||||
|
|
|
@ -1263,6 +1263,93 @@ class GrammarTests(unittest.TestCase):
|
||||||
compile('x is True', '<testcase>', 'exec')
|
compile('x is True', '<testcase>', 'exec')
|
||||||
compile('x is ...', '<testcase>', 'exec')
|
compile('x is ...', '<testcase>', 'exec')
|
||||||
|
|
||||||
|
def test_warn_missed_comma(self):
|
||||||
|
def check(test):
|
||||||
|
with self.assertWarnsRegex(SyntaxWarning, msg):
|
||||||
|
compile(test, '<testcase>', 'exec')
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.filterwarnings('error', category=SyntaxWarning)
|
||||||
|
with self.assertRaisesRegex(SyntaxError, msg):
|
||||||
|
compile(test, '<testcase>', 'exec')
|
||||||
|
|
||||||
|
msg=r'is not callable; perhaps you missed a comma\?'
|
||||||
|
check('[(1, 2) (3, 4)]')
|
||||||
|
check('[(x, y) (3, 4)]')
|
||||||
|
check('[[1, 2] (3, 4)]')
|
||||||
|
check('[{1, 2} (3, 4)]')
|
||||||
|
check('[{1: 2} (3, 4)]')
|
||||||
|
check('[[i for i in range(5)] (3, 4)]')
|
||||||
|
check('[{i for i in range(5)} (3, 4)]')
|
||||||
|
check('[(i for i in range(5)) (3, 4)]')
|
||||||
|
check('[{i: i for i in range(5)} (3, 4)]')
|
||||||
|
check('[f"{x}" (3, 4)]')
|
||||||
|
check('[f"x={x}" (3, 4)]')
|
||||||
|
check('["abc" (3, 4)]')
|
||||||
|
check('[b"abc" (3, 4)]')
|
||||||
|
check('[123 (3, 4)]')
|
||||||
|
check('[12.3 (3, 4)]')
|
||||||
|
check('[12.3j (3, 4)]')
|
||||||
|
check('[None (3, 4)]')
|
||||||
|
check('[True (3, 4)]')
|
||||||
|
check('[... (3, 4)]')
|
||||||
|
|
||||||
|
msg=r'is not subscriptable; perhaps you missed a comma\?'
|
||||||
|
check('[{1, 2} [i, j]]')
|
||||||
|
check('[{i for i in range(5)} [i, j]]')
|
||||||
|
check('[(i for i in range(5)) [i, j]]')
|
||||||
|
check('[(lambda x, y: x) [i, j]]')
|
||||||
|
check('[123 [i, j]]')
|
||||||
|
check('[12.3 [i, j]]')
|
||||||
|
check('[12.3j [i, j]]')
|
||||||
|
check('[None [i, j]]')
|
||||||
|
check('[True [i, j]]')
|
||||||
|
check('[... [i, j]]')
|
||||||
|
|
||||||
|
msg=r'indices must be integers or slices, not tuple; perhaps you missed a comma\?'
|
||||||
|
check('[(1, 2) [i, j]]')
|
||||||
|
check('[(x, y) [i, j]]')
|
||||||
|
check('[[1, 2] [i, j]]')
|
||||||
|
check('[[i for i in range(5)] [i, j]]')
|
||||||
|
check('[f"{x}" [i, j]]')
|
||||||
|
check('[f"x={x}" [i, j]]')
|
||||||
|
check('["abc" [i, j]]')
|
||||||
|
check('[b"abc" [i, j]]')
|
||||||
|
|
||||||
|
msg=r'indices must be integers or slices, not tuple;'
|
||||||
|
check('[[1, 2] [3, 4]]')
|
||||||
|
msg=r'indices must be integers or slices, not list;'
|
||||||
|
check('[[1, 2] [[3, 4]]]')
|
||||||
|
check('[[1, 2] [[i for i in range(5)]]]')
|
||||||
|
msg=r'indices must be integers or slices, not set;'
|
||||||
|
check('[[1, 2] [{3, 4}]]')
|
||||||
|
check('[[1, 2] [{i for i in range(5)}]]')
|
||||||
|
msg=r'indices must be integers or slices, not dict;'
|
||||||
|
check('[[1, 2] [{3: 4}]]')
|
||||||
|
check('[[1, 2] [{i: i for i in range(5)}]]')
|
||||||
|
msg=r'indices must be integers or slices, not generator;'
|
||||||
|
check('[[1, 2] [(i for i in range(5))]]')
|
||||||
|
msg=r'indices must be integers or slices, not function;'
|
||||||
|
check('[[1, 2] [(lambda x, y: x)]]')
|
||||||
|
msg=r'indices must be integers or slices, not str;'
|
||||||
|
check('[[1, 2] [f"{x}"]]')
|
||||||
|
check('[[1, 2] [f"x={x}"]]')
|
||||||
|
check('[[1, 2] ["abc"]]')
|
||||||
|
msg=r'indices must be integers or slices, not'
|
||||||
|
check('[[1, 2] [b"abc"]]')
|
||||||
|
check('[[1, 2] [12.3]]')
|
||||||
|
check('[[1, 2] [12.3j]]')
|
||||||
|
check('[[1, 2] [None]]')
|
||||||
|
check('[[1, 2] [...]]')
|
||||||
|
|
||||||
|
with warnings.catch_warnings():
|
||||||
|
warnings.filterwarnings('error', category=SyntaxWarning)
|
||||||
|
compile('[(lambda x, y: x) (3, 4)]', '<testcase>', 'exec')
|
||||||
|
compile('[[1, 2] [i]]', '<testcase>', 'exec')
|
||||||
|
compile('[[1, 2] [0]]', '<testcase>', 'exec')
|
||||||
|
compile('[[1, 2] [True]]', '<testcase>', 'exec')
|
||||||
|
compile('[[1, 2] [1:2]]', '<testcase>', 'exec')
|
||||||
|
compile('[{(1, 2): 3} [i, j]]', '<testcase>', 'exec')
|
||||||
|
|
||||||
def test_binary_mask_ops(self):
|
def test_binary_mask_ops(self):
|
||||||
x = 1 & 1
|
x = 1 & 1
|
||||||
x = 1 ^ 1
|
x = 1 ^ 1
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
The compiler emits now syntax warnings in the case when a comma is likely
|
||||||
|
missed before tuple or list.
|
146
Python/compile.c
146
Python/compile.c
|
@ -175,7 +175,7 @@ static int compiler_addop(struct compiler *, int);
|
||||||
static int compiler_addop_i(struct compiler *, int, Py_ssize_t);
|
static int compiler_addop_i(struct compiler *, int, Py_ssize_t);
|
||||||
static int compiler_addop_j(struct compiler *, int, basicblock *, int);
|
static int compiler_addop_j(struct compiler *, int, basicblock *, int);
|
||||||
static int compiler_error(struct compiler *, const char *);
|
static int compiler_error(struct compiler *, const char *);
|
||||||
static int compiler_warn(struct compiler *, const char *);
|
static int compiler_warn(struct compiler *, const char *, ...);
|
||||||
static int compiler_nameop(struct compiler *, identifier, expr_context_ty);
|
static int compiler_nameop(struct compiler *, identifier, expr_context_ty);
|
||||||
|
|
||||||
static PyCodeObject *compiler_mod(struct compiler *, mod_ty);
|
static PyCodeObject *compiler_mod(struct compiler *, mod_ty);
|
||||||
|
@ -3766,6 +3766,122 @@ compiler_compare(struct compiler *c, expr_ty e)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyTypeObject *
|
||||||
|
infer_type(expr_ty e)
|
||||||
|
{
|
||||||
|
switch (e->kind) {
|
||||||
|
case Tuple_kind:
|
||||||
|
return &PyTuple_Type;
|
||||||
|
case List_kind:
|
||||||
|
case ListComp_kind:
|
||||||
|
return &PyList_Type;
|
||||||
|
case Dict_kind:
|
||||||
|
case DictComp_kind:
|
||||||
|
return &PyDict_Type;
|
||||||
|
case Set_kind:
|
||||||
|
case SetComp_kind:
|
||||||
|
return &PySet_Type;
|
||||||
|
case GeneratorExp_kind:
|
||||||
|
return &PyGen_Type;
|
||||||
|
case Lambda_kind:
|
||||||
|
return &PyFunction_Type;
|
||||||
|
case JoinedStr_kind:
|
||||||
|
case FormattedValue_kind:
|
||||||
|
return &PyUnicode_Type;
|
||||||
|
case Constant_kind:
|
||||||
|
return e->v.Constant.value->ob_type;
|
||||||
|
default:
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
check_caller(struct compiler *c, expr_ty e)
|
||||||
|
{
|
||||||
|
switch (e->kind) {
|
||||||
|
case Constant_kind:
|
||||||
|
case Tuple_kind:
|
||||||
|
case List_kind:
|
||||||
|
case ListComp_kind:
|
||||||
|
case Dict_kind:
|
||||||
|
case DictComp_kind:
|
||||||
|
case Set_kind:
|
||||||
|
case SetComp_kind:
|
||||||
|
case GeneratorExp_kind:
|
||||||
|
case JoinedStr_kind:
|
||||||
|
case FormattedValue_kind:
|
||||||
|
return compiler_warn(c, "'%.200s' object is not callable; "
|
||||||
|
"perhaps you missed a comma?",
|
||||||
|
infer_type(e)->tp_name);
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
check_subscripter(struct compiler *c, expr_ty e)
|
||||||
|
{
|
||||||
|
PyObject *v;
|
||||||
|
|
||||||
|
switch (e->kind) {
|
||||||
|
case Constant_kind:
|
||||||
|
v = e->v.Constant.value;
|
||||||
|
if (!(v == Py_None || v == Py_Ellipsis ||
|
||||||
|
PyLong_Check(v) || PyFloat_Check(v) || PyComplex_Check(v) ||
|
||||||
|
PyAnySet_Check(v)))
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* fall through */
|
||||||
|
case Set_kind:
|
||||||
|
case SetComp_kind:
|
||||||
|
case GeneratorExp_kind:
|
||||||
|
case Lambda_kind:
|
||||||
|
return compiler_warn(c, "'%.200s' object is not subscriptable; "
|
||||||
|
"perhaps you missed a comma?",
|
||||||
|
infer_type(e)->tp_name);
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
check_index(struct compiler *c, expr_ty e, slice_ty s)
|
||||||
|
{
|
||||||
|
PyObject *v;
|
||||||
|
|
||||||
|
if (s->kind != Index_kind) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
PyTypeObject *index_type = infer_type(s->v.Index.value);
|
||||||
|
if (index_type == NULL
|
||||||
|
|| PyType_FastSubclass(index_type, Py_TPFLAGS_LONG_SUBCLASS)
|
||||||
|
|| index_type == &PySlice_Type) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (e->kind) {
|
||||||
|
case Constant_kind:
|
||||||
|
v = e->v.Constant.value;
|
||||||
|
if (!(PyUnicode_Check(v) || PyBytes_Check(v) || PyTuple_Check(v))) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
/* fall through */
|
||||||
|
case Tuple_kind:
|
||||||
|
case List_kind:
|
||||||
|
case ListComp_kind:
|
||||||
|
case JoinedStr_kind:
|
||||||
|
case FormattedValue_kind:
|
||||||
|
return compiler_warn(c, "%.200s indices must be integers or slices, "
|
||||||
|
"not %.200s; "
|
||||||
|
"perhaps you missed a comma?",
|
||||||
|
infer_type(e)->tp_name,
|
||||||
|
index_type->tp_name);
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
maybe_optimize_method_call(struct compiler *c, expr_ty e)
|
maybe_optimize_method_call(struct compiler *c, expr_ty e)
|
||||||
{
|
{
|
||||||
|
@ -3801,7 +3917,9 @@ compiler_call(struct compiler *c, expr_ty e)
|
||||||
{
|
{
|
||||||
if (maybe_optimize_method_call(c, e) > 0)
|
if (maybe_optimize_method_call(c, e) > 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
if (!check_caller(c, e->v.Call.func)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
VISIT(c, expr, e->v.Call.func);
|
VISIT(c, expr, e->v.Call.func);
|
||||||
return compiler_call_helper(c, 0,
|
return compiler_call_helper(c, 0,
|
||||||
e->v.Call.args,
|
e->v.Call.args,
|
||||||
|
@ -4694,6 +4812,12 @@ compiler_visit_expr1(struct compiler *c, expr_ty e)
|
||||||
VISIT_SLICE(c, e->v.Subscript.slice, AugLoad);
|
VISIT_SLICE(c, e->v.Subscript.slice, AugLoad);
|
||||||
break;
|
break;
|
||||||
case Load:
|
case Load:
|
||||||
|
if (!check_subscripter(c, e->v.Subscript.value)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!check_index(c, e->v.Subscript.value, e->v.Subscript.slice)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
VISIT(c, expr, e->v.Subscript.value);
|
VISIT(c, expr, e->v.Subscript.value);
|
||||||
VISIT_SLICE(c, e->v.Subscript.slice, Load);
|
VISIT_SLICE(c, e->v.Subscript.slice, Load);
|
||||||
break;
|
break;
|
||||||
|
@ -4987,20 +5111,30 @@ compiler_error(struct compiler *c, const char *errstr)
|
||||||
and returns 0.
|
and returns 0.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
compiler_warn(struct compiler *c, const char *errstr)
|
compiler_warn(struct compiler *c, const char *format, ...)
|
||||||
{
|
{
|
||||||
PyObject *msg = PyUnicode_FromString(errstr);
|
va_list vargs;
|
||||||
|
#ifdef HAVE_STDARG_PROTOTYPES
|
||||||
|
va_start(vargs, format);
|
||||||
|
#else
|
||||||
|
va_start(vargs);
|
||||||
|
#endif
|
||||||
|
PyObject *msg = PyUnicode_FromFormatV(format, vargs);
|
||||||
|
va_end(vargs);
|
||||||
if (msg == NULL) {
|
if (msg == NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg, c->c_filename,
|
if (PyErr_WarnExplicitObject(PyExc_SyntaxWarning, msg, c->c_filename,
|
||||||
c->u->u_lineno, NULL, NULL) < 0)
|
c->u->u_lineno, NULL, NULL) < 0)
|
||||||
{
|
{
|
||||||
Py_DECREF(msg);
|
|
||||||
if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) {
|
if (PyErr_ExceptionMatches(PyExc_SyntaxWarning)) {
|
||||||
|
/* Replace the SyntaxWarning exception with a SyntaxError
|
||||||
|
to get a more accurate error report */
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
return compiler_error(c, errstr);
|
assert(PyUnicode_AsUTF8(msg) != NULL);
|
||||||
|
compiler_error(c, PyUnicode_AsUTF8(msg));
|
||||||
}
|
}
|
||||||
|
Py_DECREF(msg);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
Py_DECREF(msg);
|
Py_DECREF(msg);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue