Repository: python/mypy

Issue:
New semantic analyzer: tests for number of iterations
It would be nice to be able to test things like "the top level of module `m` should be analyzed exactly twice". I've had cases where there were more iterations than expected, and these kinds of errors are hard to detect without tests.


Discussion:

Resolving patch:
diff --git a/mypy/newsemanal/semanal_main.py b/mypy/newsemanal/semanal_main.py
--- a/mypy/newsemanal/semanal_main.py
+++ b/mypy/newsemanal/semanal_main.py
@@ -222,7 +222,11 @@ def process_functions(graph: 'Graph', scc: List[str], patches: Patches) -> None:
         # but _methods_ must be processed in the order they are defined,
         # because some features (most notably partial types) depend on
         # order of definitions on self.
-        targets = sorted(get_all_leaf_targets(tree), key=lambda x: x[1].line)
+        #
+        # There can be multiple generated methods per line. Use target
+        # name as the second sort key to get a repeatable sort order on
+        # Python 3.5, which doesn't preserve dictionary order.
+        targets = sorted(get_all_leaf_targets(tree), key=lambda x: (x[1].line, x[0]))
         for target, node, active_type in targets:
             assert isinstance(node, (FuncDef, OverloadedFuncDef, Decorator))
             process_top_level_function(analyzer,
diff --git a/mypy/newsemanal/semanal_namedtuple.py b/mypy/newsemanal/semanal_namedtuple.py
--- a/mypy/newsemanal/semanal_namedtuple.py
+++ b/mypy/newsemanal/semanal_namedtuple.py
@@ -437,6 +437,7 @@ def add_method(funcname: str,
                 v._fullname = func._fullname
                 func.is_decorated = True
                 dec = Decorator(func, [NameExpr('classmethod')], v)
+                dec.line = line
                 sym = SymbolTableNode(MDEF, dec)
             else:
                 sym = SymbolTableNode(MDEF, func)


Test patch:
diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py
--- a/mypy/test/testcheck.py
+++ b/mypy/test/testcheck.py
@@ -230,7 +230,9 @@ def run_case_once(self, testcase: DataDrivenTestCase,
                 expected = testcase.expected_fine_grained_targets.get(incremental_step + 1)
                 actual = res.manager.processed_targets
                 # Skip the initial builtin cycle.
-                actual = [t for t in actual if not any(t.startswith(mod) for mod in core_modules)]
+                actual = [t for t in actual
+                          if not any(t.startswith(mod)
+                                     for mod in core_modules + ['mypy_extensions'])]
                 if expected is not None:
                     assert_target_equivalence(name, expected, actual)
             if incremental_step > 1:
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -45,6 +45,7 @@ a.foo(B())
 class A:
     def foo(self, x: 'B') -> None: pass
 class B(A): pass
+[targets __main__, __main__, __main__.A.foo]
 [out]
 main:3: error: Argument 1 to "foo" of "A" has incompatible type "A"; expected "B"
 
@@ -189,7 +190,7 @@ class A:
 class B(A):
     def f(self) -> None:
         self.x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int")
-[out]
+[targets __main__, __main__.A.f, __main__.B.f]
 
 [case testAssignmentToAttributeInMultipleMethods]
 import typing
@@ -898,6 +899,7 @@ import typing
 class A:
     class B: pass
 A.B = None # E: Cannot assign to a type
+[targets __main__]
 
 [case testAccessingClassAttributeWithTypeInferenceIssue]
 x = C.x # E: Cannot determine type of 'x'
@@ -921,6 +923,7 @@ class A(Generic[T]):
     x = None  # type: T
 A.x  # E: Access to generic instance variables via class is ambiguous
 A[int].x  # E: Access to generic instance variables via class is ambiguous
+[targets __main__]
 
 [case testAccessingNestedGenericClassAttribute]
 from typing import Generic, List, TypeVar, Union
@@ -938,14 +941,13 @@ A[int, int].x  # E: Access to generic instance variables via class is ambiguous
 
 
 [case testClassWithinFunction]
-
 def f() -> None:
     class A:
         def g(self) -> None: pass
     a = None # type: A
     a.g()
     a.g(a) # E: Too many arguments for "g" of "A"
-[out]
+[targets __main__, __main__.f]
 
 [case testConstructNestedClass]
 import typing
@@ -1119,6 +1121,7 @@ A().f(1)
 A.f('') # E: Argument 1 to "f" of "A" has incompatible type "str"; expected "int"
 A().f('') # E: Argument 1 to "f" of "A" has incompatible type "str"; expected "int"
 [builtins fixtures/classmethod.pyi]
+[targets __main__, __main__.A.f]
 
 [case testBuiltinClassMethod]
 import typing
diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test
--- a/test-data/unit/check-namedtuple.test
+++ b/test-data/unit/check-namedtuple.test
@@ -150,6 +150,7 @@ i = n.b # type: int  # E: Incompatible types in assignment (expression has type
 x, y = n
 if int():
     x = y  # E: Incompatible types in assignment (expression has type "str", variable has type "int")
+[targets __main__, __main__.N.__new__, __main__.N._asdict, __main__.N._make, __main__.N._replace]
 
 
 [case testNamedTupleWithTupleFieldNamesWithItemTypes]
diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test
--- a/test-data/unit/check-newsemanal.test
+++ b/test-data/unit/check-newsemanal.test
@@ -1062,6 +1062,8 @@ class C: pass
 import a
 from b import C
 
+[targets c, b, a, c, b, __main__]
+
 [case testNewAnalyzerImportOverExistingInCycle]
 import a
 [file a.py]
@@ -3089,3 +3091,23 @@ import a
 
 def func() -> int: ...
 [targets b, a, a, b.func, a.func, __main__]
+
+[case testNewAnalyzerForwardReferenceInFunction]
+def f(x: 'A') -> 'A':
+    return A()
+
+class A:
+    pass
+[targets __main__, __main__.f]
+
+[case testNewAnalyzerSimpleImportStarNoDeferral]
+from m import *
+
+x: A
+f()
+
+[file m.py]
+class A: pass
+def f() -> None: pass
+
+[targets m, m.f, __main__]
diff --git a/test-data/unit/check-newtype.test b/test-data/unit/check-newtype.test
--- a/test-data/unit/check-newtype.test
+++ b/test-data/unit/check-newtype.test
@@ -19,7 +19,8 @@ num = id + 1
 
 reveal_type(id)  # N: Revealed type is '__main__.UserId'
 reveal_type(num)  # N: Revealed type is 'builtins.int'
-[out]
+
+[targets __main__, __main__.UserId.__init__, __main__.name_by_id]
 
 [case testNewTypePEP484Example2]
 from typing import NewType
diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test
--- a/test-data/unit/check-type-aliases.test
+++ b/test-data/unit/check-type-aliases.test
@@ -4,6 +4,7 @@ i = int
 def f(x: i) -> None: pass
 f(1)
 f('') # E: Argument 1 to "f" has incompatible type "str"; expected "int"
+[targets __main__, __main__.f]
 
 [case testUnionTypeAlias]
 from typing import Union
@@ -12,6 +13,7 @@ def f(x: U) -> None: pass
 f(1)
 f('')
 f(()) # E: Argument 1 to "f" has incompatible type "Tuple[]"; expected "Union[int, str]"
+[targets __main__, __main__.f]
 
 [case testTupleTypeAlias]
 from typing import Tuple
@@ -19,6 +21,7 @@ T = Tuple[int, str]
 def f(x: T) -> None: pass
 f((1, 'x'))
 f(1) # E: Argument 1 to "f" has incompatible type "int"; expected "Tuple[int, str]"
+[targets __main__, __main__.f]
 
 [case testCallableTypeAlias]
 from typing import Callable
@@ -26,6 +29,7 @@ A = Callable[[int], None]
 f = None  # type: A
 f(1)
 f('') # E: Argument 1 has incompatible type "str"; expected "int"
+[targets __main__]
 
 [case testListTypeAlias]
 from typing import List
@@ -34,7 +38,7 @@ def f(x: A) -> None: pass
 f([1])
 f(['x']) # E: List item 0 has incompatible type "str"; expected "int"
 [builtins fixtures/list.pyi]
-[out]
+[targets __main__, __main__.f]
 
 [case testAnyTypeAlias]
 from typing import Any
diff --git a/test-data/unit/check-typeddict.test b/test-data/unit/check-typeddict.test
--- a/test-data/unit/check-typeddict.test
+++ b/test-data/unit/check-typeddict.test
@@ -9,6 +9,7 @@ reveal_type(p)  # N: Revealed type is 'TypedDict('__main__.Point', {'x': builtin
 reveal_type(p.values()) # N: Revealed type is 'typing.Iterable[builtins.object*]'
 [builtins fixtures/dict.pyi]
 [typing fixtures/typing-full.pyi]
+[targets sys, __main__]
 
 [case testCanCreateTypedDictInstanceWithDictCall]
 from mypy_extensions import TypedDict