Skip to content

Commit

Permalink
Fix incompatible overrides of overloaded generics with self types (#1…
Browse files Browse the repository at this point in the history
…4882)

Fixes #14866

Basically does the potential todo I'd mentioned in #14017
  • Loading branch information
hauntsaninja committed Mar 11, 2023
1 parent fb6b8bc commit e2e5d7f
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 20 deletions.
41 changes: 21 additions & 20 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1876,23 +1876,6 @@ def check_method_override_for_base_with_name(
original_class_or_static = False # a variable can't be class or static

if isinstance(original_type, FunctionLike):
active_self_type = self.scope.active_self_type()
if isinstance(original_type, Overloaded) and active_self_type:
# If we have an overload, filter to overloads that match the self type.
# This avoids false positives for concrete subclasses of generic classes,
# see testSelfTypeOverrideCompatibility for an example.
# It's possible we might want to do this as part of bind_and_map_method
filtered_items = [
item
for item in original_type.items
if not item.arg_types or is_subtype(active_self_type, item.arg_types[0])
]
# If we don't have any filtered_items, maybe it's always a valid override
# of the superclass? However if you get to that point you're in murky type
# territory anyway, so we just preserve the type and have the behaviour match
# that of older versions of mypy.
if filtered_items:
original_type = Overloaded(filtered_items)
original_type = self.bind_and_map_method(base_attr, original_type, defn.info, base)
if original_node and is_property(original_node):
original_type = get_property_type(original_type)
Expand Down Expand Up @@ -1964,10 +1947,28 @@ def bind_and_map_method(
is_class_method = sym.node.func.is_class
else:
is_class_method = sym.node.is_class
bound = bind_self(typ, self.scope.active_self_type(), is_class_method)

mapped_typ = cast(FunctionLike, map_type_from_supertype(typ, sub_info, super_info))
active_self_type = self.scope.active_self_type()
if isinstance(mapped_typ, Overloaded) and active_self_type:
# If we have an overload, filter to overloads that match the self type.
# This avoids false positives for concrete subclasses of generic classes,
# see testSelfTypeOverrideCompatibility for an example.
filtered_items = [
item
for item in mapped_typ.items
if not item.arg_types or is_subtype(active_self_type, item.arg_types[0])
]
# If we don't have any filtered_items, maybe it's always a valid override
# of the superclass? However if you get to that point you're in murky type
# territory anyway, so we just preserve the type and have the behaviour match
# that of older versions of mypy.
if filtered_items:
mapped_typ = Overloaded(filtered_items)

return bind_self(mapped_typ, active_self_type, is_class_method)
else:
bound = typ
return cast(FunctionLike, map_type_from_supertype(bound, sub_info, super_info))
return cast(FunctionLike, map_type_from_supertype(typ, sub_info, super_info))

def get_op_other_domain(self, tp: FunctionLike) -> Type | None:
if isinstance(tp, CallableType):
Expand Down
24 changes: 24 additions & 0 deletions test-data/unit/check-selftype.test
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,30 @@ class J(A[int]):

[builtins fixtures/tuple.pyi]

[case testSelfTypeOverrideCompatibilityGeneric]
from typing import TypeVar, Generic, overload

T = TypeVar("T", str, int, None)

class A(Generic[T]):
@overload
def f(self, s: T) -> T: ...
@overload
def f(self: A[str], s: bytes) -> str: ...
def f(self, s: object): ...

class B(A[int]):
def f(self, s: int) -> int: ...

class C(A[None]):
def f(self, s: int) -> int: ... # E: Signature of "f" incompatible with supertype "A" \
# N: Superclass: \
# N: @overload \
# N: def f(self, s: None) -> None \
# N: Subclass: \
# N: def f(self, s: int) -> int
[builtins fixtures/tuple.pyi]

[case testSelfTypeOverrideCompatibilityTypeVar-xfail]
from typing import overload, TypeVar, Union

Expand Down

0 comments on commit e2e5d7f

Please sign in to comment.