-- Tests for strict Optional behavior

[case testImplicitNoneType]
x = None
x()  # E: "None" not callable

[case testImplicitNoneTypeInNestedFunction]
def f() -> None:
    def g() -> None:
        x = None
        x()  # E: "None" not callable

[case testExplicitNoneType]
x = None  # type: None
x()  # E: "None" not callable

[case testNoneMemberOfOptional]
from typing import Optional
x = None  # type: Optional[int]

[case testTypeMemberOfOptional]
from typing import Optional
x = 0  # type: Optional[int]

[case testNoneNotMemberOfType]
x = None  # type: int
[out]
main:1: error: Incompatible types in assignment (expression has type "None", variable has type "int")

[case testTypeNotMemberOfNone]
x = 0  # type: None
[out]
main:1: error: Incompatible types in assignment (expression has type "int", variable has type "None")

[case testOptionalNotMemberOfType]
from typing import Optional
def f(a: int) -> None: pass
x = None  # type:  Optional[int]
f(x)  # E: Argument 1 to "f" has incompatible type "Optional[int]"; expected "int"

[case testIsinstanceCases]
from typing import Optional
x = None  # type:  Optional[int]
if isinstance(x, int):
  reveal_type(x)  # E: Revealed type is 'builtins.int'
else:
  reveal_type(x)  # E: Revealed type is 'None'
[builtins fixtures/isinstance.pyi]

[case testIfCases]
from typing import Optional
x = None  # type:  Optional[int]
if x:
  reveal_type(x)  # E: Revealed type is 'builtins.int'
else:
  reveal_type(x)  # E: Revealed type is 'Union[builtins.int, None]'
[builtins fixtures/bool.pyi]

[case testIfNotCases]
from typing import Optional
x = None  # type:  Optional[int]
if not x:
  reveal_type(x)  # E: Revealed type is 'Union[builtins.int, None]'
else:
  reveal_type(x)  # E: Revealed type is 'builtins.int'
[builtins fixtures/bool.pyi]

[case testIsNotNoneCases]
from typing import Optional
x = None  # type:  Optional[int]
if x is not None:
  reveal_type(x)  # E: Revealed type is 'builtins.int'
else:
  reveal_type(x)  # E: Revealed type is 'None'
[builtins fixtures/bool.pyi]

[case testIsNoneCases]
from typing import Optional
x = None  # type:  Optional[int]
if x is None:
  reveal_type(x)  # E: Revealed type is 'None'
else:
  reveal_type(x)  # E: Revealed type is 'builtins.int'
reveal_type(x)  # E: Revealed type is 'Union[builtins.int, None]'
[builtins fixtures/bool.pyi]

[case testAnyCanBeNone]
from typing import Optional, Any
x = None  # type:  Any
if x is None:
  reveal_type(x)  # E: Revealed type is 'None'
else:
  reveal_type(x)  # E: Revealed type is 'Any'
[builtins fixtures/bool.pyi]

[case testOrCases]
from typing import Optional
x = None  # type: Optional[str]
y1 = x or 'a'
reveal_type(y1)  # E: Revealed type is 'builtins.str'
y2 = x or 1
reveal_type(y2)  # E: Revealed type is 'Union[builtins.str, builtins.int]'
z1 = 'a' or x
reveal_type(z1)  # E: Revealed type is 'Union[builtins.str, None]'
z2 = int() or x
reveal_type(z2)  # E: Revealed type is 'Union[builtins.int, builtins.str, None]'

[case testAndCases]
from typing import Optional
x = None  # type: Optional[str]
y1 = x and 'b'
reveal_type(y1)  # E: Revealed type is 'Union[builtins.str, None]'
y2 = x and 1  # x could be '', so...
reveal_type(y2)  # E: Revealed type is 'Union[builtins.str, None, builtins.int]'
z1 = 'b' and x
reveal_type(z1)  # E: Revealed type is 'Union[builtins.str, None]'
z2 = int() and x
reveal_type(z2)  # E: Revealed type is 'Union[builtins.int, builtins.str, None]'

[case testLambdaReturningNone]
f = lambda: None
x = f()  # E: Function does not return a value

[case testNoneArgumentType]
def f(x: None) -> None: pass
f(None)

