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
63 changes: 63 additions & 0 deletions test-data/unit/check-narrowing.test
Original file line number Diff line number Diff line change
Expand Up @@ -3439,6 +3439,69 @@ def bar(X: type[T]) -> T:
raise
[builtins fixtures/type.pyi]

[case testNarrowingConstrainedTypeVarType]
# flags: --strict-equality --warn-unreachable
from typing import TypeVar, Any, Type

TargetType = TypeVar("TargetType", int, float, str)

def convert_type(target_type: Type[TargetType]) -> TargetType:
if target_type == str:
return str() # E: Incompatible return value type (got "str", expected "int") \
# E: Incompatible return value type (got "str", expected "float")
if target_type == int:
return int() # E: Incompatible return value type (got "int", expected "str")
if target_type == float:
return float() # E: Incompatible return value type (got "float", expected "int") \
# E: Incompatible return value type (got "float", expected "str")
raise
[builtins fixtures/primitives.pyi]


[case testNarrowingEqualityWithPromotions]
# flags: --strict-equality --warn-unreachable
from __future__ import annotations
from typing import Literal

def f1(number: float, i: int):
if number == i:
reveal_type(number) # N: Revealed type is "builtins.float"
reveal_type(i) # N: Revealed type is "builtins.int"

def f2(number: float, five: Literal[5]):
if number == five:
reveal_type(number) # E: Statement is unreachable
reveal_type(five)

def f3(number: float | int, five: Literal[5]):
if number == five:
reveal_type(number) # N: Revealed type is "Literal[5]"
reveal_type(five) # N: Revealed type is "Literal[5]"

def f4(number: float | None, i: int):
if number == i:
reveal_type(number) # N: Revealed type is "builtins.float | None"
reveal_type(i) # N: Revealed type is "builtins.int"

def f5(number: float | int, i: int):
if number == i:
reveal_type(number) # N: Revealed type is "builtins.int"
reveal_type(i) # N: Revealed type is "builtins.int"

def f6(number: float | complex, i: int):
if number == i:
reveal_type(number) # N: Revealed type is "builtins.float | builtins.complex"
reveal_type(i) # N: Revealed type is "builtins.int"

class Custom:
def __eq__(self, other: object) -> bool: return True

def f7(number: float, x: Custom | int):
if number == x:
reveal_type(number) # N: Revealed type is "builtins.float"
reveal_type(x) # N: Revealed type is "__main__.Custom"
[builtins fixtures/primitives.pyi]

[case testNarrowingAnyNegativeIntersection-xfail]
# flags: --strict-equality --warn-unreachable
# https://github.com/python/mypy/issues/20597
Expand Down
29 changes: 18 additions & 11 deletions test-data/unit/check-python310.test
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,17 @@ match m:
case other:
reveal_type(other) # N: Revealed type is "Any"

[case testMatchLiteralPatternAlreadyNarrower-skip]
m: bool
[case testMatchLiteralPatternBoolIntAlreadyNarrower]
# flags: --strict-equality --warn-unreachable

match m:
case 1:
reveal_type(m) # This should probably be unreachable, but isn't detected as such.
def foo(m: bool, x: int):
match m:
case 1:
reveal_type(m) # N: Revealed type is "Literal[1]"

match m:
case x:
reveal_type(m) # N: Revealed type is "builtins.bool"
[builtins fixtures/primitives.pyi]

[case testMatchLiteralPatternUnreachable]
Expand Down Expand Up @@ -402,7 +407,7 @@ match d.tag:
reveal_type(d) # N: Revealed type is "__main__.B"
reveal_type(d.num) # N: Revealed type is "builtins.int"

[case testMatchSequenceUnion-skip]
[case testMatchSequenceUnion]
from typing import List, Union
m: Union[List[List[str]], str]

Expand Down Expand Up @@ -1878,7 +1883,8 @@ match m:
assert_never(unreachable)
[builtins fixtures/enum.pyi]

[case testMatchLiteralPatternEnumCustomEquals-skip]
[case testMatchLiteralPatternEnumCustomEquals]
# flags: --strict-equality --warn-unreachable
from enum import Enum
class Medal(Enum):
gold = 1
Expand All @@ -1891,9 +1897,9 @@ m: Medal

match m:
case Medal.gold:
reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.gold]"
case _:
reveal_type(m) # N: Revealed type is "__main__.Medal"
case _:
reveal_type(m) # N: Revealed type is "Literal[__main__.Medal.silver] | Literal[__main__.Medal.bronze]"
[builtins fixtures/enum.pyi]

[case testMatchNarrowUsingPatternGuardSpecialCase]
Expand Down Expand Up @@ -2035,7 +2041,8 @@ match c:
assert_never(c) # E: Argument 1 to "assert_never" has incompatible type "Literal['red']"; expected "Never"
[typing fixtures/typing-typeddict.pyi]

[case testMatchAsPatternIntersection-skip]
[case testMatchAsPatternIntersection]
# flags: --strict-equality --warn-unreachable
class A: pass
class B: pass
class C: pass
Expand All @@ -2046,7 +2053,7 @@ def f(x: A) -> None:
reveal_type(y) # N: Revealed type is "__main__.<subclass of "__main__.A" and "__main__.B">"
case C() as y:
reveal_type(y) # N: Revealed type is "__main__.<subclass of "__main__.A" and "__main__.C">"
reveal_type(y) # N: Revealed type is "Union[__main__.<subclass of "__main__.A" and "__main__.B">, __main__.<subclass of "__main__.A" and "__main__.C">]"
reveal_type(y) # N: Revealed type is "__main__.<subclass of "__main__.A" and "__main__.B"> | __main__.<subclass of "__main__.A" and "__main__.C">"

[case testMatchWithBreakAndContinue]
def f(x: int | str | None) -> None:
Expand Down
19 changes: 13 additions & 6 deletions test-data/unit/check-tuples.test
Original file line number Diff line number Diff line change
Expand Up @@ -1533,17 +1533,24 @@ reveal_type(x) # N: Revealed type is "tuple[builtins.int, builtins.int]"
[builtins fixtures/tuple.pyi]

[case testTupleOverlapDifferentTuples]
# flags: --warn-unreachable
from typing import Optional, Tuple
class A: pass
class B: pass

possibles: Tuple[int, Tuple[A]]
x: Optional[Tuple[B]]
def f1(possibles: Tuple[int, Tuple[A]], x: Optional[Tuple[B]]):
if x in possibles:
reveal_type(x) # N: Revealed type is "tuple[__main__.B]"
else:
reveal_type(x) # N: Revealed type is "tuple[__main__.B] | None"

class AA(A): pass

if x in possibles:
reveal_type(x) # N: Revealed type is "tuple[__main__.B]"
else:
reveal_type(x) # N: Revealed type is "tuple[__main__.B] | None"
def f2(possibles: Tuple[int, Tuple[A]], x: Optional[Tuple[AA]]):
if x in possibles:
reveal_type(x) # N: Revealed type is "tuple[__main__.AA]"
else:
reveal_type(x) # N: Revealed type is "tuple[__main__.AA] | None"

[builtins fixtures/tuple.pyi]

Expand Down