Skip to content

Commit e2e5d7f

Browse files
authored
Fix incompatible overrides of overloaded generics with self types (#14882)
Fixes #14866 Basically does the potential todo I'd mentioned in #14017
1 parent fb6b8bc commit e2e5d7f

File tree

2 files changed

+45
-20
lines changed

2 files changed

+45
-20
lines changed

mypy/checker.py

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1876,23 +1876,6 @@ def check_method_override_for_base_with_name(
18761876
original_class_or_static = False # a variable can't be class or static
18771877

18781878
if isinstance(original_type, FunctionLike):
1879-
active_self_type = self.scope.active_self_type()
1880-
if isinstance(original_type, Overloaded) and active_self_type:
1881-
# If we have an overload, filter to overloads that match the self type.
1882-
# This avoids false positives for concrete subclasses of generic classes,
1883-
# see testSelfTypeOverrideCompatibility for an example.
1884-
# It's possible we might want to do this as part of bind_and_map_method
1885-
filtered_items = [
1886-
item
1887-
for item in original_type.items
1888-
if not item.arg_types or is_subtype(active_self_type, item.arg_types[0])
1889-
]
1890-
# If we don't have any filtered_items, maybe it's always a valid override
1891-
# of the superclass? However if you get to that point you're in murky type
1892-
# territory anyway, so we just preserve the type and have the behaviour match
1893-
# that of older versions of mypy.
1894-
if filtered_items:
1895-
original_type = Overloaded(filtered_items)
18961879
original_type = self.bind_and_map_method(base_attr, original_type, defn.info, base)
18971880
if original_node and is_property(original_node):
18981881
original_type = get_property_type(original_type)
@@ -1964,10 +1947,28 @@ def bind_and_map_method(
19641947
is_class_method = sym.node.func.is_class
19651948
else:
19661949
is_class_method = sym.node.is_class
1967-
bound = bind_self(typ, self.scope.active_self_type(), is_class_method)
1950+
1951+
mapped_typ = cast(FunctionLike, map_type_from_supertype(typ, sub_info, super_info))
1952+
active_self_type = self.scope.active_self_type()
1953+
if isinstance(mapped_typ, Overloaded) and active_self_type:
1954+
# If we have an overload, filter to overloads that match the self type.
1955+
# This avoids false positives for concrete subclasses of generic classes,
1956+
# see testSelfTypeOverrideCompatibility for an example.
1957+
filtered_items = [
1958+
item
1959+
for item in mapped_typ.items
1960+
if not item.arg_types or is_subtype(active_self_type, item.arg_types[0])
1961+
]
1962+
# If we don't have any filtered_items, maybe it's always a valid override
1963+
# of the superclass? However if you get to that point you're in murky type
1964+
# territory anyway, so we just preserve the type and have the behaviour match
1965+
# that of older versions of mypy.
1966+
if filtered_items:
1967+
mapped_typ = Overloaded(filtered_items)
1968+
1969+
return bind_self(mapped_typ, active_self_type, is_class_method)
19681970
else:
1969-
bound = typ
1970-
return cast(FunctionLike, map_type_from_supertype(bound, sub_info, super_info))
1971+
return cast(FunctionLike, map_type_from_supertype(typ, sub_info, super_info))
19711972

19721973
def get_op_other_domain(self, tp: FunctionLike) -> Type | None:
19731974
if isinstance(tp, CallableType):

test-data/unit/check-selftype.test

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,30 @@ class J(A[int]):
208208

209209
[builtins fixtures/tuple.pyi]
210210

211+
[case testSelfTypeOverrideCompatibilityGeneric]
212+
from typing import TypeVar, Generic, overload
213+
214+
T = TypeVar("T", str, int, None)
215+
216+
class A(Generic[T]):
217+
@overload
218+
def f(self, s: T) -> T: ...
219+
@overload
220+
def f(self: A[str], s: bytes) -> str: ...
221+
def f(self, s: object): ...
222+
223+
class B(A[int]):
224+
def f(self, s: int) -> int: ...
225+
226+
class C(A[None]):
227+
def f(self, s: int) -> int: ... # E: Signature of "f" incompatible with supertype "A" \
228+
# N: Superclass: \
229+
# N: @overload \
230+
# N: def f(self, s: None) -> None \
231+
# N: Subclass: \
232+
# N: def f(self, s: int) -> int
233+
[builtins fixtures/tuple.pyi]
234+
211235
[case testSelfTypeOverrideCompatibilityTypeVar-xfail]
212236
from typing import overload, TypeVar, Union
213237

0 commit comments

Comments
 (0)