Repository: python/mypy

Issue:
Generic Self Type classmethods behave incorrectly on instances
Repro:

```python
from typing import TypeVar, Tuple, Type

T = TypeVar('T', bound='Friend')

class Friend:
    other = None  # type: Friend

    @classmethod
    def make(cls: Type[T]) -> T:
        return cls()

class SuperFriend(Friend):
    pass

reveal_type(SuperFriend.make())  # Revealed type is 'test_generic_self.SuperFriend*'
reveal_type(SuperFriend().make())  # Revealed type is '<nothing>'
```

Both invocations should return `test_generic_self.SuperFriend*`.


Discussion:
This looks like a bug to me.
@elazarg what do you think?
Yep

Resolving patch:
diff --git a/mypy/checkmember.py b/mypy/checkmember.py
--- a/mypy/checkmember.py
+++ b/mypy/checkmember.py
@@ -4,7 +4,7 @@
 
 from mypy.types import (
     Type, Instance, AnyType, TupleType, TypedDictType, CallableType, FunctionLike, TypeVarDef,
-    Overloaded, TypeVarType, UnionType, PartialType,
+    Overloaded, TypeVarType, UnionType, PartialType, UninhabitedType,
     DeletedType, NoneTyp, TypeType, function_type, get_type_vars,
 )
 from mypy.nodes import (
@@ -306,7 +306,7 @@ def analyze_var(name: str, var: Var, itype: Instance, info: TypeInfo, node: Cont
                 # class.
                 functype = t
                 check_method_type(functype, itype, var.is_classmethod, node, msg)
-                signature = bind_self(functype, original_type)
+                signature = bind_self(functype, original_type, var.is_classmethod)
                 if var.is_property:
                     # A property cannot have an overloaded type => the cast
                     # is fine.
@@ -472,7 +472,7 @@ class B(A): pass
         tvars = [TypeVarDef(n, i + 1, [], builtin_type('builtins.object'), tv.variance)
                  for (i, n), tv in zip(enumerate(info.type_vars), info.defn.type_vars)]
         if is_classmethod:
-            t = bind_self(t, original_type)
+            t = bind_self(t, original_type, is_classmethod=True)
         return t.copy_modified(variables=tvars + t.variables)
     elif isinstance(t, Overloaded):
         return Overloaded([cast(CallableType, add_class_tvars(item, itype, is_classmethod,
@@ -596,7 +596,7 @@ def map_type_from_supertype(typ: Type, sub_info: TypeInfo,
 F = TypeVar('F', bound=FunctionLike)
 
 
-def bind_self(method: F, original_type: Type = None) -> F:
+def bind_self(method: F, original_type: Type = None, is_classmethod: bool = False) -> F:
     """Return a copy of `method`, with the type of its first parameter (usually
     self or cls) bound to original_type.
 
@@ -642,8 +642,13 @@ class B(A): pass
             # XXX value restriction as union?
             original_type = erase_to_bound(self_param_type)
 
-        typearg = infer_type_arguments([x.id for x in func.variables],
-                                       self_param_type, original_type)[0]
+        ids = [x.id for x in func.variables]
+        typearg = infer_type_arguments(ids, self_param_type, original_type)[0]
+        if (is_classmethod and isinstance(typearg, UninhabitedType)
+                and isinstance(original_type, (Instance, TypeVarType, TupleType))):
+            # In case we call a classmethod through an instance x, fallback to type(x)
+            # TODO: handle Union
+            typearg = infer_type_arguments(ids, self_param_type, TypeType(original_type))[0]
 
         def expand(target: Type) -> Type:
             assert typearg is not None


Test patch:
diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test
--- a/test-data/unit/check-selftype.test
+++ b/test-data/unit/check-selftype.test
@@ -208,7 +208,6 @@ class B(A):
 
 [case testSelfTypeClone]
 from typing import TypeVar, Type
-
 T = TypeVar('T', bound='C')
 
 class C:
@@ -219,13 +218,26 @@ class C:
     def new(cls: Type[T]) -> T:
         return cls()
 
-def clone(arg: T) -> T:
-    reveal_type(arg.copy)  # E: Revealed type is 'def () -> T`-1'
-    return arg.copy()
+class D(C): pass
+
+reveal_type(D.new)  # E: Revealed type is 'def () -> __main__.D*'
+reveal_type(D().new)  # E: Revealed type is 'def () -> __main__.D*'
+reveal_type(D.new())  # E: Revealed type is '__main__.D*'
+reveal_type(D().new())  # E: Revealed type is '__main__.D*'
 
+Q = TypeVar('Q', bound=C)
+
+def clone(arg: Q) -> Q:
+    reveal_type(arg.copy)  # E: Revealed type is 'def () -> Q`-1'
+    reveal_type(arg.copy())  # E: Revealed type is 'Q`-1'
+    reveal_type(arg.new)  # E: Revealed type is 'def () -> Q`-1'
+    reveal_type(arg.new())  # E: Revealed type is 'Q`-1'
+    return arg.copy()
 
-def make(cls: Type[T]) -> T:
-    reveal_type(cls.new)  # E: Revealed type is 'def () -> T`-1'
+def make(cls: Type[Q]) -> Q:
+    reveal_type(cls.new)  # E: Revealed type is 'def () -> Q`-1'
+    reveal_type(cls().new)  # E: Revealed type is 'def () -> Q`-1'
+    reveal_type(cls().new())  # E: Revealed type is 'Q`-1'
     return cls.new()
 
 [builtins fixtures/classmethod.pyi]