Skip to content

Commit 192bc85

Browse files
committed
Add support for overloads with @OverRide
1 parent f949dd4 commit 192bc85

File tree

4 files changed

+111
-4
lines changed

4 files changed

+111
-4
lines changed

mypy/checker.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,9 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
641641
if defn.impl:
642642
defn.impl.accept(self)
643643
if defn.info:
644-
self.check_method_override(defn)
644+
found_base_method = self.check_method_override(defn)
645+
if defn.is_explicit_override and found_base_method is False:
646+
self.msg.no_overridable_method(defn.name, defn)
645647
self.check_inplace_operator_method(defn)
646648
if not defn.is_property:
647649
self.check_overlapping_overloads(defn)

mypy/nodes.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,7 @@ class FuncBase(Node):
512512
"is_class", # Uses "@classmethod" (explicit or implicit)
513513
"is_static", # Uses "@staticmethod"
514514
"is_final", # Uses "@final"
515+
"is_explicit_override", # Uses "@override"
515516
"_fullname",
516517
)
517518

@@ -529,6 +530,7 @@ def __init__(self) -> None:
529530
self.is_class = False
530531
self.is_static = False
531532
self.is_final = False
533+
self.is_explicit_override = False
532534
# Name with module prefix
533535
self._fullname = ""
534536

@@ -747,7 +749,6 @@ class FuncDef(FuncItem, SymbolNode, Statement):
747749
"is_mypy_only",
748750
# Present only when a function is decorated with @typing.datasclass_transform or similar
749751
"dataclass_transform_spec",
750-
"is_explicit_override",
751752
)
752753

753754
__match_args__ = ("name", "arguments", "type", "body")
@@ -776,8 +777,6 @@ def __init__(
776777
# Definitions that appear in if TYPE_CHECKING are marked with this flag.
777778
self.is_mypy_only = False
778779
self.dataclass_transform_spec: DataclassTransformSpec | None = None
779-
# Decorated with @override
780-
self.is_explicit_override = False
781780

782781
@property
783782
def name(self) -> str:

mypy/semanal.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,6 +1197,9 @@ def analyze_overload_sigs_and_impl(
11971197
types.append(callable)
11981198
if item.var.is_property:
11991199
self.fail("An overload can not be a property", item)
1200+
# If any item was decorated with `@override`, the whole overload
1201+
# becomes an explicit override.
1202+
defn.is_explicit_override |= item.func.is_explicit_override
12001203
elif isinstance(item, FuncDef):
12011204
if i == len(defn.items) - 1 and not self.is_stub_file:
12021205
impl = item

test-data/unit/check-functions.test

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2852,6 +2852,40 @@ class D(A):
28522852
[builtins fixtures/property.pyi]
28532853
[typing fixtures/typing-full.pyi]
28542854

2855+
[case explicitOverrideSettableProperty]
2856+
# flags: --python-version 3.12
2857+
from typing import override
2858+
2859+
class A:
2860+
@property
2861+
def f(self) -> str: pass
2862+
2863+
@f.setter
2864+
def f(self, value: str) -> None: pass
2865+
2866+
class B(A):
2867+
@property # E: Read-only property cannot override read-write property
2868+
@override
2869+
def f(self) -> str: pass
2870+
2871+
class C(A):
2872+
@override
2873+
@property
2874+
def f(self) -> str: pass
2875+
2876+
@f.setter
2877+
def f(self, value: str) -> None: pass
2878+
2879+
class D(A):
2880+
@override # E: Signature of "f" incompatible with supertype "A"
2881+
@property
2882+
def f(self) -> int: pass
2883+
2884+
@f.setter
2885+
def f(self, value: int) -> None: pass
2886+
[builtins fixtures/property.pyi]
2887+
[typing fixtures/typing-full.pyi]
2888+
28552889
[case invalidExplicitOverride]
28562890
# flags: --python-version 3.12
28572891
from typing import override
@@ -2896,3 +2930,72 @@ class B(A):
28962930
def f2(self, x: int) -> str: pass # E: Method "f2" is marked as an override, but no base method was found with this name
28972931
[typing fixtures/typing-full.pyi]
28982932
[builtins fixtures/tuple.pyi]
2933+
2934+
[case explicitOverrideOverloads]
2935+
# flags: --python-version 3.12
2936+
from typing import overload, override
2937+
2938+
class A:
2939+
def f(self, x: int) -> str: pass
2940+
2941+
class B(A):
2942+
@overload # E: Method "f2" is marked as an override, but no base method was found with this name
2943+
def f2(self, x: int) -> str: pass
2944+
@overload
2945+
def f2(self, x: str) -> str: pass
2946+
@override
2947+
def f2(self, x: int | str) -> str: pass
2948+
[typing fixtures/typing-full.pyi]
2949+
[builtins fixtures/tuple.pyi]
2950+
2951+
[case explicitOverrideNotOnOverloadsImplementation]
2952+
# flags: --python-version 3.12
2953+
from typing import overload, override
2954+
2955+
class A:
2956+
def f(self, x: int) -> str: pass
2957+
2958+
class B(A):
2959+
@overload # E: Method "f2" is marked as an override, but no base method was found with this name
2960+
def f2(self, x: int) -> str: pass
2961+
@override
2962+
@overload
2963+
def f2(self, x: str) -> str: pass
2964+
def f2(self, x: int | str) -> str: pass
2965+
2966+
class C(A):
2967+
@overload
2968+
def f(self, y: int) -> str: pass
2969+
@override
2970+
@overload
2971+
def f(self, y: str) -> str: pass
2972+
def f(self, y: int | str) -> str: pass
2973+
[typing fixtures/typing-full.pyi]
2974+
[builtins fixtures/tuple.pyi]
2975+
2976+
[case explicitOverrideOnMultipleOverloads]
2977+
# flags: --python-version 3.12
2978+
from typing import overload, override
2979+
2980+
class A:
2981+
def f(self, x: int) -> str: pass
2982+
2983+
class B(A):
2984+
@override # E: Method "f2" is marked as an override, but no base method was found with this name
2985+
@overload
2986+
def f2(self, x: int) -> str: pass
2987+
@override
2988+
@overload
2989+
def f2(self, x: str) -> str: pass
2990+
def f2(self, x: int | str) -> str: pass
2991+
2992+
class C(A):
2993+
@overload
2994+
def f(self, y: int) -> str: pass
2995+
@override
2996+
@overload
2997+
def f(self, y: str) -> str: pass
2998+
@override
2999+
def f(self, y: int | str) -> str: pass
3000+
[typing fixtures/typing-full.pyi]
3001+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)