Repository: python/mypy

Issue:
Wrong file blamed for a mixture of NamedTuple and passing a function instead of type.
* Are you reporting a bug, or opening a feature request?
bug

* Please insert below the code you are checking with mypy,
  * Create an empty dir/__init__.py.
  * Create dir/file1.py with:
```
import typing

def foo():
  pass


Type1 = typing.NamedTuple('Type1', [
    ('foo', foo),
])
```
  * Create file2.py (in the root dir) with:
```
from typing import NamedTuple

from dir.file1 import Type1

Type2 = NamedTuple('Type2', [
    ('x', Type1),
])
```
  * Run: mypy file2.py --follow-imports=silent

* What is the actual behavior/output?

The error message is:
file2.py:8: error: Name 'foo' is not defined

-> Note that file2.py has no line 8 (it the problematic line in file1.py) and no mention of `foo`.

* What is the behavior/output you expect?
  * the error message should point to file1.py or say something about Type1 in file2.py being invalid.
  * foo is well defined in this case, making the error message more cryptic.

When reducing this case I've found that removing --follow-import-silent gives an error message about file1.py which is actionable, but the other message is still present and is wrong, since the line number refers to a line in file1.py.

* What are the versions of mypy and Python you are using?

mypy 0.761 on Python 3.5.2

* What are the mypy flags you are using? (For example --strict-optional)

--follow-imports=silent



Discussion:

Resolving patch:
diff --git a/mypy/semanal_namedtuple.py b/mypy/semanal_namedtuple.py
--- a/mypy/semanal_namedtuple.py
+++ b/mypy/semanal_namedtuple.py
@@ -8,7 +8,8 @@
 from typing_extensions import Final
 
 from mypy.types import (
-    Type, TupleType, AnyType, TypeOfAny, TypeVarDef, CallableType, TypeType, TypeVarType
+    Type, TupleType, AnyType, TypeOfAny, TypeVarDef, CallableType, TypeType, TypeVarType,
+    UnboundType,
 )
 from mypy.semanal_shared import (
     SemanticAnalyzerInterface, set_callable_name, calculate_tuple_fallback, PRIORITY_FALLBACKS
@@ -334,6 +335,9 @@ def parse_namedtuple_fields_with_types(self, nodes: List[Expression], context: C
                 except TypeTranslationError:
                     return self.fail_namedtuple_arg('Invalid field type', type_node)
                 analyzed = self.api.anal_type(type)
+                # Workaround #4987 and avoid introducing a bogus UnboundType
+                if isinstance(analyzed, UnboundType):
+                    analyzed = AnyType(TypeOfAny.from_error)
                 # These should be all known, otherwise we would defer in visit_assignment_stmt().
                 if analyzed is None:
                     return None


Test patch:
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
@@ -948,3 +948,17 @@ class A:
 
 reveal_type(A().b)  # N: Revealed type is 'Any'
 [builtins fixtures/tuple.pyi]
+
+[case testNamedTupleWrongfile]
+from typing import NamedTuple
+from b import Type1
+Type2 = NamedTuple('Type2', [('x', Type1)])
+[file b.py]
+from typing import NamedTuple
+
+def foo():
+    pass
+
+Type1 = NamedTuple('Type1', [('foo', foo)])  # E: Function "b.foo" is not valid as a type  # N: Perhaps you need "Callable[...]" or a callback protocol?
+
+[builtins fixtures/tuple.pyi]
diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test
--- a/test-data/unit/fine-grained.test
+++ b/test-data/unit/fine-grained.test
@@ -8477,8 +8477,8 @@ m.py:4: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#varia
 ==
 m.py:4: error: Variable "a.A" is not valid as a type
 m.py:4: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
-m.py:5: note: Revealed type is 'A?'
-m.py:7: note: Revealed type is 'A?'
+m.py:5: note: Revealed type is 'Any'
+m.py:7: note: Revealed type is 'Any'
 
 [case testAliasForwardFunctionDirect]
 # flags: --ignore-missing-imports