fix: Narrowing with "tags" on unions (of TypedDicts or normal classes) doesn't work with the match statement - #18791
Conversation
…) doesn't work with the match statement Signed-off-by: Gene Parmesan Thomas <[email protected]>
This comment has been minimized.
This comment has been minimized.
hauntsaninja
left a comment
There was a problem hiding this comment.
This looks good, just had two minor comments
|
|
||
| Contributed by Marc Mueller (PR [18641](https://github.com/python/mypy/pull/18641)) | ||
|
|
||
| ### Other Notable Fixes and Improvements |
There was a problem hiding this comment.
we typically will do changelog entries later, can you remove this?
There was a problem hiding this comment.
Removed the changelog entry for now; will add it later alongside other notes.
| pattern_map, else_map = conditional_types_to_typemaps( | ||
| named_subject, pattern_type.type, pattern_type.rest_type | ||
| ) | ||
| # Also refine the parent expression of the subject. |
There was a problem hiding this comment.
thanks for docs, but i think this is fairly clear from the propagate_up_typemap_info docstring, so could you remove the comment?
There was a problem hiding this comment.
Dropped the extra comment here since the helper’s docstring is enough
Signed-off-by: gopoto <[email protected]>
6d03c52 to
0a6333a
Compare
|
According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅ |
Summary
Addressed issue #16286, which reported that
matchstatements failed to narrow unions when matching on discriminant values (e.g.tagfields inTypedDicts or classes). This led to mypy complaining about missing keys/attributes insidecaseblocks even though plainif ... ==branches worked.What changed?
mypy/checker.py:visit_match_stmt, after callingconditional_types_to_typemapsfor the subject, the resulting type map is passed throughpropagate_up_typemap_info(just like in theif/isinstancecode paths). This means if the subject is an attribute or index expression (d.tag/d["tag"]), we try to refine the parentdbased on the pattern.pattern_mapwhen it's defined, to avoidUnboundLocalErrorin unreachable branches.test-data/unit/check-python310.test:testMatchNarrowingUnionTypedDictViaIndexchecks thatmatch d["tag"]refines aTypedDictunion and allows access to the appropriate key in each branch.testMatchNarrowingUnionClassViaAttributechecks the same for a class union usingmatch d.tag.Why?
Previously, the binder only narrowed the type of the expression being matched (e.g.
d["tag"]), not the parent expression. Without propagating that information upwards, mypy didn't realize it could rule out unrelatedTypedDict/class variants in eachcase, leading to erroneoustypeddict-itemandunion-attrerrors. The fix mirrors the existing behaviour forifand similar checks.How was it tested?
pytest -k testMatchNarrowingUnionTypedDictViaIndexandpytest -k testMatchNarrowingUnionClassViaAttributeto confirm the new tests passed.pytest -k check-python310to ensure the entire pattern-matching suite still passes after the change.python runtests.py self).pre-commit run --all-filesto satisfy formatting and lint hooks.Checklist
CHANGELOG.md.Fixes #16286.
This PR was generated by an AI system in collaboration with maintainers: @hauntsaninja