| -- Type checker test cases for conditional checks that result in some |
| -- blocks classified as unreachable (they are not type checked or semantically |
| -- analyzed). |
| -- |
| -- For example, we skip blocks that will not be executed on the active |
| -- Python version. |
| |
| [case testConditionalTypeAliasPY3] |
| import typing |
| def f(): pass |
| PY3 = f() |
| if PY3: |
| t = int |
| x = object() + 'x' # E: Unsupported left operand type for + ("object") |
| else: |
| t = str |
| y = 'x' / 1 |
| x |
| z = 1 # type: t |
| |
| [case testConditionalAssignmentPY2] |
| import typing |
| def f(): pass |
| PY2 = f() |
| if PY2: |
| x = object() + 'x' |
| else: |
| y = 'x' / 1 # E: Unsupported left operand type for / ("str") |
| y |
| |
| [case testConditionalImport] |
| import typing |
| def f(): pass |
| PY2 = f() |
| if PY2: |
| import fuzzybar |
| from barbar import * |
| from pawwaw import a, bc |
| else: |
| import m |
| [file m.py] |
| import typing |
| x = 1 |
| if int(): |
| x = 'a' |
| [out] |
| tmp/m.py:4: error: Incompatible types in assignment (expression has type "str", variable has type "int") |
| |
| [case testNegatedMypyConditional] |
| import typing |
| MYPY = 0 |
| if not MYPY: |
| import xyz753 |
| else: |
| import pow123 # E |
| [builtins fixtures/bool.pyi] |
| [out] |
| main:6: error: Cannot find implementation or library stub for module named "pow123" |
| main:6: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports |
| |
| [case testMypyConditional] |
| import typing |
| MYPY = 0 |
| if MYPY: |
| None + 1 # E: Unsupported left operand type for + ("None") |
| else: |
| None + '' |
| [builtins fixtures/bool.pyi] |
| |
| [case testTypeCheckingConditional] |
| import typing |
| if typing.TYPE_CHECKING: |
| import pow123 # E |
| else: |
| import xyz753 |
| [typing fixtures/typing-medium.pyi] |
| [out] |
| main:3: error: Cannot find implementation or library stub for module named "pow123" |
| main:3: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports |
| |
| [case testTypeCheckingConditionalFromImport] |
| from typing import TYPE_CHECKING |
| if TYPE_CHECKING: |
| import pow123 # E |
| else: |
| import xyz753 |
| [typing fixtures/typing-medium.pyi] |
| [out] |
| main:3: error: Cannot find implementation or library stub for module named "pow123" |
| main:3: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports |
| |
| [case testNegatedTypeCheckingConditional] |
| import typing |
| if not typing.TYPE_CHECKING: |
| import pow123 # E |
| else: |
| import xyz753 |
| [builtins fixtures/bool.pyi] |
| [typing fixtures/typing-medium.pyi] |
| [out] |
| main:5: error: Cannot find implementation or library stub for module named "xyz753" |
| main:5: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports |
| |
| [case testUndefinedTypeCheckingConditional] |
| if not TYPE_CHECKING: # E |
| import pow123 |
| else: |
| import xyz753 |
| [builtins fixtures/bool.pyi] |
| [out] |
| main:1: error: Name "TYPE_CHECKING" is not defined |
| main:4: error: Cannot find implementation or library stub for module named "xyz753" |
| main:4: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports |
| |
| [case testConditionalClassDefPY3] |
| def f(): pass |
| PY3 = f() |
| if PY3: |
| pass |
| else: |
| class X(object): |
| pass |
| |
| [case testUnreachabilityAndElifPY3] |
| def f(): pass |
| PY3 = f() |
| if PY3: |
| pass |
| elif bool(): |
| import nonexistent |
| 1 + '' |
| else: |
| import bad_name |
| 1 + '' |
| [builtins fixtures/bool.pyi] |
| [out] |
| |
| [case testSysVersionInfo] |
| import sys |
| if sys.version_info[0] >= 3: |
| def foo() -> int: return 0 |
| else: |
| def foo() -> str: return '' |
| reveal_type(foo()) # N: Revealed type is "builtins.int" |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysVersionInfoReversedOperandsOrder] |
| import sys |
| if (3,) <= sys.version_info: |
| def foo() -> int: return 0 |
| else: |
| def foo() -> str: return '' |
| reveal_type(foo()) # N: Revealed type is "builtins.int" |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysVersionInfoNegated] |
| import sys |
| if not (sys.version_info[0] < 3): |
| def foo() -> int: return 0 |
| else: |
| def foo() -> str: return '' |
| reveal_type(foo()) # N: Revealed type is "builtins.int" |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysVersionInfoSliced1] |
| import sys |
| if sys.version_info[:1] >= (3,): |
| def foo() -> int: return 0 |
| else: |
| def foo() -> str: return '' |
| foo() + 0 |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysVersionInfoSliced2] |
| import sys |
| if sys.version_info[:2] >= (3, 0): |
| def foo() -> int: return 0 |
| else: |
| def foo() -> str: return '' |
| foo() + 0 |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysVersionInfoSliced3] |
| import sys |
| if sys.version_info[:] >= (3, 0): |
| def foo() -> int: return 0 |
| else: |
| def foo() -> str: return '' |
| foo() + 0 |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysVersionInfoSliced4] |
| import sys |
| if sys.version_info[0:2] >= (3, 0): |
| def foo() -> int: return 0 |
| else: |
| def foo() -> str: return '' |
| foo() + 0 |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysVersionInfoSliced5] |
| import sys |
| if sys.version_info[0:] >= (3,): |
| def foo() -> int: return 0 |
| else: |
| def foo() -> str: return '' |
| foo() + 0 |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysVersionInfoSliced6] |
| import sys |
| if sys.version_info[1:] >= (5,): |
| def foo() -> int: return 0 |
| else: |
| def foo() -> str: return '' |
| foo() + 0 |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysVersionInfoSliced7] |
| import sys |
| if sys.version_info >= (3, 5): |
| def foo() -> int: return 0 |
| else: |
| def foo() -> str: return '' |
| foo() + 0 |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysVersionInfoSliced8] |
| # Our pyversion only has (major, minor), |
| # so testing for (major, minor, bugfix) is unsupported. |
| import sys |
| if sys.version_info >= (3, 5, 0): |
| def foo() -> int: return 0 |
| else: |
| def foo() -> str: return '' # E: All conditional function variants must have identical signatures \ |
| # N: Original: \ |
| # N: def foo() -> int \ |
| # N: Redefinition: \ |
| # N: def foo() -> str |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysVersionInfoSliced9] |
| # Our pyversion only has (major, minor), |
| # so testing for (minor, bugfix) is unsupported (also it's silly :-). |
| import sys |
| if sys.version_info[1:] >= (5, 0): |
| def foo() -> int: return 0 |
| else: |
| def foo() -> str: return '' # E: All conditional function variants must have identical signatures \ |
| # N: Original: \ |
| # N: def foo() -> int \ |
| # N: Redefinition: \ |
| # N: def foo() -> str |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysPlatform1] |
| import sys |
| if sys.platform == 'fictional': |
| def foo() -> int: return 0 |
| else: |
| def foo() -> str: return '' |
| foo() + '' |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysPlatform2] |
| import sys |
| if sys.platform != 'fictional': |
| def foo() -> int: return 0 |
| else: |
| def foo() -> str: return '' |
| foo() + 0 |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysPlatformNegated] |
| import sys |
| if not (sys.platform == 'fictional'): |
| def foo() -> int: return 0 |
| else: |
| def foo() -> str: return '' |
| foo() + 0 |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysVersionInfoClass] |
| import sys |
| if sys.version_info < (3, 5): |
| class C: |
| pass |
| else: |
| class C: |
| def foo(self) -> int: return 0 |
| C().foo() + 0 |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysVersionInfoImport] |
| import sys |
| if sys.version_info >= (3, 5): |
| import collections |
| else: |
| collections = None |
| Pt = collections.namedtuple('Pt', 'x y z') |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysVersionInfoVariable] |
| import sys |
| if sys.version_info >= (3, 5): |
| x = '' |
| else: |
| x = 0 |
| x + '' |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysVersionInfoInClass] |
| import sys |
| class C: |
| if sys.version_info >= (3, 5): |
| def foo(self) -> int: return 0 |
| else: |
| def foo(self) -> str: return '' |
| reveal_type(C().foo()) # N: Revealed type is "builtins.int" |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysVersionInfoInFunction] |
| import sys |
| def foo() -> None: |
| if sys.version_info >= (3, 5): |
| x = '' |
| else: |
| x = 0 |
| reveal_type(x) # N: Revealed type is "builtins.str" |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysPlatformInMethod] |
| import sys |
| class C: |
| def foo(self) -> None: |
| if sys.platform != 'fictional': |
| x = '' |
| else: |
| x = 0 |
| reveal_type(x) # N: Revealed type is "builtins.str" |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysPlatformInFunctionImport1] |
| import sys |
| def foo() -> None: |
| if sys.platform != 'fictional': |
| import a |
| else: |
| import b as a |
| a.x |
| [file a.py] |
| x = 1 |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysPlatformInFunctionImport2] |
| import sys |
| def foo() -> None: |
| if sys.platform == 'fictional': |
| import b as a |
| else: |
| import a |
| a.x |
| [file a.py] |
| x = 1 |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testSysPlatformInFunctionImport3] |
| from typing import Callable |
| import sys |
| |
| def idf(x: Callable[[], None]) -> Callable[[], None]: return x |
| |
| @idf |
| def foo() -> None: |
| if sys.platform == 'fictional': |
| import b as a |
| else: |
| import a |
| a.x |
| [file a.py] |
| x = 1 |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| |
| [case testSysPlatformInMethodImport2] |
| |
| import sys |
| class A: |
| def foo(self) -> None: |
| if sys.platform == 'fictional': |
| import b as a |
| else: |
| import a |
| a.x |
| [file a.py] |
| x = 1 |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testCustomSysVersionInfo] |
| # flags: --python-version 3.11 |
| import sys |
| if sys.version_info == (3, 11): |
| x = "foo" |
| else: |
| x = 3 |
| reveal_type(x) # N: Revealed type is "builtins.str" |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testCustomSysVersionInfo2] |
| # flags: --python-version 3.11 |
| import sys |
| if sys.version_info == (3, 6): |
| x = "foo" |
| else: |
| x = 3 |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testCustomSysPlatform] |
| # flags: --platform linux |
| import sys |
| if sys.platform == 'linux': |
| x = "foo" |
| else: |
| x = 3 |
| reveal_type(x) # N: Revealed type is "builtins.str" |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testCustomSysPlatform2] |
| # flags: --platform win32 |
| import sys |
| if sys.platform == 'linux': |
| x = "foo" |
| else: |
| x = 3 |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testCustomSysPlatformStartsWith] |
| # flags: --platform win32 |
| import sys |
| if sys.platform.startswith('win'): |
| x = "foo" |
| else: |
| x = 3 |
| reveal_type(x) # N: Revealed type is "builtins.str" |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testShortCircuitInExpression] |
| import typing |
| def make() -> bool: pass |
| PY2 = PY3 = make() |
| |
| a = PY2 and 's' |
| b = PY3 and 's' |
| c = PY2 or 's' |
| d = PY3 or 's' |
| e = (PY2 or PY3) and 's' |
| f = (PY3 or PY2) and 's' |
| g = (PY2 or PY3) or 's' |
| h = (PY3 or PY2) or 's' |
| reveal_type(a) # N: Revealed type is "builtins.bool" |
| reveal_type(b) # N: Revealed type is "Literal['s']" |
| reveal_type(c) # N: Revealed type is "Literal['s']" |
| reveal_type(d) # N: Revealed type is "builtins.bool" |
| reveal_type(e) # N: Revealed type is "Literal['s']" |
| reveal_type(f) # N: Revealed type is "Literal['s']" |
| reveal_type(g) # N: Revealed type is "builtins.bool" |
| reveal_type(h) # N: Revealed type is "builtins.bool" |
| [builtins fixtures/ops.pyi] |
| [out] |
| |
| [case testShortCircuitAndWithConditionalAssignment] |
| # flags: --platform linux |
| import sys |
| |
| def f(): pass |
| PY2 = f() |
| if PY2 and sys.platform == 'linux': |
| x = 'foo' |
| else: |
| x = 3 |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| if sys.platform == 'linux' and PY2: |
| y = 'foo' |
| else: |
| y = 3 |
| reveal_type(y) # N: Revealed type is "builtins.int" |
| [builtins fixtures/ops.pyi] |
| |
| [case testShortCircuitOrWithConditionalAssignment] |
| # flags: --platform linux |
| import sys |
| |
| def f(): pass |
| PY2 = f() |
| if PY2 or sys.platform == 'linux': |
| x = 'foo' |
| else: |
| x = 3 |
| reveal_type(x) # N: Revealed type is "builtins.str" |
| if sys.platform == 'linux' or PY2: |
| y = 'foo' |
| else: |
| y = 3 |
| reveal_type(y) # N: Revealed type is "builtins.str" |
| [builtins fixtures/ops.pyi] |
| |
| [case testShortCircuitNoEvaluation] |
| # flags: --platform linux --always-false COMPILE_TIME_FALSE |
| import sys |
| |
| if sys.platform == 'darwin': |
| mac_only = 'junk' |
| |
| # `mac_only` should not be evaluated |
| if sys.platform == 'darwin' and mac_only: |
| pass |
| if sys.platform == 'linux' or mac_only: |
| pass |
| |
| COMPILE_TIME_FALSE = 'junk' |
| |
| if COMPILE_TIME_FALSE: |
| compile_time_false_only = 'junk' |
| |
| # `compile_time_false_only` should not be evaluated |
| if COMPILE_TIME_FALSE and compile_time_false_only: |
| pass |
| if not COMPILE_TIME_FALSE or compile_time_false_only: |
| pass |
| |
| MYPY = False |
| |
| if not MYPY: |
| mypy_only = 'junk' |
| |
| # `mypy_only` should not be evaluated |
| if not MYPY and mypy_only: |
| pass |
| if MYPY or mypy_only: |
| pass |
| [builtins fixtures/ops.pyi] |
| |
| [case testSemanticAnalysisFalseButTypeNarrowingTrue] |
| # flags: --always-false COMPILE_TIME_FALSE |
| from typing import Literal |
| |
| indeterminate: str |
| COMPILE_TIME_FALSE: Literal[True] # type-narrowing: mapped in 'if' only |
| a = COMPILE_TIME_FALSE or indeterminate |
| reveal_type(a) # N: Revealed type is "builtins.str" |
| b = indeterminate or COMPILE_TIME_FALSE |
| reveal_type(b) # N: Revealed type is "Union[builtins.str, Literal[True]]" |
| [typing fixtures/typing-medium.pyi] |
| |
| [case testSemanticAnalysisTrueButTypeNarrowingFalse] |
| # flags: --always-true COMPILE_TIME_TRUE |
| from typing import Literal |
| |
| indeterminate: str |
| COMPILE_TIME_TRUE: Literal[False] # type narrowed to `else` only |
| a = COMPILE_TIME_TRUE or indeterminate |
| reveal_type(a) # N: Revealed type is "Literal[False]" |
| b = indeterminate or COMPILE_TIME_TRUE |
| reveal_type(b) # N: Revealed type is "Union[builtins.str, Literal[False]]" |
| |
| [typing fixtures/typing-medium.pyi] |
| [case testConditionalAssertWithoutElse] |
| import typing |
| |
| class A: pass |
| class B(A): pass |
| |
| x = A() |
| reveal_type(x) # N: Revealed type is "__main__.A" |
| |
| if typing.TYPE_CHECKING: |
| assert isinstance(x, B) |
| reveal_type(x) # N: Revealed type is "__main__.B" |
| |
| reveal_type(x) # N: Revealed type is "__main__.B" |
| |
| [builtins fixtures/isinstancelist.pyi] |
| [typing fixtures/typing-medium.pyi] |
| |
| [case testUnreachableWhenSuperclassIsAny] |
| from typing import Any |
| |
| # This can happen if we're importing a class from a missing module |
| Parent: Any |
| class Child(Parent): |
| def foo(self) -> int: |
| reveal_type(self) # N: Revealed type is "__main__.Child" |
| if self is None: |
| reveal_type(self) |
| return None |
| reveal_type(self) # N: Revealed type is "__main__.Child" |
| return 3 |
| |
| def bar(self) -> int: |
| if 1: |
| self = super(Child, self).something() |
| reveal_type(self) # N: Revealed type is "__main__.Child" |
| if self is None: |
| reveal_type(self) |
| return None |
| reveal_type(self) # N: Revealed type is "__main__.Child" |
| return 3 |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testUnreachableWhenSuperclassIsAnyNoStrictOptional] |
| # flags: --no-strict-optional |
| from typing import Any |
| |
| Parent: Any |
| class Child(Parent): |
| def foo(self) -> int: |
| reveal_type(self) # N: Revealed type is "__main__.Child" |
| if self is None: |
| reveal_type(self) # N: Revealed type is "None" |
| return None |
| reveal_type(self) # N: Revealed type is "__main__.Child" |
| return 3 |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testUnreachableAfterToplevelAssert] |
| import sys |
| reveal_type(0) # N: Revealed type is "Literal[0]?" |
| assert sys.platform == 'lol' |
| reveal_type('') # No error here :-) |
| [builtins fixtures/ops.pyi] |
| |
| [case testUnreachableAfterToplevelAssert2] |
| import sys |
| reveal_type(0) # N: Revealed type is "Literal[0]?" |
| assert sys.version_info[0] == 1 |
| reveal_type('') # No error here :-) |
| [builtins fixtures/ops.pyi] |
| |
| [case testUnreachableAfterToplevelAssert3] |
| reveal_type(0) # N: Revealed type is "Literal[0]?" |
| MYPY = False |
| assert not MYPY |
| reveal_type('') # No error here :-) |
| [builtins fixtures/ops.pyi] |
| |
| [case testUnreachableAfterToplevelAssert4] |
| # flags: --always-false NOPE |
| reveal_type(0) # N: Revealed type is "Literal[0]?" |
| NOPE = False |
| assert NOPE |
| reveal_type('') # No error here :-) |
| [builtins fixtures/ops.pyi] |
| |
| [case testUnreachableAfterToplevelAssertImport] |
| import foo |
| foo.bar() # E: "object" has no attribute "bar" |
| [file foo.py] |
| import sys |
| assert sys.platform == 'lol' |
| def bar() -> None: pass |
| [builtins fixtures/ops.pyi] |
| |
| [case testUnreachableAfterToplevelAssertImport2] |
| # flags: --platform lol |
| import foo |
| foo.bar() # No error :-) |
| [file foo.py] |
| import sys |
| assert sys.platform == 'lol' |
| def bar() -> None: pass |
| [builtins fixtures/ops.pyi] |
| |
| [case testUnreachableAfterToplevelAssertNotInsideIf] |
| import sys |
| if sys.version_info[0] >= 2: |
| assert sys.platform == 'lol' |
| reveal_type('') # N: Revealed type is "Literal['']?" |
| reveal_type('') # N: Revealed type is "Literal['']?" |
| [builtins fixtures/ops.pyi] |
| |
| [case testUnreachableFlagWithBadControlFlow1] |
| # flags: --warn-unreachable |
| a: int |
| if isinstance(a, int): |
| reveal_type(a) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(a) # E: Statement is unreachable |
| [builtins fixtures/isinstancelist.pyi] |
| |
| [case testUnreachableFlagWithBadControlFlow2] |
| # flags: --warn-unreachable |
| b: int |
| while isinstance(b, int): |
| reveal_type(b) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(b) # E: Statement is unreachable |
| [builtins fixtures/isinstancelist.pyi] |
| |
| [case testUnreachableFlagWithBadControlFlow3] |
| # flags: --warn-unreachable |
| def foo(c: int) -> None: |
| reveal_type(c) # N: Revealed type is "builtins.int" |
| assert not isinstance(c, int) |
| reveal_type(c) # E: Statement is unreachable |
| [builtins fixtures/isinstancelist.pyi] |
| |
| [case testUnreachableFlagWithBadControlFlow4] |
| # flags: --warn-unreachable |
| d: int |
| if False: |
| reveal_type(d) # E: Statement is unreachable |
| [builtins fixtures/isinstancelist.pyi] |
| |
| [case testUnreachableFlagWithBadControlFlow5] |
| # flags: --warn-unreachable |
| e: int |
| if True: |
| reveal_type(e) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(e) # E: Statement is unreachable |
| [builtins fixtures/isinstancelist.pyi] |
| |
| [case testUnreachableFlagStatementAfterReturn] |
| # flags: --warn-unreachable |
| def foo(x: int) -> None: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| return |
| reveal_type(x) # E: Statement is unreachable |
| |
| [case testUnreachableFlagTryBlocks] |
| # flags: --warn-unreachable |
| |
| def foo(x: int) -> int: |
| try: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| return x |
| reveal_type(x) # E: Statement is unreachable |
| finally: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| if True: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(x) # E: Statement is unreachable |
| |
| def bar(x: int) -> int: |
| try: |
| if True: |
| raise Exception() |
| reveal_type(x) # E: Statement is unreachable |
| except: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| return x |
| else: |
| reveal_type(x) # E: Statement is unreachable |
| |
| def baz(x: int) -> int: |
| try: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| except: |
| # Mypy assumes all lines could throw an exception |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| return x |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| return x |
| [builtins fixtures/exception.pyi] |
| |
| [case testUnreachableFlagIgnoresSemanticAnalysisUnreachable] |
| # flags: --warn-unreachable --python-version 3.7 --platform win32 --always-false FOOBAR |
| import sys |
| from typing import TYPE_CHECKING |
| |
| x: int |
| if TYPE_CHECKING: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(x) |
| |
| if not TYPE_CHECKING: |
| reveal_type(x) |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| |
| if sys.platform == 'darwin': |
| reveal_type(x) |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| |
| if sys.platform == 'win32': |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(x) |
| |
| if sys.version_info == (2, 7): |
| reveal_type(x) |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| |
| if sys.version_info == (3, 7): |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(x) |
| |
| FOOBAR = "" |
| if FOOBAR: |
| reveal_type(x) |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| [builtins fixtures/ops.pyi] |
| [typing fixtures/typing-medium.pyi] |
| |
| [case testUnreachableFlagIgnoresSemanticAnalysisExprUnreachable] |
| # flags: --warn-unreachable --always-false FOOBAR |
| import sys |
| from typing import TYPE_CHECKING |
| |
| FOOBAR = "" |
| |
| def foo() -> bool: ... |
| |
| lst = [1, 2, 3] |
| |
| a = FOOBAR and foo() |
| b = (not FOOBAR) or foo() |
| c = 1 if FOOBAR else 2 |
| d = [x for x in lst if FOOBAR] |
| [builtins fixtures/list.pyi] |
| [typing fixtures/typing-medium.pyi] |
| |
| [case testUnreachableFlagOkWithDeadStatements] |
| # flags: --warn-unreachable |
| from typing import NoReturn |
| def assert_never(x: NoReturn) -> NoReturn: |
| assert False |
| |
| def nonthrowing_assert_never(x: NoReturn) -> None: ... |
| |
| def expect_str(x: str) -> str: pass |
| |
| x: int |
| if False: |
| assert False |
| reveal_type(x) # E: Statement is unreachable |
| |
| if False: |
| raise Exception() |
| reveal_type(x) # E: Statement is unreachable |
| |
| if False: |
| assert_never(x) |
| reveal_type(x) # E: Statement is unreachable |
| |
| if False: |
| nonthrowing_assert_never(x) # E: Statement is unreachable |
| reveal_type(x) |
| |
| if False: |
| # Ignore obvious type errors |
| assert_never(expect_str(x)) |
| reveal_type(x) # E: Statement is unreachable |
| [builtins fixtures/exception.pyi] |
| |
| [case testNeverVariants] |
| from typing import Never |
| from typing_extensions import Never as TENever |
| from typing import NoReturn |
| from typing_extensions import NoReturn as TENoReturn |
| from mypy_extensions import NoReturn as MENoReturn |
| |
| bottom1: Never |
| reveal_type(bottom1) # N: Revealed type is "<nothing>" |
| bottom2: TENever |
| reveal_type(bottom2) # N: Revealed type is "<nothing>" |
| bottom3: NoReturn |
| reveal_type(bottom3) # N: Revealed type is "<nothing>" |
| bottom4: TENoReturn |
| reveal_type(bottom4) # N: Revealed type is "<nothing>" |
| bottom5: MENoReturn |
| reveal_type(bottom5) # N: Revealed type is "<nothing>" |
| |
| [builtins fixtures/tuple.pyi] |
| |
| [case testUnreachableFlagExpressions] |
| # flags: --warn-unreachable |
| def foo() -> bool: ... |
| |
| lst = [1, 2, 3, 4] |
| |
| a = True or foo() # E: Right operand of "or" is never evaluated |
| b = 42 or False # E: Right operand of "or" is never evaluated |
| d = False and foo() # E: Right operand of "and" is never evaluated |
| e = True or (True or (True or foo())) # E: Right operand of "or" is never evaluated |
| f = (True or foo()) or (True or foo()) # E: Right operand of "or" is never evaluated |
| |
| k = [x for x in lst if isinstance(x, int) or foo()] # E: Right operand of "or" is never evaluated |
| [builtins fixtures/isinstancelist.pyi] |
| |
| [case testUnreachableFlagMiscTestCaseMissingMethod] |
| # flags: --warn-unreachable |
| |
| class Case1: |
| def test1(self) -> bool: |
| return False and self.missing() # E: Right operand of "and" is never evaluated |
| |
| def test2(self) -> bool: |
| return not self.property_decorator_missing and self.missing() # E: Function "property_decorator_missing" could always be true in boolean context \ |
| # E: Right operand of "and" is never evaluated |
| |
| def property_decorator_missing(self) -> bool: |
| return True |
| [builtins fixtures/bool.pyi] |
| |
| [case testUnreachableFlagWithGenerics] |
| # flags: --warn-unreachable |
| from typing import TypeVar, Generic |
| |
| T1 = TypeVar('T1', bound=int) |
| T2 = TypeVar('T2', int, str) |
| T3 = TypeVar('T3', None, str) |
| |
| def test1(x: T1) -> T1: |
| if isinstance(x, int): |
| reveal_type(x) # N: Revealed type is "T1`-1" |
| else: |
| reveal_type(x) # E: Statement is unreachable |
| return x |
| |
| def test2(x: T2) -> T2: |
| if isinstance(x, int): |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.str" |
| |
| if False: |
| # This is unreachable, but we don't report an error, unfortunately. |
| # The presence of the TypeVar with values unfortunately currently shuts |
| # down type-checking for this entire function. |
| # TODO: Find a way of removing this limitation |
| reveal_type(x) |
| |
| return x |
| |
| class Test3(Generic[T2]): |
| x: T2 |
| |
| def func(self) -> None: |
| if isinstance(self.x, int): |
| reveal_type(self.x) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(self.x) # N: Revealed type is "builtins.str" |
| |
| if False: |
| # Same issue as above |
| reveal_type(self.x) |
| |
| |
| class Test4(Generic[T3]): |
| def __init__(self, x: T3): |
| # https://github.com/python/mypy/issues/9456 |
| # On TypeVars with value restrictions, we currently have no way |
| # of checking a statement for all the type expansions. |
| # Thus unreachable warnings are disabled |
| if x and False: |
| pass |
| # This test should fail after this limitation is removed. |
| if False and x: |
| pass |
| |
| [builtins fixtures/isinstancelist.pyi] |
| |
| [case testUnreachableFlagContextManagersNoSuppress] |
| # flags: --warn-unreachable |
| from contextlib import contextmanager |
| from typing import Optional, Iterator, Any |
| from typing_extensions import Literal |
| class DoesNotSuppress1: |
| def __enter__(self) -> int: ... |
| def __exit__(self, exctype: object, excvalue: object, traceback: object) -> Optional[bool]: ... |
| |
| class DoesNotSuppress2: |
| def __enter__(self) -> int: ... |
| def __exit__(self, exctype: object, excvalue: object, traceback: object) -> Literal[False]: ... |
| |
| class DoesNotSuppress3: |
| def __enter__(self) -> int: ... |
| def __exit__(self, exctype: object, excvalue: object, traceback: object) -> Any: ... |
| |
| class DoesNotSuppress4: |
| def __enter__(self) -> int: ... |
| def __exit__(self, exctype: object, excvalue: object, traceback: object) -> None: ... |
| |
| @contextmanager |
| def simple() -> Iterator[int]: |
| yield 3 |
| |
| def cond() -> bool: ... |
| |
| def noop() -> None: ... |
| |
| def f_no_suppress_1a() -> int: |
| with DoesNotSuppress1(): |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| def f_no_suppress_1b() -> int: |
| with DoesNotSuppress1(): |
| if cond(): |
| return 3 |
| else: |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| def f_no_suppress_2() -> int: |
| with DoesNotSuppress2(): |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| def f_no_suppress_3() -> int: |
| with DoesNotSuppress3(): |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| def f_no_suppress_4() -> int: |
| with DoesNotSuppress4(): |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| def f_no_suppress_5() -> int: |
| with simple(): |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| [typing fixtures/typing-medium.pyi] |
| [builtins fixtures/tuple.pyi] |
| |
| [case testUnreachableFlagContextManagersSuppressed] |
| # flags: --warn-unreachable |
| from contextlib import contextmanager |
| from typing import Optional, Iterator, Any |
| from typing_extensions import Literal |
| |
| class DoesNotSuppress: |
| def __enter__(self) -> int: ... |
| def __exit__(self, exctype: object, excvalue: object, traceback: object) -> Optional[bool]: ... |
| |
| class Suppresses1: |
| def __enter__(self) -> int: ... |
| def __exit__(self, exctype: object, excvalue: object, traceback: object) -> bool: ... |
| |
| class Suppresses2: |
| def __enter__(self) -> int: ... |
| def __exit__(self, exctype: object, excvalue: object, traceback: object) -> Literal[True]: ... |
| |
| def cond() -> bool: ... |
| |
| def noop() -> None: ... |
| |
| def f_suppress_1a() -> int: # E: Missing return statement |
| with Suppresses1(): |
| return 3 |
| noop() |
| |
| def f_suppress_1b() -> int: # E: Missing return statement |
| with Suppresses1(): |
| if cond(): |
| return 3 |
| else: |
| return 3 |
| noop() |
| |
| def f_suppress_2() -> int: # E: Missing return statement |
| with Suppresses2(): |
| return 3 |
| noop() |
| |
| def f_mix() -> int: # E: Missing return statement |
| with DoesNotSuppress(), Suppresses1(), DoesNotSuppress(): |
| return 3 |
| noop() |
| [typing fixtures/typing-medium.pyi] |
| [builtins fixtures/tuple.pyi] |
| |
| [case testUnreachableFlagContextManagersSuppressedNoStrictOptional] |
| # flags: --warn-unreachable --no-strict-optional |
| from contextlib import contextmanager |
| from typing import Optional, Iterator, Any |
| from typing_extensions import Literal |
| |
| class DoesNotSuppress1: |
| def __enter__(self) -> int: ... |
| def __exit__(self, exctype: object, excvalue: object, traceback: object) -> Optional[bool]: ... |
| |
| # Normally, this should suppress. But when strict-optional mode is disabled, we can't |
| # necessarily distinguish between bool and Optional[bool]. So we default to assuming |
| # no suppression, since that's what most context managers will do. |
| class DoesNotSuppress2: |
| def __enter__(self) -> int: ... |
| def __exit__(self, exctype: object, excvalue: object, traceback: object) -> bool: ... |
| |
| # But if we see Literal[True], it's pretty unlikely the return type is actually meant to |
| # be 'Optional[Literal[True]]'. So, we optimistically assume this is meant to be suppressing. |
| class Suppresses: |
| def __enter__(self) -> int: ... |
| def __exit__(self, exctype: object, excvalue: object, traceback: object) -> Literal[True]: ... |
| |
| def noop() -> None: ... |
| |
| def f_no_suppress_1() -> int: |
| with DoesNotSuppress1(): |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| def f_no_suppress_2() -> int: |
| with DoesNotSuppress1(): |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| def f_suppress() -> int: # E: Missing return statement |
| with Suppresses(): |
| return 3 |
| noop() |
| [typing fixtures/typing-medium.pyi] |
| [builtins fixtures/tuple.pyi] |
| |
| [case testUnreachableFlagContextAsyncManagersNoSuppress] |
| # flags: --warn-unreachable --python-version 3.7 |
| from contextlib import asynccontextmanager |
| from typing import Optional, AsyncIterator, Any |
| from typing_extensions import Literal |
| |
| class DoesNotSuppress1: |
| async def __aenter__(self) -> int: ... |
| async def __aexit__(self, exctype: object, excvalue: object, traceback: object) -> Optional[bool]: ... |
| |
| class DoesNotSuppress2: |
| async def __aenter__(self) -> int: ... |
| async def __aexit__(self, exctype: object, excvalue: object, traceback: object) -> Literal[False]: ... |
| |
| class DoesNotSuppress3: |
| async def __aenter__(self) -> int: ... |
| async def __aexit__(self, exctype: object, excvalue: object, traceback: object) -> Any: ... |
| |
| class DoesNotSuppress4: |
| async def __aenter__(self) -> int: ... |
| async def __aexit__(self, exctype: object, excvalue: object, traceback: object) -> None: ... |
| |
| @asynccontextmanager |
| async def simple() -> AsyncIterator[int]: |
| yield 3 |
| |
| def cond() -> bool: ... |
| |
| def noop() -> None: ... |
| |
| async def f_no_suppress_1a() -> int: |
| async with DoesNotSuppress1(): |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| async def f_no_suppress_1b() -> int: |
| async with DoesNotSuppress1(): |
| if cond(): |
| return 3 |
| else: |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| async def f_no_suppress_2() -> int: |
| async with DoesNotSuppress2(): |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| async def f_no_suppress_3() -> int: |
| async with DoesNotSuppress3(): |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| async def f_no_suppress_4() -> int: |
| async with DoesNotSuppress4(): |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| async def f_no_suppress_5() -> int: |
| async with simple(): |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| [typing fixtures/typing-full.pyi] |
| [builtins fixtures/tuple.pyi] |
| |
| [case testUnreachableFlagContextAsyncManagersSuppressed] |
| # flags: --warn-unreachable --python-version 3.7 |
| from contextlib import asynccontextmanager |
| from typing import Optional, AsyncIterator, Any |
| from typing_extensions import Literal |
| |
| class DoesNotSuppress: |
| async def __aenter__(self) -> int: ... |
| async def __aexit__(self, exctype: object, excvalue: object, traceback: object) -> Optional[bool]: ... |
| |
| class Suppresses1: |
| async def __aenter__(self) -> int: ... |
| async def __aexit__(self, exctype: object, excvalue: object, traceback: object) -> bool: ... |
| |
| class Suppresses2: |
| async def __aenter__(self) -> int: ... |
| async def __aexit__(self, exctype: object, excvalue: object, traceback: object) -> Literal[True]: ... |
| |
| def cond() -> bool: ... |
| |
| def noop() -> None: ... |
| |
| async def f_suppress_1() -> int: # E: Missing return statement |
| async with Suppresses1(): |
| return 3 |
| noop() |
| |
| async def f_suppress_2() -> int: # E: Missing return statement |
| async with Suppresses1(): |
| if cond(): |
| return 3 |
| else: |
| return 3 |
| noop() |
| |
| async def f_suppress_3() -> int: # E: Missing return statement |
| async with Suppresses2(): |
| return 3 |
| noop() |
| |
| async def f_mix() -> int: # E: Missing return statement |
| async with DoesNotSuppress(), Suppresses1(), DoesNotSuppress(): |
| return 3 |
| noop() |
| [typing fixtures/typing-full.pyi] |
| [builtins fixtures/tuple.pyi] |
| |
| [case testUnreachableFlagContextAsyncManagersAbnormal] |
| # flags: --warn-unreachable --python-version 3.7 |
| from contextlib import asynccontextmanager |
| from typing import Optional, AsyncIterator, Any |
| from typing_extensions import Literal |
| |
| class RegularManager: |
| def __enter__(self) -> int: ... |
| def __exit__(self, exctype: object, excvalue: object, traceback: object) -> bool: ... |
| |
| class AsyncManager: |
| async def __aenter__(self) -> int: ... |
| async def __aexit__(self, exctype: object, excvalue: object, traceback: object) -> bool: ... |
| |
| def noop() -> None: ... |
| |
| async def f_bad_1() -> int: |
| async with RegularManager(): # E: "RegularManager" has no attribute "__aenter__"; maybe "__enter__"? \ |
| # E: "RegularManager" has no attribute "__aexit__"; maybe "__exit__"? |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| def f_bad_2() -> int: |
| with AsyncManager(): # E: "AsyncManager" has no attribute "__enter__"; maybe "__aenter__"? \ |
| # E: "AsyncManager" has no attribute "__exit__"; maybe "__aexit__"? |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| # TODO: We should consider reporting an error when the user tries using |
| # context manager with malformed signatures instead of silently continuing. |
| |
| class RegularManagerMalformedSignature: |
| def __enter__(self) -> int: ... |
| def __exit__(self, exctype: object, excvalue: object, traceback: object) -> object: ... |
| |
| class AsyncManagerMalformedSignature: |
| async def __aenter__(self) -> int: ... |
| async def __aexit__(self, exctype: object, excvalue: object, traceback: object) -> object: ... |
| |
| def f_malformed_1() -> int: |
| with RegularManagerMalformedSignature(): |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| async def f_malformed_2() -> int: |
| async with AsyncManagerMalformedSignature(): |
| return 3 |
| noop() # E: Statement is unreachable |
| |
| [typing fixtures/typing-full.pyi] |
| [builtins fixtures/tuple.pyi] |
| |
| [case testUnreachableUntypedFunction] |
| # flags: --warn-unreachable |
| |
| def test_untyped_fn(obj): |
| assert obj.prop is True |
| |
| obj.update(prop=False) |
| obj.reload() |
| |
| assert obj.prop is False |
| reveal_type(obj.prop) |
| |
| def test_typed_fn(obj) -> None: |
| assert obj.prop is True |
| |
| obj.update(prop=False) |
| obj.reload() |
| |
| assert obj.prop is False |
| reveal_type(obj.prop) # E: Statement is unreachable |
| |
| [case testUnreachableCheckedUntypedFunction] |
| # flags: --warn-unreachable --check-untyped-defs |
| |
| def test_untyped_fn(obj): |
| assert obj.prop is True |
| |
| obj.update(prop=False) |
| obj.reload() |
| |
| assert obj.prop is False |
| reveal_type(obj.prop) # E: Statement is unreachable |
| |
| [case testConditionalTypeVarException] |
| # every part of this test case was necessary to trigger the crash |
| import sys |
| from typing import TypeVar |
| |
| T = TypeVar("T", int, str) |
| |
| def f(t: T) -> None: |
| if sys.platform == "lol": |
| try: |
| pass |
| except BaseException as e: |
| pass |
| [builtins fixtures/dict.pyi] |
| |
| |
| [case testUnreachableLiteral] |
| # flags: --warn-unreachable |
| from typing_extensions import Literal |
| |
| def nope() -> Literal[False]: ... |
| |
| def f() -> None: |
| if nope(): |
| x = 1 # E: Statement is unreachable |
| [builtins fixtures/dict.pyi] |
| |
| [case testUnreachableLiteralFrom__bool__] |
| # flags: --warn-unreachable |
| from typing_extensions import Literal |
| |
| class Truth: |
| def __bool__(self) -> Literal[True]: ... |
| |
| class Lie: |
| def __bool__(self) -> Literal[False]: ... |
| |
| class Maybe: |
| def __bool__(self) -> Literal[True | False]: ... |
| |
| t = Truth() |
| if t: |
| x = 1 |
| else: |
| x = 2 # E: Statement is unreachable |
| |
| if Lie(): |
| x = 3 # E: Statement is unreachable |
| |
| if Maybe(): |
| x = 4 |
| |
| |
| def foo() -> bool: ... |
| |
| y = Truth() or foo() # E: Right operand of "or" is never evaluated |
| z = Lie() and foo() # E: Right operand of "and" is never evaluated |
| [builtins fixtures/dict.pyi] |
| |
| [case testUnreachableModuleBody1] |
| # flags: --warn-unreachable |
| from typing import NoReturn |
| def foo() -> NoReturn: |
| raise Exception("foo") |
| foo() |
| x = 1 # E: Statement is unreachable |
| [builtins fixtures/exception.pyi] |
| |
| [case testUnreachableModuleBody2] |
| # flags: --warn-unreachable |
| raise Exception |
| x = 1 # E: Statement is unreachable |
| [builtins fixtures/exception.pyi] |
| |
| [case testUnreachableNoReturnBinaryOps] |
| # flags: --warn-unreachable |
| from typing import NoReturn |
| |
| a: NoReturn |
| a and 1 # E: Right operand of "and" is never evaluated |
| a or 1 # E: Right operand of "or" is never evaluated |
| a or a # E: Right operand of "or" is never evaluated |
| 1 and a and 1 # E: Right operand of "and" is never evaluated |
| a and a # E: Right operand of "and" is never evaluated |
| [builtins fixtures/exception.pyi] |
| |
| [case testUnreachableFlagWithTerminalBranchInDeferredNode] |
| # flags: --warn-unreachable |
| from typing import NoReturn |
| |
| def assert_never(x: NoReturn) -> NoReturn: ... |
| |
| def force_forward_ref() -> int: |
| return 4 |
| |
| def f(value: None) -> None: |
| x |
| if value is not None: |
| assert_never(value) |
| |
| x = force_forward_ref() |
| [builtins fixtures/exception.pyi] |
| |
| [case testSetitemNoReturn] |
| # flags: --warn-unreachable |
| from typing import NoReturn |
| class Foo: |
| def __setitem__(self, key: str, value: str) -> NoReturn: |
| raise Exception |
| Foo()['a'] = 'a' |
| x = 0 # E: Statement is unreachable |
| [builtins fixtures/exception.pyi] |
| |
| [case TestNoImplicNoReturnFromError] |
| # flags: --warn-unreachable |
| from typing import TypeVar |
| |
| T = TypeVar("T") |
| class Foo: |
| def __setitem__(self, key: str, value: str) -> T: # E: A function returning TypeVar should receive at least one argument containing the same TypeVar |
| raise Exception |
| |
| def f() -> None: |
| Foo()['a'] = 'a' |
| x = 0 # This should not be reported as unreachable |
| [builtins fixtures/exception.pyi] |
| |
| [case testIntentionallyEmptyGeneratorFunction] |
| # flags: --warn-unreachable |
| from typing import Generator |
| |
| def f() -> Generator[None, None, None]: |
| return |
| yield |
| |
| [case testIntentionallyEmptyGeneratorFunction_None] |
| # flags: --warn-unreachable |
| from typing import Generator |
| |
| def f() -> Generator[None, None, None]: |
| return None |
| yield None |