Repository: python/mypy

Issue:
Cannot determine type of a function with a no_type_check decorator
Mypy complains if I call a `no_type_check` function above the definition:

```
import typing

def f() -> None:
    foo()  # Error: Cannot determine type of 'foo'

@typing.no_type_check
def foo(x: {1:2}) -> [1]:
    1 + 'x'
```

We should detect `no_type_check` during semantic analysis and set the type of the `Decorator` object to `Any` there, before type checking.



Discussion:

Resolving patch:
diff --git a/mypy/semanal.py b/mypy/semanal.py
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -1223,14 +1223,15 @@ def visit_decorator(self, dec: Decorator) -> None:
         for d in dec.decorators:
             d.accept(self)
         removed = []  # type: List[int]
+        no_type_check = False
         for i, d in enumerate(dec.decorators):
             if refers_to_fullname(d, 'abc.abstractmethod'):
                 removed.append(i)
                 dec.func.is_abstract = True
                 self.check_decorated_function_is_method('abstractmethod', dec)
             elif refers_to_fullname(d, 'asyncio.tasks.coroutine'):
-                    removed.append(i)
-                    dec.func.is_coroutine = True
+                removed.append(i)
+                dec.func.is_coroutine = True
             elif refers_to_fullname(d, 'builtins.staticmethod'):
                 removed.append(i)
                 dec.func.is_static = True
@@ -1248,6 +1249,9 @@ def visit_decorator(self, dec: Decorator) -> None:
                 self.check_decorated_function_is_method('property', dec)
                 if len(dec.func.args) > 1:
                     self.fail('Too many arguments', dec.func)
+            elif refers_to_fullname(d, 'typing.no_type_check'):
+                dec.var.type = AnyType()
+                no_type_check = True
         for i in reversed(removed):
             del dec.decorators[i]
         if not dec.is_overload or dec.var.is_property:
@@ -1261,7 +1265,8 @@ def visit_decorator(self, dec: Decorator) -> None:
                                 dec)
         if dec.decorators and dec.var.is_property:
             self.fail('Decorated property not supported', dec)
-        dec.func.accept(self)
+        if not no_type_check:
+            dec.func.accept(self)
         if not dec.decorators and not dec.var.is_property:
             # No non-special decorators left. We can trivially infer the type
             # of the function here.


Test patch:
diff --git a/mypy/test/data/check-functions.test b/mypy/test/data/check-functions.test
--- a/mypy/test/data/check-functions.test
+++ b/mypy/test/data/check-functions.test
@@ -578,7 +578,7 @@ def foo(x: 's', y: {'x': 4}) -> 42:
 def bar() -> None:
     1 + 'x'
 
-[case testCallingNoTypeCheckFunction]
+[case testCallingNoTypeCheckFunction2]
 import typing
 
 @typing.no_type_check
@@ -588,6 +588,23 @@ def foo(x: {1:2}) -> [1]:
 foo()
 foo(1, 'b')
 
+[case testCallingNoTypeCheckFunction2]
+import typing
+
+def f() -> None:
+    foo()
+
+@typing.no_type_check
+def foo(x: {1:2}) -> [1]:
+    1 + 'x'
+
+[case testNoTypeCheckDecoratorSemanticError]
+import typing
+
+@typing.no_type_check
+def foo(x: {1:2}) -> [1]:
+    x = y
+
 
 -- Conditional function definition
 -- -------------------------------