mirror of
https://github.com/python/cpython.git
synced 2026-10-28 10:25:05 +08:00
gh-145019: improve SyntaxError when match patterns bind different names (#145939)
Co-authored-by: AN Long <aisk@users.noreply.github.com> Co-authored-by: Petr Viktorin <encukou@gmail.com>
This commit is contained in:
7 files changed
+72
-2
No files matched your search
@@ -673,6 +673,7 @@ _PyStaticObjects_CheckAll(PyInterpreterState *interp) {
|
||||
_PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(nlocals), "nlocals", 7);
|
||||
_PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(node_depth), "node_depth", 10);
|
||||
_PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(node_offset), "node_offset", 11);
|
||||
_PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(nothing), "nothing", 7);
|
||||
_PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(ns), "ns", 2);
|
||||
_PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(nstype), "nstype", 6);
|
||||
_PyStaticObject_CheckUnicodeSingleton((PyObject *)&_Py_ID(nt), "nt", 2);
|
||||
|
||||
@@ -685,6 +685,7 @@ struct _Py_global_strings {
|
||||
STRUCT_FOR_ID(nlocals)
|
||||
STRUCT_FOR_ID(node_depth)
|
||||
STRUCT_FOR_ID(node_offset)
|
||||
STRUCT_FOR_ID(nothing)
|
||||
STRUCT_FOR_ID(ns)
|
||||
STRUCT_FOR_ID(nstype)
|
||||
STRUCT_FOR_ID(nt)
|
||||
|
||||
@@ -1960,6 +1960,7 @@ extern "C" {
|
||||
INIT_ID(nlocals), \
|
||||
INIT_ID(node_depth), \
|
||||
INIT_ID(node_offset), \
|
||||
INIT_ID(nothing), \
|
||||
INIT_ID(ns), \
|
||||
INIT_ID(nstype), \
|
||||
INIT_ID(nt), \
|
||||
|
||||
@@ -637,6 +637,7 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
|
||||
offsetof(struct _Py_global_strings, identifiers._py_nlocals._ascii.ob_base),
|
||||
offsetof(struct _Py_global_strings, identifiers._py_node_depth._ascii.ob_base),
|
||||
offsetof(struct _Py_global_strings, identifiers._py_node_offset._ascii.ob_base),
|
||||
offsetof(struct _Py_global_strings, identifiers._py_nothing._ascii.ob_base),
|
||||
offsetof(struct _Py_global_strings, identifiers._py_ns._ascii.ob_base),
|
||||
offsetof(struct _Py_global_strings, identifiers._py_nstype._ascii.ob_base),
|
||||
offsetof(struct _Py_global_strings, identifiers._py_nt._ascii.ob_base),
|
||||
|
||||
@@ -1502,6 +1502,50 @@ Ensure that early = are not matched by the parser as invalid comparisons
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: invalid syntax
|
||||
|
||||
Ensure that alternative patterns bind the same names (when there is
|
||||
a single capture in the first pattern that is not uniformly captured,
|
||||
the error message is sightly different).
|
||||
|
||||
>>> match 1:
|
||||
... case x | 1: pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: name capture 'x' makes remaining patterns unreachable
|
||||
|
||||
>>> match 1:
|
||||
... case x | y: pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: name capture 'x' makes remaining patterns unreachable
|
||||
|
||||
>>> match 1:
|
||||
... case 1 | x: pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: alternative patterns bind different names (pattern 1 binds nothing, pattern 2 binds ['x'])
|
||||
|
||||
>>> match 1:
|
||||
... case (x, y) | 1: pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: alternative patterns bind different names (pattern 1 binds ['x', 'y'], pattern 2 binds nothing)
|
||||
|
||||
>>> match 1:
|
||||
... case 1 | ("point", {"x": x, "y": y}): pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: alternative patterns bind different names (pattern 1 binds nothing, pattern 2 binds ['x', 'y'])
|
||||
|
||||
>>> match 1:
|
||||
... case ("point", {"x": x, "y": y}) | 1: pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: alternative patterns bind different names (pattern 1 binds ['x', 'y'], pattern 2 binds nothing)
|
||||
|
||||
>>> match 1:
|
||||
... case ("user", {"id": id}) | ("admin", {"name": name}): pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: alternative patterns bind different names (pattern 1 binds ['id'], pattern 2 binds ['name'])
|
||||
|
||||
>>> match 1:
|
||||
... case ("user", {"id": id}) | ("admin", {"id": id}) | ("other", {"ip": ip}): pass
|
||||
Traceback (most recent call last):
|
||||
SyntaxError: alternative patterns bind different names (pattern 1 binds ['id'], pattern 3 binds ['ip'])
|
||||
|
||||
Incomplete dictionary literals
|
||||
|
||||
>>> {1:2, 3:4, 5}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
Improve :exc:`SyntaxError` when :keyword:`match` alternative patterns bind
|
||||
different names. Patch by Bénédikt Tran.
|
||||
+22
-2
@@ -6432,6 +6432,8 @@ codegen_pattern_or(compiler *c, pattern_ty p, pattern_context *pc)
|
||||
NEW_JUMP_TARGET_LABEL(c, end);
|
||||
Py_ssize_t size = asdl_seq_LEN(p->v.MatchOr.patterns);
|
||||
assert(size > 1);
|
||||
PyObject *mismatched_names = NULL;
|
||||
Py_ssize_t mismatch_index = 0;
|
||||
// We're going to be messing with pc. Keep the original info handy:
|
||||
pattern_context old_pc = *pc;
|
||||
Py_INCREF(pc->stores);
|
||||
@@ -6466,6 +6468,8 @@ codegen_pattern_or(compiler *c, pattern_ty p, pattern_context *pc)
|
||||
control = Py_NewRef(pc->stores);
|
||||
}
|
||||
else if (nstores != PyList_GET_SIZE(control)) {
|
||||
mismatch_index = i;
|
||||
mismatched_names = Py_NewRef(pc->stores);
|
||||
goto diff;
|
||||
}
|
||||
else if (nstores) {
|
||||
@@ -6476,6 +6480,8 @@ codegen_pattern_or(compiler *c, pattern_ty p, pattern_context *pc)
|
||||
Py_ssize_t istores = PySequence_Index(pc->stores, name);
|
||||
if (istores < 0) {
|
||||
PyErr_Clear();
|
||||
mismatch_index = i;
|
||||
mismatched_names = Py_NewRef(pc->stores);
|
||||
goto diff;
|
||||
}
|
||||
if (icontrol != istores) {
|
||||
@@ -6567,10 +6573,24 @@ codegen_pattern_or(compiler *c, pattern_ty p, pattern_context *pc)
|
||||
// Pop the copy of the subject:
|
||||
ADDOP(c, LOC(p), POP_TOP);
|
||||
return SUCCESS;
|
||||
diff:
|
||||
_PyCompile_Error(c, LOC(p), "alternative patterns bind different names");
|
||||
diff:;
|
||||
int control_is_empty = PyList_GET_SIZE(control) == 0;
|
||||
int pattern_is_empty = (
|
||||
mismatched_names == NULL
|
||||
|| PyList_GET_SIZE(mismatched_names) == 0
|
||||
);
|
||||
|
||||
_PyCompile_Error(
|
||||
c, LOC(p),
|
||||
"alternative patterns bind different names "
|
||||
"(pattern 1 binds %S, pattern %zd binds %S)",
|
||||
control_is_empty ? &_Py_ID(nothing) : control,
|
||||
mismatch_index + 1,
|
||||
pattern_is_empty ? &_Py_ID(nothing) : mismatched_names
|
||||
);
|
||||
error:
|
||||
PyMem_Free(old_pc.fail_pop);
|
||||
Py_XDECREF(mismatched_names);
|
||||
Py_DECREF(old_pc.stores);
|
||||
Py_XDECREF(control);
|
||||
return ERROR;
|
||||
|
||||
Reference in new issue
Block a user