Skip to content

Commit 6192ade

Browse files
fix else branch in match case with literals
1 parent 75306f0 commit 6192ade

File tree

3 files changed

+32
-4
lines changed

3 files changed

+32
-4
lines changed

mypy/subtypes.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2147,6 +2147,11 @@ def covers_at_runtime(item: Type, supertype: Type) -> bool:
21472147
item = get_proper_type(item)
21482148
supertype = get_proper_type(supertype)
21492149

2150+
# Use last known value for Instance types, if available.
2151+
# This ensures that e.g. Literal["max"]? is covered by Literal["max"].
2152+
if isinstance(item, Instance) and item.last_known_value is not None:
2153+
item = item.last_known_value
2154+
21502155
# Since runtime type checks will ignore type arguments, erase the types.
21512156
if not (isinstance(supertype, FunctionLike) and supertype.is_type_obj()):
21522157
supertype = erase_type(supertype)

mypy/test/testtypes.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ def test_simplified_union_with_literals(self) -> None:
589589
[fx.lit1_inst, fx.lit3_inst], UnionType([fx.lit1_inst, fx.lit3_inst])
590590
)
591591
self.assert_simplified_union([fx.lit1_inst, fx.uninhabited], fx.lit1_inst)
592-
self.assert_simplified_union([fx.lit1, fx.lit1_inst], fx.lit1)
592+
self.assert_simplified_union([fx.lit1, fx.lit1_inst], fx.lit1_inst)
593593
self.assert_simplified_union([fx.lit1, fx.lit2_inst], UnionType([fx.lit1, fx.lit2_inst]))
594594
self.assert_simplified_union([fx.lit1, fx.lit3_inst], UnionType([fx.lit1, fx.lit3_inst]))
595595

@@ -639,7 +639,9 @@ def test_simplified_union_with_mixed_str_literals(self) -> None:
639639
[fx.lit_str1, fx.lit_str2, fx.lit_str3_inst],
640640
UnionType([fx.lit_str1, fx.lit_str2, fx.lit_str3_inst]),
641641
)
642-
self.assert_simplified_union([fx.lit_str1, fx.lit_str1, fx.lit_str1_inst], fx.lit_str1)
642+
self.assert_simplified_union(
643+
[fx.lit_str1, fx.lit_str1, fx.lit_str1_inst], fx.lit_str1_inst
644+
)
643645

644646
def assert_simplified_union(self, original: list[Type], union: Type) -> None:
645647
assert_equal(make_simplified_union(original), union)

mypy/typeops.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,8 @@ def make_simplified_union(
580580
* [int, Any] -> Union[int, Any] (Any types are not simplified away!)
581581
* [Any, Any] -> Any
582582
* [int, Union[bytes, str]] -> Union[int, bytes, str]
583+
* [Literal[1]?, Literal[1]] -> Literal[1]?
584+
* Literal["max"]?, Literal["max", "sum"] -> Literal["max"]? | Literal["sum"]
583585
584586
Note: This must NOT be used during semantic analysis, since TypeInfos may not
585587
be fully initialized.
@@ -608,13 +610,32 @@ def make_simplified_union(
608610
):
609611
simplified_set = try_contracting_literals_in_union(simplified_set)
610612

611-
result = get_proper_type(UnionType.make_union(simplified_set, line, column))
613+
# Step 5: Combine Literals and Instances with LKVs, e.g. Literal[1]?, Literal[1] -> Literal[1]?
614+
new_items = []
615+
for item in simplified_set:
616+
if isinstance(item, LiteralType):
617+
# scan if there is an Instance with a last_known_value that matches
618+
for other in simplified_set:
619+
if (
620+
isinstance(other, Instance)
621+
and other.last_known_value is not None
622+
and item == other.last_known_value
623+
):
624+
# do not include item
625+
break
626+
else:
627+
new_items.append(item)
628+
else:
629+
# If the item is not a LiteralType, we can use it directly.
630+
new_items.append(item)
631+
632+
result = get_proper_type(UnionType.make_union(new_items, line, column))
612633

613634
nitems = len(items)
614635
if nitems > 1 and (
615636
nitems > 2 or not (type(items[0]) is NoneType or type(items[1]) is NoneType)
616637
):
617-
# Step 5: At last, we erase any (inconsistent) extra attributes on instances.
638+
# Step 6: At last, we erase any (inconsistent) extra attributes on instances.
618639

619640
# Initialize with None instead of an empty set as a micro-optimization. The set
620641
# is needed very rarely, so we try to avoid constructing it.

0 commit comments

Comments
 (0)