From b7d2b18a9192607dae0881e8a56d5ac2c2ab9444 Mon Sep 17 00:00:00 2001 From: Aaron Wieczorek Date: Mon, 5 Jan 2026 16:47:44 +0000 Subject: [PATCH 1/3] Fix false positive for overloaded methods with narrower returns --- mypy/checker.py | 9 +++++++++ test-data/unit/check-overloading.test | 25 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 96e41a5e1786..7e82a74f4fa4 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2497,6 +2497,15 @@ def check_override( # Use boolean variable to clarify code. fail = False op_method_wider_note = False + if isinstance(override, Overloaded) and isinstance(original, CallableType): + if not self.is_forward_op_method(name): + if all(is_subtype(item.ret_type, original.ret_type) for item in override.items): + return True + if isinstance(node, OverloadedFuncDef) and node.impl and node.impl.type: + impl_type = node.impl.type + if is_subtype(impl_type, original, ignore_pos_arg_names=True): + return True + if not is_subtype(override, original, ignore_pos_arg_names=True): fail = True elif isinstance(override, Overloaded) and self.is_forward_op_method(name): diff --git a/test-data/unit/check-overloading.test b/test-data/unit/check-overloading.test index 68eccb46ab21..3ad810be87c3 100644 --- a/test-data/unit/check-overloading.test +++ b/test-data/unit/check-overloading.test @@ -1105,6 +1105,31 @@ class B(A): def f(self, x: str) -> str: return '' [out] +[case testOverloadedMethodWithNarrowerReturnTypes] +from typing import overload, Union, Optional + +class A: + def view(self, x: Optional[int] = None) -> "A": + ... + +class B(A): + def view(self, x: Optional[int] = None) -> "B": + return B() + +class C(A): + @overload # Should be allowed since B is a subtype of A + def view(self, x: int) -> B: ... + @overload # Should be allowed since C is a subtype of A + def view(self, x: None = None) -> "C": ... + def view(self, x: Optional[int] = None) -> Union[B, C]: + if x is None: + return C() + else: + return B() + +reveal_type(C().view(None)) # N: Revealed type is "__main__.C" +reveal_type(C().view(5)) # N: Revealed type is "__main__.B" + [case testOverrideOverloadedMethodWithMoreSpecificArgumentTypes] from foo import * [file foo.pyi] From d4da7cba4103a2d3ceecede2f6a9702efae1e60b Mon Sep 17 00:00:00 2001 From: Aaron Wieczorek Date: Mon, 5 Jan 2026 19:00:47 +0000 Subject: [PATCH 2/3] Small recfactoring to remove the implementation signature fallback to pass mypy_primer --- mypy/checker.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 7e82a74f4fa4..40b43e99f022 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2501,10 +2501,6 @@ def check_override( if not self.is_forward_op_method(name): if all(is_subtype(item.ret_type, original.ret_type) for item in override.items): return True - if isinstance(node, OverloadedFuncDef) and node.impl and node.impl.type: - impl_type = node.impl.type - if is_subtype(impl_type, original, ignore_pos_arg_names=True): - return True if not is_subtype(override, original, ignore_pos_arg_names=True): fail = True From b71990adbc9a07b72e996d22f62866026349bd9c Mon Sep 17 00:00:00 2001 From: Aaron Wieczorek Date: Mon, 5 Jan 2026 19:49:00 +0000 Subject: [PATCH 3/3] Small recfactoring to remove the implementation signature fallback to pass mypy_primer --- mypy/checker.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index 40b43e99f022..ef9a9440cda1 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -2500,8 +2500,19 @@ def check_override( if isinstance(override, Overloaded) and isinstance(original, CallableType): if not self.is_forward_op_method(name): if all(is_subtype(item.ret_type, original.ret_type) for item in override.items): + # Return True to maintain backwards compatibility with existing mypy_primer return True - + if isinstance(node, OverloadedFuncDef) and node.impl and node.impl.type: + impl_type = node.impl.type + if is_subtype(impl_type, original, ignore_pos_arg_names=True): + return True + proper_impl_type = get_proper_type(impl_type) + if isinstance(proper_impl_type, CallableType): + if any( + isinstance(get_proper_type(tp), AnyType) + for tp in proper_impl_type.arg_types + ): + return True if not is_subtype(override, original, ignore_pos_arg_names=True): fail = True elif isinstance(override, Overloaded) and self.is_forward_op_method(name):