-
-
Notifications
You must be signed in to change notification settings - Fork 34.4k
gh-145019: improve SyntaxError when match patterns bind different names
#145939
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -1502,6 +1502,33 @@ | |||||||||||||||||||||||||||||||||||||||||||||
| Traceback (most recent call last): | ||||||||||||||||||||||||||||||||||||||||||||||
| SyntaxError: invalid syntax | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| Ensure that alternative patterns bind the same names | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| >>> 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: ... | ||||||||||||||||||||||||||||||||||||||||||||||
| Traceback (most recent call last): | ||||||||||||||||||||||||||||||||||||||||||||||
| SyntaxError: alternative patterns bind different names (first pattern binds no names, pattern 2 binds ['x']) | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| >>> match 1: | ||||||||||||||||||||||||||||||||||||||||||||||
| ... case ("user", {"id": id}) | ("admin", {"name": name}): pass | ||||||||||||||||||||||||||||||||||||||||||||||
| Traceback (most recent call last): | ||||||||||||||||||||||||||||||||||||||||||||||
| SyntaxError: alternative patterns bind different names (first pattern 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 (first pattern binds ['id'], pattern 3 binds ['ip']) | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+1520
to
+1530
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually wondered whether it was better to display the list as is or use some join to have a string. How do you feel about it?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you give an example?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently:l (I think)
Suggestion:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think either current or number 1. Current will be easier :)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes current is simple and easier to parse. It may however be better if we remove common entries (unless it is already done). |
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| Incomplete dictionary literals | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| >>> {1:2, 3:4, 5} | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| Improve :exc:`SyntaxError` when :keyword:`match` alternative patterns bind | ||
| different names. Patch by Bénédikt Tran. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6270,6 +6270,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); | ||
|
|
@@ -6304,6 +6306,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) { | ||
|
|
@@ -6314,6 +6318,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) { | ||
|
|
@@ -6405,10 +6411,26 @@ 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:; | ||
| PyObject *no_names = NULL; | ||
| if (PyList_GET_SIZE(control) == 0 || !mismatched_names) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unless I am missing something here the condition Similarly, the ternary at line 6428 ( |
||
| no_names = PyUnicode_FromString("no names"); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This guy is only substituted when the first pattern ( Showing
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh good catch. I should have increased test coverage. I will put more cases to exercise this path. Thanks!
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is freed later before the |
||
| if (no_names == NULL) { | ||
| goto error; | ||
| } | ||
| } | ||
| _PyCompile_Error( | ||
| c, LOC(p), | ||
| "alternative patterns bind different names " | ||
| "(first pattern binds %S, pattern %d binds %S)", | ||
picnixz marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| PyList_GET_SIZE(control) == 0 ? no_names : control, | ||
| mismatch_index + 1, | ||
| mismatched_names == NULL ? no_names : mismatched_names | ||
| ); | ||
| Py_XDECREF(no_names); | ||
| error: | ||
| PyMem_Free(old_pc.fail_pop); | ||
| Py_XDECREF(mismatched_names); | ||
| Py_DECREF(old_pc.stores); | ||
| Py_XDECREF(control); | ||
| return ERROR; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.