-- Test cases for generating fine-grained dependencies for classes.
--
-- The dependencies are used for fined-grained incremental checking.
--
-- See the comment at the top of deps.test for more documentation.

-- TODO: Move class related test cases from deps.test to here

[case testNamedTuple]
from typing import NamedTuple, Any
from a import A
N = NamedTuple('N', [('a', 'A')])

def f(a: Any) -> None:
    n = N(a)
    n.a
[file a.py]
class A: pass
[out]
<m.N.__init__> -> m.f
<m.N.__new__> -> m.f
<m.N.a> -> m.f
<m.N> -> m.f
<a.A> -> <m.N.a>, <m.N>, m

[case testNamedTuple2]
from typing import NamedTuple, Any, Tuple
from a import A, B
N = NamedTuple('N', [('a', 'Tuple[A, B]')])

def f(a: Any) -> None:
    n = N(a)
    n.a
[file a.py]
class A: pass
class B: pass
[out]
<m.N.__init__> -> m.f
<m.N.__new__> -> m.f
<m.N.a> -> m.f
<m.N> -> m.f
<a.A> -> <m.N.a>, <m.N>, m
<a.B> -> <m.N.a>, <m.N>, m

[case testNamedTuple3]
from typing import NamedTuple
N = NamedTuple('N', [('x', int)])
x = N(1)
M = NamedTuple('M', [('z', 'N')])
y = M(x)
[out]
<m.M.__init__> -> m
<m.M.__new__> -> m
<m.M> -> <m.y>, m
<m.N.__init__> -> m
<m.N.__new__> -> m
<m.N> -> <m.M.z>, <m.M>, <m.x>, <m.y>, m
<m.x> -> m
<m.y> -> m

[case testNamedTuple4]
from typing import NamedTuple, Any
from a import A
class N(NamedTuple):
    a: A

def f(a: Any) -> None:
    n = N(a)
    n.a
[file a.py]
class A: pass
[out]
<m.N.__init__> -> m.f
<m.N.__new__> -> m.f
<m.N.a> -> m.f
<m.N> -> m.N, m.f
<a.A> -> <m.N.a>, <m.N>, m, m.N

[case testIfFalseInClassBody]
class A:
    if False:
        x = None  # type: str
        x.foo()
[builtins fixtures/bool.pyi]
[out]
<m.A> -> m.A

[case testAlwaysFalseIsinstanceInClassBody]
class A:
    x: int
    if isinstance(x, str):
        y: str = None
        y.foo()
[builtins fixtures/isinstance.pyi]
[out]
<m.A> -> m.A

[case testDoubleAttributeInitializationToNone]
class C:
    def __init__(self) -> None:
        self.x = None
        self.x = None
[out]
<m.C.x> -> m.C.__init__
<m.C> -> m.C

[case testClassNestedWithinFunction]
class C: pass

def f() -> None:
    class S1(C): pass

class D:
    def g(self) -> None:
        class S2(C): pass
[out]
-- TODO: Is it okay to have targets like m.S1@4.__init__?
<m.C.(abstract)> -> <m.S1@4.__init__>, <m.S2@8.__init__>, m.D.g, m.f
<m.C.__init__> -> <m.S1@4.__init__>, <m.S2@8.__init__>
<m.C.__new__> -> <m.S1@4.__new__>, <m.S2@8.__new__>
<m.C> -> m.C, m.D.g, m.f
<m.D.g> -> m.D.g
<m.D> -> m.D
<m.f> -> m.f

[case testClassSuper]
class C:
    def __init__(self, x: int) -> None: pass
    def foo(self) -> None: pass

class D(C):
    def __init__(self, x: int) -> None:
        super().__init__(x)
        super().foo()
[out]
<m.C.(abstract)> -> <m.D.__init__>, m
<m.C.__init__> -> <m.D.__init__>, m.D.__init__
<m.C.__new__> -> <m.D.__new__>
<m.C.foo> -> <m.D.foo>
<m.C> -> m, m.C, m.D
<m.D.__init__> -> m.D.__init__
<m.D.foo> -> m.D.__init__
<m.D> -> m.D

[case testClassMissingInit]
class C:
    def __init__(self, x: int) -> None: pass

class D(C):
    pass

def foo() -> None:
    D(6)
[out]
<m.C.(abstract)> -> <m.D.__init__>, m
<m.C.__init__> -> <m.D.__init__>
<m.C.__new__> -> <m.D.__new__>
<m.C> -> m, m.C, m.D
<m.D.__init__> -> m.foo
<m.D.__new__> -> m.foo
<m.D> -> m.D, m.foo

[case testClassBasedEnum]
from enum import Enum
from m import B

class A(Enum):
    X = B()

def f(a: A) -> None:
    pass
def g() -> None:
    A.X
[file m.py]
class B: pass
[out]
<m.A.X> -> m.g
-- The __init__ is superfluous but benign
<m.A.__init__> -> m.g
<m.A.__new__> -> m.g
<m.A> -> <m.f>, m.A, m.f, m.g
<m.B.__init__> -> m
<m.B.__new__> -> m
-- The <m.A.X> dependecy target is superfluous but benign
<m.B> -> <m.A.X>, m
