Skip to content

Commit b2194ac

Browse files
committed
gh-145019: improve SyntaxError when match patterns bind different names
1 parent 51e8acf commit b2194ac

3 files changed

Lines changed: 53 additions & 2 deletions

File tree

Lib/test/test_syntax.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1502,6 +1502,33 @@
15021502
Traceback (most recent call last):
15031503
SyntaxError: invalid syntax
15041504
1505+
Ensure that alternative patterns bind the same names
1506+
1507+
>>> match 1:
1508+
... case x | 1: pass
1509+
Traceback (most recent call last):
1510+
SyntaxError: name capture 'x' makes remaining patterns unreachable
1511+
1512+
>>> match 1:
1513+
... case x | y: pass
1514+
Traceback (most recent call last):
1515+
SyntaxError: name capture 'x' makes remaining patterns unreachable
1516+
1517+
>>> match 1:
1518+
... case 1 | x: ...
1519+
Traceback (most recent call last):
1520+
SyntaxError: alternative patterns bind different names (first pattern binds no names, pattern 2 binds ['x'])
1521+
1522+
>>> match 1:
1523+
... case ("user", {"id": id}) | ("admin", {"name": name}): pass
1524+
Traceback (most recent call last):
1525+
SyntaxError: alternative patterns bind different names (first pattern binds ['id'], pattern 2 binds ['name'])
1526+
1527+
>>> match 1:
1528+
... case ("user", {"id": id}) | ("admin", {"id": id}) | ("other", {"ip": ip}): pass
1529+
Traceback (most recent call last):
1530+
SyntaxError: alternative patterns bind different names (first pattern binds ['id'], pattern 3 binds ['ip'])
1531+
15051532
Incomplete dictionary literals
15061533
15071534
>>> {1:2, 3:4, 5}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improve :exc:`SyntaxError` when :keyword:`match` alternative patterns bind
2+
different names. Patch by Bénédikt Tran.

Python/codegen.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6270,6 +6270,8 @@ codegen_pattern_or(compiler *c, pattern_ty p, pattern_context *pc)
62706270
NEW_JUMP_TARGET_LABEL(c, end);
62716271
Py_ssize_t size = asdl_seq_LEN(p->v.MatchOr.patterns);
62726272
assert(size > 1);
6273+
PyObject *mismatched_names = NULL;
6274+
Py_ssize_t mismatch_index = 0;
62736275
// We're going to be messing with pc. Keep the original info handy:
62746276
pattern_context old_pc = *pc;
62756277
Py_INCREF(pc->stores);
@@ -6304,6 +6306,8 @@ codegen_pattern_or(compiler *c, pattern_ty p, pattern_context *pc)
63046306
control = Py_NewRef(pc->stores);
63056307
}
63066308
else if (nstores != PyList_GET_SIZE(control)) {
6309+
mismatch_index = i;
6310+
mismatched_names = Py_NewRef(pc->stores);
63076311
goto diff;
63086312
}
63096313
else if (nstores) {
@@ -6314,6 +6318,8 @@ codegen_pattern_or(compiler *c, pattern_ty p, pattern_context *pc)
63146318
Py_ssize_t istores = PySequence_Index(pc->stores, name);
63156319
if (istores < 0) {
63166320
PyErr_Clear();
6321+
mismatch_index = i;
6322+
mismatched_names = Py_NewRef(pc->stores);
63176323
goto diff;
63186324
}
63196325
if (icontrol != istores) {
@@ -6405,10 +6411,26 @@ codegen_pattern_or(compiler *c, pattern_ty p, pattern_context *pc)
64056411
// Pop the copy of the subject:
64066412
ADDOP(c, LOC(p), POP_TOP);
64076413
return SUCCESS;
6408-
diff:
6409-
_PyCompile_Error(c, LOC(p), "alternative patterns bind different names");
6414+
diff:;
6415+
PyObject *no_names = NULL;
6416+
if (PyList_GET_SIZE(control) == 0 || !mismatched_names) {
6417+
no_names = PyUnicode_FromString("no names");
6418+
if (no_names == NULL) {
6419+
goto error;
6420+
}
6421+
}
6422+
_PyCompile_Error(
6423+
c, LOC(p),
6424+
"alternative patterns bind different names "
6425+
"(first pattern binds %S, pattern %d binds %S)",
6426+
PyList_GET_SIZE(control) == 0 ? no_names : control,
6427+
mismatch_index + 1,
6428+
mismatched_names == NULL ? no_names : mismatched_names
6429+
);
6430+
Py_XDECREF(no_names);
64106431
error:
64116432
PyMem_Free(old_pc.fail_pop);
6433+
Py_XDECREF(mismatched_names);
64126434
Py_DECREF(old_pc.stores);
64136435
Py_XDECREF(control);
64146436
return ERROR;

0 commit comments

Comments
 (0)