Skip to content

Commit aa4b74f

Browse files
fix else branch in match case with literals
1 parent be0091a commit aa4b74f

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
@@ -2136,6 +2136,11 @@ def covers_at_runtime(item: Type, supertype: Type) -> bool:
21362136
item = get_proper_type(item)
21372137
supertype = get_proper_type(supertype)
21382138

2139+
# Use last known value for Instance types, if available.
2140+
# This ensures that e.g. Literal["max"]? is covered by Literal["max"].
2141+
if isinstance(item, Instance) and item.last_known_value is not None:
2142+
item = item.last_known_value
2143+
21392144
# Since runtime type checks will ignore type arguments, erase the types.
21402145
if not (isinstance(supertype, FunctionLike) and supertype.is_type_obj()):
21412146
supertype = erase_type(supertype)

mypy/test/testtypes.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ def test_simplified_union_with_literals(self) -> None:
601601
[fx.lit1_inst, fx.lit3_inst], UnionType([fx.lit1_inst, fx.lit3_inst])
602602
)
603603
self.assert_simplified_union([fx.lit1_inst, fx.uninhabited], fx.lit1_inst)
604-
self.assert_simplified_union([fx.lit1, fx.lit1_inst], fx.lit1)
604+
self.assert_simplified_union([fx.lit1, fx.lit1_inst], fx.lit1_inst)
605605
self.assert_simplified_union([fx.lit1, fx.lit2_inst], UnionType([fx.lit1, fx.lit2_inst]))
606606
self.assert_simplified_union([fx.lit1, fx.lit3_inst], UnionType([fx.lit1, fx.lit3_inst]))
607607

@@ -651,7 +651,9 @@ def test_simplified_union_with_mixed_str_literals(self) -> None:
651651
[fx.lit_str1, fx.lit_str2, fx.lit_str3_inst],
652652
UnionType([fx.lit_str1, fx.lit_str2, fx.lit_str3_inst]),
653653
)
654-
self.assert_simplified_union([fx.lit_str1, fx.lit_str1, fx.lit_str1_inst], fx.lit_str1)
654+
self.assert_simplified_union(
655+
[fx.lit_str1, fx.lit_str1, fx.lit_str1_inst], fx.lit_str1_inst
656+
)
655657

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

mypy/typeops.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,8 @@ def make_simplified_union(
571571
* [int, Any] -> Union[int, Any] (Any types are not simplified away!)
572572
* [Any, Any] -> Any
573573
* [int, Union[bytes, str]] -> Union[int, bytes, str]
574+
* [Literal[1]?, Literal[1]] -> Literal[1]?
575+
* Literal["max"]?, Literal["max", "sum"] -> Literal["max"]? | Literal["sum"]
574576
575577
Note: This must NOT be used during semantic analysis, since TypeInfos may not
576578
be fully initialized.
@@ -599,13 +601,32 @@ def make_simplified_union(
599601
):
600602
simplified_set = try_contracting_literals_in_union(simplified_set)
601603

602-
result = get_proper_type(UnionType.make_union(simplified_set, line, column))
604+
# Step 5: Combine Literals and Instances with LKVs, e.g. Literal[1]?, Literal[1] -> Literal[1]?
605+
new_items = []
606+
for item in simplified_set:
607+
if isinstance(item, LiteralType):
608+
# scan if there is an Instance with a last_known_value that matches
609+
for other in simplified_set:
610+
if (
611+
isinstance(other, Instance)
612+
and other.last_known_value is not None
613+
and item == other.last_known_value
614+
):
615+
# do not include item
616+
break
617+
else:
618+
new_items.append(item)
619+
else:
620+
# If the item is not a LiteralType, we can use it directly.
621+
new_items.append(item)
622+
623+
result = get_proper_type(UnionType.make_union(new_items, line, column))
603624

604625
nitems = len(items)
605626
if nitems > 1 and (
606627
nitems > 2 or not (type(items[0]) is NoneType or type(items[1]) is NoneType)
607628
):
608-
# Step 5: At last, we erase any (inconsistent) extra attributes on instances.
629+
# Step 6: At last, we erase any (inconsistent) extra attributes on instances.
609630

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

0 commit comments

Comments
 (0)