-- 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 testConditionalTypeAliasPY3_python2]
import typing
def f(): pass
PY3 = f()
if PY3:
    t = int
    x = object() + 'x'
else:
    t = str
    y = 'x' / 1 # E: "str" has no attribute "__div__"
y
z = '' # 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 testConditionalAssignmentPY2_python2]
import typing
def f(): pass
PY2 = f()
if PY2:
    x = object() + 'x' # E: Unsupported left operand type for + ("object")
else:
    y = 'x' / 1
x

[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
x = 'a'
[out]
tmp/m.py:3: 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 module named 'pow123'
main:6: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help)

[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
[out]
main:3: error: Cannot find module named 'pow123'
main:3: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help)

[case testTypeCheckingConditionalFromImport]
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    import pow123 # E
else:
    import xyz753
[out]
main:3: error: Cannot find module named 'pow123'
main:3: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help)

[case testNegatedTypeCheckingConditional]
import typing
if not typing.TYPE_CHECKING:
    import pow123 # E
else:
    import xyz753
[builtins fixtures/bool.pyi]
[out]
main:5: error: Cannot find module named 'xyz753'
main:5: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help)

[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 module named 'xyz753'
main:4: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help)

[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_python2]
import sys
if sys.version_info[0] >= 3:
    def foo():
        # type: () -> int
        return 0
else:
    def foo():
        # type: () -> str
        return ''
reveal_type(foo())  # E: Revealed type is 'builtins.str'
[builtins_py2 fixtures/ops.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())  # E: Revealed type is 'builtins.int'
[builtins fixtures/ops.pyi]
[out]

[case testSysVersionInfoNegated_python2]
import sys
if not (sys.version_info[0] < 3):
    def foo():
        # type: () -> int
        return 0
else:
    def foo():
        # type: () -> str
        return ''
reveal_type(foo())  # E: Revealed type is 'builtins.str'
[builtins_py2 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())  # E: 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
[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
[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())  # E: 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)  # E: 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)  # E: 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 testSysPlatformInMethodImport2]
import sys
class A:
    def foo(self) -> None:
        if sys.platform == 'fictional':
            # TODO: This is inconsistent with how top-level functions work
            #     (https://github.com/python/mypy/issues/4324)
            import b as a # E: Cannot find module named 'b' \
            # N: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help)
        else:
            import a
        a.x
[file a.py]
x = 1
[builtins fixtures/ops.pyi]
[out]

[case testCustomSysVersionInfo]
# flags: --python-version 3.5
import sys
if sys.version_info == (3, 5):
    x = "foo"
else:
    x = 3
reveal_type(x)  # E: Revealed type is 'builtins.str'
[builtins fixtures/ops.pyi]
[out]

[case testCustomSysVersionInfo2]
# flags: --python-version 3.5
import sys
if sys.version_info == (3, 6):
    x = "foo"
else:
    x = 3
reveal_type(x)  # E: 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)  # E: 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)  # E: 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)  # E: 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)  # E: Revealed type is 'builtins.bool'
reveal_type(b)  # E: Revealed type is 'builtins.str'
reveal_type(c)  # E: Revealed type is 'builtins.str'
reveal_type(d)  # E: Revealed type is 'builtins.bool'
reveal_type(e)  # E: Revealed type is 'builtins.str'
reveal_type(f)  # E: Revealed type is 'builtins.str'
reveal_type(g)  # E: Revealed type is 'builtins.bool'
reveal_type(h)  # E: 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)  # E: Revealed type is 'builtins.int'
if sys.platform == 'linux' and PY2:
    y = 'foo'
else:
    y = 3
reveal_type(y)  # E: 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)  # E: Revealed type is 'builtins.str'
if sys.platform == 'linux' or PY2:
    y = 'foo'
else:
    y = 3
reveal_type(y)  # E: Revealed type is 'builtins.str'
[builtins fixtures/ops.pyi]

[case testConditionalAssertWithoutElse]
import typing

class A: pass
class B(A): pass

x = A()
reveal_type(x)  # E: Revealed type is '__main__.A'

if typing.TYPE_CHECKING:
    assert isinstance(x, B)
    reveal_type(x)  # E: Revealed type is '__main__.B'

reveal_type(x)  # E: Revealed type is '__main__.B'

[builtins fixtures/isinstancelist.pyi]

[case testUnreachableWhenSuperclassIsAny]
# flags: --strict-optional
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)       # E: Revealed type is '__main__.Child'
        if self is None:
            reveal_type(self)
            return None
        reveal_type(self)       # E: Revealed type is '__main__.Child'
        return 3

    def bar(self) -> int:
        self = super(Child, self).something()
        reveal_type(self)       # E: Revealed type is '__main__.Child'
        if self is None:
            reveal_type(self)
            return None
        reveal_type(self)       # E: 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)       # E: Revealed type is '__main__.Child'
        if self is None:
            reveal_type(self)   # E: Revealed type is '__main__.Child'
            return None
        reveal_type(self)       # E: Revealed type is '__main__.Child'
        return 3
[builtins fixtures/isinstance.pyi]