[case testInferOptionalFromDefaultNone]
def f(x: int = None) -> None:
  x + 1  # E: Unsupported left operand type for + ("None") \
         # N: Left operand is of type "Optional[int]"
f(None)
[out]

[case testNoInferOptionalFromDefaultNone]
# flags: --no-implicit-optional
def f(x: int = None) -> None:  # E: Incompatible default for argument "x" (default has type "None", argument has type "int")
  pass
[out]

[case testInferOptionalFromDefaultNoneComment]
def f(x=None):
  # type: (int) -> None
  x + 1  # E: Unsupported left operand type for + ("None") \
         # N: Left operand is of type "Optional[int]"
f(None)
[out]

[case testNoInferOptionalFromDefaultNoneComment]
# flags: --no-implicit-optional
def f(x=None):  # E: Incompatible default for argument "x" (default has type "None", argument has type "int")
  # type: (int) -> None
  pass
[out]

[case testInferOptionalType]
x = None
if bool():
  # scope limit assignment
  x = 1
  # in scope of the assignment, x is an int
  reveal_type(x)  # E: Revealed type is 'builtins.int'
# out of scope of the assignment, it's an Optional[int]
reveal_type(x)  # E: Revealed type is 'Union[builtins.int, None]'
[builtins fixtures/bool.pyi]

[case testInferOptionalTypeLocallyBound]
x = None
x = 1
reveal_type(x)  # E: Revealed type is 'builtins.int'

[case testInferOptionalAnyType]
from typing import Any
x = None
a = None  # type: Any
if bool():
  x = a
  reveal_type(x)  # E: Revealed type is 'Any'
reveal_type(x)  # E: Revealed type is 'Union[Any, None]'
[builtins fixtures/bool.pyi]

[case testInferOptionalTypeFromOptional]
from typing import Optional
y = None  # type: Optional[int]
x = None
x = y
reveal_type(x)  # E: Revealed type is 'Union[builtins.int, None]'

[case testInferOptionalListType]
x = [None]
x.append(1)  # E: Argument 1 to "append" of "list" has incompatible type "int"; expected "None"
[builtins fixtures/list.pyi]

[case testInferNonOptionalListType]
x = []
x.append(1)
x()  # E: "List[int]" not callable
[builtins fixtures/list.pyi]

[case testInferOptionalDictKeyValueTypes]
x = {None: None}
x["bar"] = 1
[builtins fixtures/dict.pyi]
[out]
main:2: error: Invalid index type "str" for "Dict[None, None]"; expected type "None"
main:2: error: Incompatible types in assignment (expression has type "int", target has type "None")

[case testInferNonOptionalDictType]
x = {}
x["bar"] = 1
x()  # E: "Dict[str, int]" not callable
[builtins fixtures/dict.pyi]

[case testNoneClassVariable]
from typing import Optional
class C:
    x = None  # type: int
    def __init__(self) -> None:
        self.x = 0

[case testNoneClassVariableInInit]
from typing import Optional
class C:
    x = None  # type: int
    def __init__(self) -> None:
        self.x = None  # E: Incompatible types in assignment (expression has type "None", variable has type "int")
[out]

[case testMultipleAssignmentNoneClassVariableInInit]
from typing import Optional
class C:
    x, y = None, None  # type: int, str
    def __init__(self) -> None:
        self.x = None  # E: Incompatible types in assignment (expression has type "None", variable has type "int")
        self.y = None  # E: Incompatible types in assignment (expression has type "None", variable has type "str")
[out]

[case testOverloadWithNone]
from foo import *
[file foo.pyi]
from typing import overload
@overload
def f(x: None) -> str: pass
@overload
def f(x: int) -> int: pass
reveal_type(f(None))  # E: Revealed type is 'builtins.str'
reveal_type(f(0))  # E: Revealed type is 'builtins.int'

[case testOptionalTypeOrTypePlain]
from typing import Optional
def f(a: Optional[int]) -> int:
    return a or 0
[out]

[case testOptionalTypeOrTypeTypeVar]
from typing import Optional, TypeVar
T = TypeVar('T')
def f(a: Optional[T], b: T) -> T:
    return a or b
[out]

[case testOptionalTypeOrTypeBothOptional]
from typing import Optional
def f(a: Optional[int], b: Optional[int]) -> None:
    reveal_type(a or b)
