Skip to content

Commit 56c33f2

Browse files
Fix conditional_types remainder for structural types (#20450)
Fixes #20445 Fixes a regression in `conditional_type` when given a union of `Any | Protocol`
1 parent 0cc21d9 commit 56c33f2

File tree

2 files changed

+42
-29
lines changed

2 files changed

+42
-29
lines changed

mypy/checker.py

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8394,43 +8394,42 @@ def conditional_types(
83948394

83958395
if isinstance(proper_type, AnyType):
83968396
return proposed_type, current_type
8397-
elif isinstance(proposed_type, AnyType):
8397+
if isinstance(proposed_type, AnyType):
83988398
# We don't really know much about the proposed type, so we shouldn't
83998399
# attempt to narrow anything. Instead, we broaden the expr to Any to
84008400
# avoid false positives
84018401
return proposed_type, default
8402-
elif not any(type_range.is_upper_bound for type_range in proposed_type_ranges) and (
8403-
# concrete subtypes
8404-
is_proper_subtype(current_type, proposed_type, ignore_promotions=True)
8402+
if not any(type_range.is_upper_bound for type_range in proposed_type_ranges):
8403+
# concrete subtype
8404+
if is_proper_subtype(current_type, proposed_type, ignore_promotions=True):
8405+
return default, UninhabitedType()
8406+
84058407
# structural subtypes
8406-
or (
8407-
(
8408-
isinstance(proposed_type, CallableType)
8409-
or (isinstance(proposed_type, Instance) and proposed_type.type.is_protocol)
8408+
if (
8409+
isinstance(proposed_type, CallableType)
8410+
or (isinstance(proposed_type, Instance) and proposed_type.type.is_protocol)
8411+
) and is_subtype(current_type, proposed_type, ignore_promotions=True):
8412+
# Note: It's possible that current_type=`Any | Proto` while proposed_type=`Proto`
8413+
# so we cannot return `Never` for the else branch
8414+
remainder = restrict_subtype_away(
8415+
current_type,
8416+
default if default is not None else proposed_type,
8417+
consider_runtime_isinstance=consider_runtime_isinstance,
84108418
)
8411-
and is_subtype(current_type, proposed_type, ignore_promotions=True)
8412-
)
8413-
):
8414-
# Expression is always of one of the types in proposed_type_ranges
8415-
return default, UninhabitedType()
8416-
elif not is_overlapping_types(current_type, proposed_type, ignore_promotions=True):
8419+
return default, remainder
8420+
if not is_overlapping_types(current_type, proposed_type, ignore_promotions=True):
84178421
# Expression is never of any type in proposed_type_ranges
84188422
return UninhabitedType(), default
8419-
else:
8420-
# we can only restrict when the type is precise, not bounded
8421-
proposed_precise_type = UnionType.make_union(
8422-
[
8423-
type_range.item
8424-
for type_range in proposed_type_ranges
8425-
if not type_range.is_upper_bound
8426-
]
8427-
)
8428-
remaining_type = restrict_subtype_away(
8429-
current_type,
8430-
proposed_precise_type,
8431-
consider_runtime_isinstance=consider_runtime_isinstance,
8432-
)
8433-
return proposed_type, remaining_type
8423+
# we can only restrict when the type is precise, not bounded
8424+
proposed_precise_type = UnionType.make_union(
8425+
[type_range.item for type_range in proposed_type_ranges if not type_range.is_upper_bound]
8426+
)
8427+
remaining_type = restrict_subtype_away(
8428+
current_type,
8429+
proposed_precise_type,
8430+
consider_runtime_isinstance=consider_runtime_isinstance,
8431+
)
8432+
return proposed_type, remaining_type
84348433

84358434

84368435
def conditional_types_to_typemaps(

test-data/unit/check-errorcodes.test

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,20 @@ class Foo:
866866
return 2
867867
[builtins fixtures/list.pyi]
868868

869+
[case testRedundantExpressionsUnionWithAny]
870+
# flags: --enable-error-code redundant-expr
871+
from typing import Any, Awaitable, Union
872+
ok_list: list[Union[Any, Awaitable]]
873+
for y in ok_list:
874+
z = 1 if isinstance(y, Awaitable) else 2
875+
876+
bad_list: list[Awaitable]
877+
for y in bad_list:
878+
z = 1 if isinstance(y, Awaitable) else 2 # E: If condition is always true [redundant-expr]
879+
[builtins fixtures/isinstancelist.pyi]
880+
[typing fixtures/typing-full.pyi]
881+
882+
869883
[case testNamedTupleNameMismatch]
870884
from typing import NamedTuple
871885

0 commit comments

Comments
 (0)