Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -8625,15 +8625,19 @@ def and_conditional_maps(m1: TypeMap, m2: TypeMap, *, use_meet: bool = False) ->
the truth of e2.
"""
# Both conditions can be true; combine the information. Anything
# we learn from either conditions' truth is valid. If the same
# expression's type is refined by both conditions, we somewhat
# arbitrarily give precedence to m2 unless m1 value is Any.
# In the future, we could use an intersection type or meet_types().
# we learn from either conditions' truth is valid.
# If the same expression's type is refined by both conditions and use_meet=False, we somewhat
# arbitrarily give precedence to m2 unless m2's value is Any or m1's value is Never.
result = m2.copy()
m2_keys = {literal_hash(n2) for n2 in m2}
m2_exprs = {literal_hash(n2): n2 for n2 in m2}
for n1 in m1:
if literal_hash(n1) not in m2_keys or isinstance(
get_proper_type(m1[n1]), (AnyType, UninhabitedType)
n1_hash = literal_hash(n1)
if n1_hash not in m2_exprs or (
not use_meet
and (
isinstance(get_proper_type(m1[n1]), UninhabitedType)
or isinstance(get_proper_type(m2[m2_exprs[n1_hash]]), AnyType)
)
):
result[n1] = m1[n1]
if use_meet:
Expand Down
20 changes: 20 additions & 0 deletions test-data/unit/check-isinstance.test
Original file line number Diff line number Diff line change
Expand Up @@ -1478,6 +1478,26 @@ def f(x: Any):
x + "foo" # E: Unsupported operand types for + ("int" and "str")
[builtins fixtures/isinstance.pyi]

[case testIsinstanceNarrowingAny]
# flags: --warn-unreachable
from typing import Any

class A: pass
class B(A): pass

def f1(x: Any) -> None:
if x and isinstance(x, A):
reveal_type(x) # N: Revealed type is "__main__.A"
if isinstance(x, A) and x:
reveal_type(x) # N: Revealed type is "__main__.A"

def f2(x: Any) -> None:
if isinstance(x, A) and not isinstance(x, B):
reveal_type(x) # N: Revealed type is "__main__.A"
if not isinstance(x, B) and isinstance(x, A):
reveal_type(x) # N: Revealed type is "__main__.A"
[builtins fixtures/isinstance.pyi]

[case testIsinstanceOfGenericClassRetainsParameters]
# flags: --warn-unreachable
from typing import List, Union
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-narrowing.test
Original file line number Diff line number Diff line change
Expand Up @@ -1727,7 +1727,7 @@ reveal_type(c) # N: Revealed type is "__main__.C"

c1: C
if isinstance(c1, tp) and isinstance(c1, D):
reveal_type(c1) # N: Revealed type is "Any"
reveal_type(c1) # N: Revealed type is "__main__.D"
else:
reveal_type(c1) # N: Revealed type is "__main__.C"
reveal_type(c1) # N: Revealed type is "__main__.C"
Expand All @@ -1741,7 +1741,7 @@ reveal_type(c2) # N: Revealed type is "__main__.C"

c3: C
if isinstance(c3, D) and isinstance(c3, tp):
reveal_type(c3) # N: Revealed type is "Any"
reveal_type(c3) # N: Revealed type is "__main__.D"
else:
reveal_type(c3) # N: Revealed type is "__main__.C"
reveal_type(c3) # N: Revealed type is "__main__.C"
Expand Down
Loading