def g(a: int, b: Optional[int]) -> None:
    reveal_type(a or b)
[out]
main:3: error: Revealed type is 'Union[builtins.int, None]'
main:5: error: Revealed type is 'Union[builtins.int, None]'

[case testOptionalTypeOrTypeComplexUnion]
from typing import Union
def f(a: Union[int, str, None]) -> None:
    reveal_type(a or 'default')
[out]
main:3: error: Revealed type is 'Union[builtins.int, builtins.str]'

[case testOptionalTypeOrTypeNoTriggerPlain]
from typing import Optional
def f(a: Optional[int], b: int) -> int:
    return b or a
[out]
main:3: error: Incompatible return value type (got "Optional[int]", expected "int")

[case testOptionalTypeOrTypeNoTriggerTypeVar]
from typing import Optional, TypeVar
T = TypeVar('T')
def f(a: Optional[T], b: T) -> T:
    return b or a
[out]
main:4: error: Incompatible return value type (got "Optional[T]", expected "T")

[case testNoneOrStringIsString]
def f() -> str:
    a = None
    b = ''
    return a or b
[out]

[case testNoneOrTypeVarIsTypeVar]
from typing import TypeVar
T = TypeVar('T')
def f(b: T) -> T:
    a = None
    return a or b
[out]

[case testYieldNothingInFunctionReturningGenerator]
from typing import Generator
def f() -> Generator[None, None, None]:
    yield
[out]

[case testNoneAndStringIsNone]
a = None
b = "foo"
reveal_type(a and b)  # E: Revealed type is 'None'

[case testNoneMatchesObjectInOverload]
import a
a.f(None)

[file a.pyi]
from typing import overload
@overload
def f() -> None: ...
@overload
def f(o: object) -> None: ...

[case testGenericSubclassReturningNone]
from typing import Generic, TypeVar

T = TypeVar('T')

class Base(Generic[T]):
  def f(self) -> T:
    pass

class SubNone(Base[None]):
  def f(self) -> None:
    pass

class SubInt(Base[int]):
  def f(self) -> int:
    return 1

[case testUseOfNoneReturningFunction]
from typing import Optional
def f() -> None:
    pass

def g(x: Optional[int]) -> int:
  pass

x = f()  # E: "f" does not return a value
f() + 1  # E: "f" does not return a value
g(f())  # E: "f" does not return a value

[case testEmptyReturn]
def f() -> None:
    return

[case testReturnNone]
def f() -> None:
    return None

[case testNoneCallable]
from typing import Callable
def f() -> None: pass
x = f  # type: Callable[[], None]

[case testOptionalCallable]
from typing import Callable, Optional
T = Optional[Callable[..., None]]

[case testAnyTypeInPartialTypeList]
# flags: --check-untyped-defs
def f(): ...

def lookup_field(name, obj):
    try:
        pass
    except:
        attr = f()
    else:
        attr = None

[case testTernaryWithNone]
reveal_type(None if bool() else 0)  # E: Revealed type is 'Union[builtins.int, None]'
[builtins fixtures/bool.pyi]

[case testListWithNone]
reveal_type([0, None, 0])    # E: Revealed type is 'builtins.list[Union[builtins.int, None]]'
[builtins fixtures/list.pyi]

[case testOptionalWhitelistSuppressesOptionalErrors]
# flags: --strict-optional-whitelist
import a
import b
[file a.py]
from typing import Optional
x = None  # type: Optional[str]
x + "foo"

[file b.py]
from typing import Optional
x = None  # type: Optional[int]
x + 1

[case testOptionalWhitelistPermitsOtherErrors]
# flags: --strict-optional-whitelist
import a
import b
[file a.py]
from typing import Optional
x = None  # type: Optional[str]
x + "foo"

[file b.py]
from typing import Optional
x = None  # type: Optional[int]
x + 1
1 + "foo"
[out]
tmp/b.py:4: error: Unsupported operand types for + ("int" and "str")

[case testOptionalWhitelistPermitsWhitelistedFiles]
# flags: --strict-optional-whitelist **/a.py
import a
import b
[file a.py]
from typing import Optional
x = None  # type: Optional[str]
x + "foo"

