Skip to content

Commit a48dd5a

Browse files
authored
Fix incompatible overrides of overloaded methods in concrete subclasses (#14017)
Fixes #14002
1 parent b18281c commit a48dd5a

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

mypy/checker.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1869,6 +1869,23 @@ def check_method_override_for_base_with_name(
18691869
original_class_or_static = False # a variable can't be class or static
18701870

18711871
if isinstance(original_type, FunctionLike):
1872+
active_self_type = self.scope.active_self_type()
1873+
if isinstance(original_type, Overloaded) and active_self_type:
1874+
# If we have an overload, filter to overloads that match the self type.
1875+
# This avoids false positives for concrete subclasses of generic classes,
1876+
# see testSelfTypeOverrideCompatibility for an example.
1877+
# It's possible we might want to do this as part of bind_and_map_method
1878+
filtered_items = [
1879+
item
1880+
for item in original_type.items
1881+
if not item.arg_types or is_subtype(active_self_type, item.arg_types[0])
1882+
]
1883+
# If we don't have any filtered_items, maybe it's always a valid override
1884+
# of the superclass? However if you get to that point you're in murky type
1885+
# territory anyway, so we just preserve the type and have the behaviour match
1886+
# that of older versions of mypy.
1887+
if filtered_items:
1888+
original_type = Overloaded(filtered_items)
18721889
original_type = self.bind_and_map_method(base_attr, original_type, defn.info, base)
18731890
if original_node and is_property(original_node):
18741891
original_type = get_property_type(original_type)

test-data/unit/check-selftype.test

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,92 @@ class B2(A[T]):
156156
def f(self: A[bytes]) -> bytes: ...
157157
def f(self): ...
158158

159+
class C(A[int]):
160+
def f(self) -> int: ...
161+
162+
class D(A[str]):
163+
def f(self) -> int: ... # E: Signature of "f" incompatible with supertype "A" \
164+
# N: Superclass: \
165+
# N: @overload \
166+
# N: def f(self) -> str \
167+
# N: Subclass: \
168+
# N: def f(self) -> int
169+
170+
class E(A[T]):
171+
def f(self) -> int: ... # E: Signature of "f" incompatible with supertype "A" \
172+
# N: Superclass: \
173+
# N: @overload \
174+
# N: def f(self) -> int \
175+
# N: @overload \
176+
# N: def f(self) -> str \
177+
# N: Subclass: \
178+
# N: def f(self) -> int
179+
180+
181+
class F(A[bytes]):
182+
# Note there's an argument to be made that this is actually compatible with the supertype
183+
def f(self) -> bytes: ... # E: Signature of "f" incompatible with supertype "A" \
184+
# N: Superclass: \
185+
# N: @overload \
186+
# N: def f(self) -> int \
187+
# N: @overload \
188+
# N: def f(self) -> str \
189+
# N: Subclass: \
190+
# N: def f(self) -> bytes
191+
192+
class G(A):
193+
def f(self): ...
194+
195+
class H(A[int]):
196+
def f(self): ...
197+
198+
class I(A[int]):
199+
def f(*args): ...
200+
201+
class J(A[int]):
202+
def f(self, arg) -> int: ... # E: Signature of "f" incompatible with supertype "A" \
203+
# N: Superclass: \
204+
# N: @overload \
205+
# N: def f(self) -> int \
206+
# N: Subclass: \
207+
# N: def f(self, arg: Any) -> int
208+
209+
[builtins fixtures/tuple.pyi]
210+
211+
[case testSelfTypeOverrideCompatibilityTypeVar-xfail]
212+
from typing import overload, TypeVar, Union
213+
214+
AT = TypeVar("AT", bound="A")
215+
216+
class A:
217+
@overload
218+
def f(self: AT, x: int) -> AT: ...
219+
@overload
220+
def f(self, x: str) -> None: ...
221+
@overload
222+
def f(self: AT) -> bytes: ...
223+
def f(*a, **kw): ...
224+
225+
class B(A):
226+
@overload # E: Signature of "f" incompatible with supertype "A" \
227+
# N: Superclass: \
228+
# N: @overload \
229+
# N: def f(self, x: int) -> B \
230+
# N: @overload \
231+
# N: def f(self, x: str) -> None \
232+
# N: @overload \
233+
# N: def f(self) -> bytes \
234+
# N: Subclass: \
235+
# N: @overload \
236+
# N: def f(self, x: int) -> B \
237+
# N: @overload \
238+
# N: def f(self, x: str) -> None
239+
def f(self, x: int) -> B: ...
240+
@overload
241+
def f(self, x: str) -> None: ...
242+
def f(*a, **kw): ...
243+
[builtins fixtures/dict.pyi]
244+
159245
[case testSelfTypeSuper]
160246
from typing import TypeVar, cast
161247

0 commit comments

Comments
 (0)