Repository: python/mypy

Issue:
self.__class__.__name__ has type Any in class derived from Any
Here's something that came up in https://github.com/python/typeshed/pull/1549#issuecomment-333227407.
```py
from typing import Any, Type, TypeVar
T = TypeVar('T')
class C(Any):
    def bar(self: T) -> Type[T]: ...
    def foo(self) -> None:
        reveal_type(self.bar())  # Type[C], as expected
        reveal_type(self.bar().__name__)  # Any, but expected str
```
@ilevkivskyi I think you muttered something about `analyze_member_access` for `TypeType` still being broken but I'm not sure I understand.


Discussion:

Resolving patch:
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -177,26 +177,35 @@ def analyze_member_access(name: str,
     elif isinstance(typ, TypeType):
         # Similar to FunctionLike + is_type_obj() above.
         item = None
+        fallback = builtin_type('builtins.type')
+        ignore_messages = msg.copy()
+        ignore_messages.disable_errors()
         if isinstance(typ.item, Instance):
             item = typ.item
         elif isinstance(typ.item, AnyType):
-            fallback = builtin_type('builtins.type')
-            ignore_messages = msg.copy()
-            ignore_messages.disable_errors()
             return analyze_member_access(name, fallback, node, is_lvalue, is_super,
                                      is_operator, builtin_type, not_ready_callback,
                                      ignore_messages, original_type=original_type, chk=chk)
         elif isinstance(typ.item, TypeVarType):
             if isinstance(typ.item.upper_bound, Instance):
                 item = typ.item.upper_bound
+        elif isinstance(typ.item, FunctionLike) and typ.item.is_type_obj():
+            item = typ.item.fallback
+        elif isinstance(typ.item, TypeType):
+            # Access member on metaclass object via Type[Type[C]]
+            if isinstance(typ.item.item, Instance):
+                item = typ.item.item.type.metaclass_type
         if item and not is_operator:
             # See comment above for why operators are skipped
             result = analyze_class_attribute_access(item, name, node, is_lvalue,
                                                     builtin_type, not_ready_callback, msg,
                                                     original_type=original_type)
             if result:
-                return result
-        fallback = builtin_type('builtins.type')
+                if not (isinstance(result, AnyType) and item.type.fallback_to_any):
+                    return result
+                else:
+                    # We don't want errors on metaclass lookup for classes with Any fallback
+                    msg = ignore_messages
         if item is not None:
             fallback = item.type.metaclass_type or fallback
         return analyze_member_access(name, fallback, node, is_lvalue, is_super,
diff --git a/mypy/types.py b/mypy/types.py
--- a/mypy/types.py
+++ b/mypy/types.py
@@ -717,8 +717,7 @@ def copy_modified(self,
         )
 
     def is_type_obj(self) -> bool:
-        t = self.fallback.type
-        return t is not None and t.is_metaclass()
+        return self.fallback.type.is_metaclass()
 
     def is_concrete_type_obj(self) -> bool:
         return self.is_type_obj() and self.is_classmethod_class
@@ -1341,10 +1340,7 @@ def __init__(self, item: Union[Instance, AnyType, TypeVarType, TupleType, NoneTy
         type UnionType must be handled through make_normalized static method.
         """
         super().__init__(line, column)
-        if isinstance(item, CallableType) and item.is_type_obj():
-            self.item = item.fallback
-        else:
-            self.item = item
+        self.item = item
 
     @staticmethod
     def make_normalized(item: Type, *, line: int = -1, column: int = -1) -> Type:


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
@@ -3995,3 +3995,44 @@ class E(metaclass=t.M): pass
 class F(six.with_metaclass(t.M)): pass
 @six.add_metaclass(t.M)
 class G: pass
+
+[case testMetaclassMemberAccessViaType]
+from typing import Type
+class M(type):
+    def m(cls, x: int) -> int:
+        pass
+
+class C(metaclass=M):
+    pass
+x = C
+y: Type[C] = C
+
+reveal_type(type(C).m) # E: Revealed type is 'def (cls: __main__.M, x: builtins.int) -> builtins.int'
+reveal_type(type(x).m) # E: Revealed type is 'def (cls: __main__.M, x: builtins.int) -> builtins.int'
+reveal_type(type(y).m) # E: Revealed type is 'def (cls: __main__.M, x: builtins.int) -> builtins.int'
+[out]
+
+[case testMetaclassMemberAccessViaType2]
+from typing import Any, Type
+class M(type):
+    def m(cls, x: int) -> int:
+        pass
+B: Any
+class C(B, metaclass=M):
+    pass
+
+x: Type[C]
+reveal_type(x.m) # E: Revealed type is 'def (x: builtins.int) -> builtins.int'
+reveal_type(x.whatever) # E: Revealed type is 'Any'
+[out]
+
+[case testMetaclassMemberAccessViaType3]
+from typing import Any, Type, TypeVar
+T = TypeVar('T')
+class C(Any):
+    def bar(self: T) -> Type[T]: pass
+    def foo(self) -> None:
+        reveal_type(self.bar()) # E: Revealed type is 'Type[__main__.C*]'
+        reveal_type(self.bar().__name__) # E: Revealed type is 'builtins.str'
+[builtins fixtures/type.pyi]
+[out]
diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test
--- a/test-data/unit/check-incremental.test
+++ b/test-data/unit/check-incremental.test
@@ -3164,3 +3164,23 @@ import foo
 external_list = [0]
 
 [builtins fixtures/dict.pyi]
+
+[case testIncrementalCrashOnTypeWithFunction]
+import a
+[file a.py]
+import b
+[file a.py.2]
+from b import x
+
+[file b.py]
+from typing import TypeVar, Type
+T = TypeVar('T')
+
+def tp(arg: T) -> Type[T]:
+    pass
+def func(x: int) -> int:
+    pass
+
+x = tp(func)
+[out]
+[out2]