[file b.py]
from typing import Optional
x = None  # type: Optional[int]
x + 1
[out]
tmp/a.py:3: error: Unsupported left operand type for + ("None")
tmp/a.py:3: note: Left operand is of type "Optional[str]"

[case testNoneContextInference]
from typing import Dict, List
def f() -> List[None]:
    return []
def g() -> Dict[None, None]:
    return {}
[builtins fixtures/dict.pyi]

[case testRaiseFromNone]
raise BaseException from None
[builtins fixtures/exception.pyi]

[case testOptionalNonPartialTypeWithNone]
from typing import Generator
def f() -> Generator[str, None, None]: pass
x = f()
reveal_type(x)  # E: Revealed type is 'typing.Generator[builtins.str, None, None]'
l = [f()]
reveal_type(l)  # E: Revealed type is 'builtins.list[typing.Generator*[builtins.str, None, None]]'
[builtins fixtures/list.pyi]

[case testNoneListTernary]
x = [None] if "" else [1]  # E: List item 0 has incompatible type "int"; expected "None"
[builtins fixtures/list.pyi]

[case testListIncompatibleErrorMessage]
from typing import List, Callable

def foo(l: List[Callable[[], str]]) -> None: pass
def f() -> int:
    return 42

foo([f])  # E: List item 0 has incompatible type "Callable[[], int]"; expected "Callable[[], str]"
[builtins fixtures/list.pyi]

[case testInferEqualsNotOptional]
from typing import Optional
x = ''  # type: Optional[str]
if x == '<string>':
    reveal_type(x)  # E: Revealed type is 'builtins.str'
else:
    reveal_type(x)  # E: Revealed type is 'Union[builtins.str, None]'
[builtins fixtures/ops.pyi]

[case testInferEqualsNotOptionalWithUnion]
from typing import Union
x = ''  # type: Union[str, int, None]
if x == '<string>':
    reveal_type(x)  # E: Revealed type is 'Union[builtins.str, builtins.int]'
else:
    reveal_type(x)  # E: Revealed type is 'Union[builtins.str, builtins.int, None]'
[builtins fixtures/ops.pyi]

[case testInferEqualsNotOptionalWithOverlap]
from typing import Union
x = ''  # type: Union[str, int, None]
if x == object():
    reveal_type(x)  # E: Revealed type is 'Union[builtins.str, builtins.int]'
else:
    reveal_type(x)  # E: Revealed type is 'Union[builtins.str, builtins.int, None]'
[builtins fixtures/ops.pyi]

[case testInferEqualsStillOptionalWithNoOverlap]
from typing import Optional
x = ''  # type: Optional[str]
if x == 0:
    reveal_type(x)  # E: Revealed type is 'Union[builtins.str, None]'
else:
    reveal_type(x)  # E: Revealed type is 'Union[builtins.str, None]'
[builtins fixtures/ops.pyi]

[case testInferEqualsStillOptionalWithBothOptional]
from typing import Union
x = ''  # type: Union[str, int, None]
y = ''  # type: Union[str, None]
if x == y:
    reveal_type(x)  # E: Revealed type is 'Union[builtins.str, builtins.int, None]'
else:
    reveal_type(x)  # E: Revealed type is 'Union[builtins.str, builtins.int, None]'
[builtins fixtures/ops.pyi]

[case testWarnNoReturnWorksWithStrictOptional]
# flags: --warn-no-return
def f() -> None:
  1 + 1  # no error

def g() -> int:
  1 + 1  #
[out]
main:5: error: Missing return statement

[case testGenericTypeAliasesOptional]
from typing import TypeVar, Generic, Optional
T = TypeVar('T')
class Node(Generic[T]):
    def __init__(self, x: T) -> None:
        self.x = x

ONode = Optional[Node[T]]
def f(x: T) -> ONode[T]:
    if 1 > 0:
        return Node(x)
    else:
        return None

x = None # type: ONode[int]
x = f(1)
x = f('x') # E: Argument 1 to "f" has incompatible type "str"; expected "int"

x.x = 1 # E: Item "None" of "Optional[Node[int]]" has no attribute "x"
if x is not None:
    x.x = 1 # OK here

[builtins fixtures/ops.pyi]

[case testOptionalTypeNarrowedInBooleanStatement]
from typing import Optional

