Repository: python/mypy

Issue:
INTERNAL ERROR on class scoped import of a decorated function
**Crash Report**

After version 0.940, In #12023 and #12199, `mypy` disabled class scoped import. But, I found in a decorated function it will still raise an INTERNAL ERROR

**Traceback**

```
(Insert traceback and other messages from mypy here -- use `--show-traceback`.)
```

```bash
$ mypy . --show-traceback
./a.py:4: error: INTERNAL ERROR -- Please try using mypy master on Github:
https://mypy.readthedocs.io/en/stable/common_issues.html#using-a-development-mypy-build
Please report a bug at https://github.com/python/mypy/issues
version: 0.942
Traceback (most recent call last):
  File "mypy/checkexpr.py", line 3978, in accept
  File "mypy/nodes.py", line 1670, in accept
  File "mypy/checkexpr.py", line 2087, in visit_member_expr
  File "mypy/checkexpr.py", line 2105, in analyze_ordinary_member_access
  File "mypy/checkmember.py", line 130, in analyze_member_access
  File "mypy/checkmember.py", line 147, in _analyze_member_access
  File "mypy/checkmember.py", line 231, in analyze_instance_member_access
  File "mypy/checkmember.py", line 385, in analyze_member_var_access
  File "mypy/checkmember.py", line 559, in analyze_var
  File "mypy/maptype.py", line 20, in map_instance_to_supertype
AttributeError: attribute 'type_vars' of 'TypeInfo' undefined
./a.py:4: : note: use --pdb to drop into pdb

```

**To Reproduce**

```bash
$ cat a.py
class A:
	from b import foo

print(A().foo())  # ← INTERNAL ERROR here
$ cat b.py
def c(func):
    return func


@c
def foo(self):
	return ""
$ mypy .
./a.py:4: error: INTERNAL ERROR -- Please try using mypy master on Github:
https://mypy.readthedocs.io/en/stable/common_issues.html#using-a-development-mypy-build
If this issue continues with mypy master, please report a bug at https://github.com/python/mypy/issues
version: 0.942
./a.py:4: : note: please use --show-traceback to print a traceback when reporting a bug
```

**Your Environment**

<!-- Include as many relevant details about the environment you experienced the bug in -->

- Mypy version used: 0.942
- Mypy command-line flags: no flag
- Mypy configuration options from `mypy.ini` (and other config files):
- Python version used: 3.8.8
- Operating system and version: macOS 12.3



Discussion:
Cc. @hauntsaninja 

Resolving patch:
diff --git a/mypy/semanal.py b/mypy/semanal.py
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -4927,7 +4927,7 @@ def _get_node_for_class_scoped_import(
         # when it can also be a FuncBase. Once fixed, `f` in the following can be removed.
         # See also https://github.com/mypyc/mypyc/issues/892
         f = cast(Any, lambda x: x)
-        if isinstance(f(symbol_node), (FuncBase, Var)):
+        if isinstance(f(symbol_node), (Decorator, FuncBase, Var)):
             # For imports in class scope, we construct a new node to represent the symbol and
             # set its `info` attribute to `self.type`.
             existing = self.current_symbol_table().get(name)
@@ -4935,7 +4935,7 @@ def _get_node_for_class_scoped_import(
                 # The redefinition checks in `add_symbol_table_node` don't work for our
                 # constructed Var / FuncBase, so check for possible redefinitions here.
                 existing is not None
-                and isinstance(f(existing.node), (FuncBase, Var))
+                and isinstance(f(existing.node), (Decorator, FuncBase, Var))
                 and (
                     isinstance(f(existing.type), f(AnyType))
                     or f(existing.type) == f(symbol_node).type
@@ -4944,7 +4944,7 @@ def _get_node_for_class_scoped_import(
                 return existing.node
 
             # Construct the new node
-            if isinstance(f(symbol_node), FuncBase):
+            if isinstance(f(symbol_node), (FuncBase, Decorator)):
                 # In theory we could construct a new node here as well, but in practice
                 # it doesn't work well, see #12197
                 typ: Optional[Type] = AnyType(TypeOfAny.from_error)


Test patch:
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
@@ -7284,3 +7284,19 @@ def meth1(self: Any, y: str) -> str: ...
 
 T = TypeVar("T")
 def meth2(self: Any, y: T) -> T: ...
+
+[case testClassScopeImportWithWrapperAndError]
+class Foo:
+    from mod import foo # E: Unsupported class scoped import
+
+[file mod.py]
+from typing import Any, Callable, TypeVar
+
+FuncT = TypeVar("FuncT", bound=Callable[..., Any])
+def identity_wrapper(func: FuncT) -> FuncT:
+    return func
+
+@identity_wrapper
+def foo(self: Any) -> str:
+    return ""
+