Skip to content

Commit 3f8d7e9

Browse files
authored
Fix self-types in access to overloaded class methods on instances (#7937)
Fixes #7926 The fix is straightforward, pass `is_classmethod` where it should be. Also wrap self argument in `Type[...]` only once, not on every iteration over overload items.
1 parent 91409ff commit 3f8d7e9

File tree

2 files changed

+37
-5
lines changed

2 files changed

+37
-5
lines changed

mypy/checkmember.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,9 @@ def analyze_instance_member_access(name: str,
204204
# TODO: use proper treatment of special methods on unions instead
205205
# of this hack here and below (i.e. mx.self_type).
206206
dispatched_type = meet.meet_types(mx.original_type, typ)
207-
signature = check_self_arg(signature, dispatched_type, False, mx.context,
208-
name, mx.msg)
209-
signature = bind_self(signature, mx.self_type)
207+
signature = check_self_arg(signature, dispatched_type, method.is_class,
208+
mx.context, name, mx.msg)
209+
signature = bind_self(signature, mx.self_type, is_classmethod=method.is_class)
210210
typ = map_instance_to_supertype(typ, method.info)
211211
member_type = expand_type_by_instance(signature, typ)
212212
freeze_type_vars(member_type)
@@ -625,6 +625,8 @@ def f(self: S) -> T: ...
625625
if not items:
626626
return functype
627627
new_items = []
628+
if is_classmethod:
629+
dispatched_arg_type = TypeType.make_normalized(dispatched_arg_type)
628630
for item in items:
629631
if not item.arg_types or item.arg_kinds[0] not in (ARG_POS, ARG_STAR):
630632
# No positional first (self) argument (*args is okay).
@@ -634,8 +636,6 @@ def f(self: S) -> T: ...
634636
return functype
635637
else:
636638
selfarg = item.arg_types[0]
637-
if is_classmethod:
638-
dispatched_arg_type = TypeType.make_normalized(dispatched_arg_type)
639639
if subtypes.is_subtype(dispatched_arg_type, erase_typevars(erase_to_bound(selfarg))):
640640
new_items.append(item)
641641
if not new_items:

test-data/unit/check-selftype.test

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,3 +937,35 @@ t: Type[Union[B, C]]
937937
x = t.meth()[0]
938938
reveal_type(x) # N: Revealed type is 'Union[__main__.B*, __main__.C*]'
939939
[builtins fixtures/isinstancelist.pyi]
940+
941+
[case testSelfTypeClassMethodOverloadedOnInstance]
942+
from typing import Optional, Type, TypeVar, overload, Union
943+
944+
Id = int
945+
946+
A = TypeVar("A", bound=AClass)
947+
948+
class AClass:
949+
@overload
950+
@classmethod
951+
def delete(cls: Type[A], id: Id, id2: Id) -> Optional[int]: ...
952+
953+
@overload
954+
@classmethod
955+
def delete(cls: Type[A], id: A, id2: None = None) -> Optional[int]: ...
956+
957+
@classmethod
958+
def delete(cls: Type[A], id: Union[A, Id], id2: Optional[Id] = None) -> Optional[int]:
959+
...
960+
961+
def foo(x: Type[AClass]) -> None:
962+
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)'
963+
y = x()
964+
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)'
965+
y.delete(10, 20)
966+
y.delete(y)
967+
968+
def bar(x: AClass) -> None:
969+
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)'
970+
x.delete(10, 20)
971+
[builtins fixtures/classmethod.pyi]

0 commit comments

Comments
 (0)