x: Optional[int] = None
x is not None and x + 42
x is not None and x + '42'  # E: Unsupported operand types for + ("int" and "str")
[builtins fixtures/isinstance.pyi]

[case testInvalidBooleanBranchIgnored]
from typing import Optional

x = None
x is not None and x + 42
[builtins fixtures/isinstance.pyi]

[case testOptionalLambdaInference]
from typing import Optional, Callable
f = None # type: Optional[Callable[[int], None]]
f = lambda x: None
f(0)
[builtins fixtures/function.pyi]

[case testDontSimplifyNoneUnionsWithStrictOptional]
from typing import Any, TypeVar, Union
A = None  # type: Any
class C(A): pass
T = TypeVar('T')
S = TypeVar('S')
def u(x: T, y: S) -> Union[S, T]: pass
a = None # type: Any

# Test both orders
reveal_type(u(C(), None))  # E: Revealed type is 'Union[None, __main__.C*]'
reveal_type(u(None, C()))  # E: Revealed type is 'Union[__main__.C*, None]'

reveal_type(u(a, None))  # E: Revealed type is 'Union[None, Any]'
reveal_type(u(None, a))  # E: Revealed type is 'Union[Any, None]'

reveal_type(u(1, None))  # E: Revealed type is 'Union[None, builtins.int*]'
reveal_type(u(None, 1))  # E: Revealed type is 'Union[builtins.int*, None]'

[case testOptionalAndAnyBaseClass]
from typing import Any, Optional
A = None  # type: Any
class C(A):
    pass
x = None  # type: Optional[C]
x.foo()  # E: Item "None" of "Optional[C]" has no attribute "foo"

[case testIsinstanceAndOptionalAndAnyBase]
from typing import Any, Optional

B = None  # type: Any
class A(B): pass

def f(a: Optional[A]):
    reveal_type(a) # E: Revealed type is 'Union[__main__.A, None]'
    if a is not None:
        reveal_type(a) # E: Revealed type is '__main__.A'
    else:
        reveal_type(a) # E: Revealed type is 'None'
    reveal_type(a) # E: Revealed type is 'Union[__main__.A, None]'
[builtins fixtures/isinstance.pyi]

[case testFlattenOptionalUnion]
from typing import Optional, Union

x: Optional[Union[int, str]]
reveal_type(x) # E: Revealed type is 'Union[builtins.int, builtins.str, None]'
y: Optional[Union[int, None]]
reveal_type(y) # E: Revealed type is 'Union[builtins.int, None]'

[case testOverloadWithNoneAndOptional]
from typing import overload, Optional

@overload
def f(x: int) -> str: ...
@overload
def f(x: Optional[int]) -> Optional[str]: ...
def f(x): return x

reveal_type(f(1)) # E: Revealed type is 'builtins.str'
reveal_type(f(None)) # E: Revealed type is 'Union[builtins.str, None]'
x: Optional[int]
reveal_type(f(x)) # E: Revealed type is 'Union[builtins.str, None]'

[case testUnionTruthinessTracking]
from typing import Optional, Any
def test_or_shortcut(value: Optional[Any]) -> None:
    if not value:
        pass
    if not value or value.get('foo') == 'hello':
        pass
[builtins fixtures/bool.pyi]

[case testNarrowingFromObjectToOptional]
from typing import Optional
x: object
y: Optional[int]
x = y
reveal_type(x) # E: Revealed type is 'Union[builtins.int, None]'
[out]

[case testNarrowOptionalOutsideLambda]
from typing import Optional

class A:
    a: int

def f(x: Optional[A]) -> None:
    assert x
    lambda: x.a
[builtins fixtures/isinstancelist.pyi]

[case testNarrowOptionalOutsideLambdaWithDeferred]
from typing import Optional

class A:
    a: int

    def f(self, x: Optional['A']) -> None:
        assert x
        lambda: (self.y, x.a) # E: Cannot determine type of 'y'
        self.y = int()
[builtins fixtures/isinstancelist.pyi]

[case testDeferredAndOptionalInferenceSpecialCase]
def f() -> str:
    y
    x = None
    if int():
        x = ''
    if x is None:
        x = ''
        g(x)
    return x

def g(x: str) -> None: pass

y = int()
[builtins fixtures/bool.pyi]
