diff --git a/mypy/checkmember.py b/mypy/checkmember.py index 859bd6afcc6d..151cdb454205 100644 --- a/mypy/checkmember.py +++ b/mypy/checkmember.py @@ -204,9 +204,9 @@ def analyze_instance_member_access(name: str, # TODO: use proper treatment of special methods on unions instead # of this hack here and below (i.e. mx.self_type). dispatched_type = meet.meet_types(mx.original_type, typ) - signature = check_self_arg(signature, dispatched_type, False, mx.context, - name, mx.msg) - signature = bind_self(signature, mx.self_type) + signature = check_self_arg(signature, dispatched_type, method.is_class, + mx.context, name, mx.msg) + signature = bind_self(signature, mx.self_type, is_classmethod=method.is_class) typ = map_instance_to_supertype(typ, method.info) member_type = expand_type_by_instance(signature, typ) freeze_type_vars(member_type) @@ -623,6 +623,8 @@ def f(self: S) -> T: ... if not items: return functype new_items = [] + if is_classmethod: + dispatched_arg_type = TypeType.make_normalized(dispatched_arg_type) for item in items: if not item.arg_types or item.arg_kinds[0] not in (ARG_POS, ARG_STAR): # No positional first (self) argument (*args is okay). @@ -632,8 +634,6 @@ def f(self: S) -> T: ... return functype else: selfarg = item.arg_types[0] - if is_classmethod: - dispatched_arg_type = TypeType.make_normalized(dispatched_arg_type) if subtypes.is_subtype(dispatched_arg_type, erase_typevars(erase_to_bound(selfarg))): new_items.append(item) if not new_items: diff --git a/test-data/unit/check-selftype.test b/test-data/unit/check-selftype.test index c4d1bb3d2e53..bf8a2a762fc1 100644 --- a/test-data/unit/check-selftype.test +++ b/test-data/unit/check-selftype.test @@ -937,3 +937,35 @@ t: Type[Union[B, C]] x = t.meth()[0] reveal_type(x) # N: Revealed type is 'Union[__main__.B*, __main__.C*]' [builtins fixtures/isinstancelist.pyi] + +[case testSelfTypeClassMethodOverloadedOnInstance] +from typing import Optional, Type, TypeVar, overload, Union + +Id = int + +A = TypeVar("A", bound=AClass) + +class AClass: + @overload + @classmethod + def delete(cls: Type[A], id: Id, id2: Id) -> Optional[int]: ... + + @overload + @classmethod + def delete(cls: Type[A], id: A, id2: None = None) -> Optional[int]: ... + + @classmethod + def delete(cls: Type[A], id: Union[A, Id], id2: Optional[Id] = None) -> Optional[int]: + ... + +def foo(x: Type[AClass]) -> None: + reveal_type(x.delete) # N: Revealed type is 'Overload(def (id: builtins.int, id2: builtins.int) -> builtins.int, def (id: __main__.AClass*, id2: None =) -> builtins.int)' + y = x() + reveal_type(y.delete) # N: Revealed type is 'Overload(def (id: builtins.int, id2: builtins.int) -> builtins.int, def (id: __main__.AClass*, id2: None =) -> builtins.int)' + y.delete(10, 20) + y.delete(y) + +def bar(x: AClass) -> None: + reveal_type(x.delete) # N: Revealed type is 'Overload(def (id: builtins.int, id2: builtins.int) -> builtins.int, def (id: __main__.AClass*, id2: None =) -> builtins.int)' + x.delete(10, 20) +[builtins fixtures/classmethod.pyi]