Skip to content

Commit

Permalink
Add support for overloads with @OverRide
Browse files Browse the repository at this point in the history
  • Loading branch information
tmke8 committed May 8, 2023
1 parent f949dd4 commit 192bc85
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 4 deletions.
4 changes: 3 additions & 1 deletion mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,9 @@ def _visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
if defn.impl:
defn.impl.accept(self)
if defn.info:
self.check_method_override(defn)
found_base_method = self.check_method_override(defn)
if defn.is_explicit_override and found_base_method is False:
self.msg.no_overridable_method(defn.name, defn)
self.check_inplace_operator_method(defn)
if not defn.is_property:
self.check_overlapping_overloads(defn)
Expand Down
5 changes: 2 additions & 3 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,7 @@ class FuncBase(Node):
"is_class", # Uses "@classmethod" (explicit or implicit)
"is_static", # Uses "@staticmethod"
"is_final", # Uses "@final"
"is_explicit_override", # Uses "@override"
"_fullname",
)

Expand All @@ -529,6 +530,7 @@ def __init__(self) -> None:
self.is_class = False
self.is_static = False
self.is_final = False
self.is_explicit_override = False
# Name with module prefix
self._fullname = ""

Expand Down Expand Up @@ -747,7 +749,6 @@ class FuncDef(FuncItem, SymbolNode, Statement):
"is_mypy_only",
# Present only when a function is decorated with @typing.datasclass_transform or similar
"dataclass_transform_spec",
"is_explicit_override",
)

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

@property
def name(self) -> str:
Expand Down
3 changes: 3 additions & 0 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -1197,6 +1197,9 @@ def analyze_overload_sigs_and_impl(
types.append(callable)
if item.var.is_property:
self.fail("An overload can not be a property", item)
# If any item was decorated with `@override`, the whole overload
# becomes an explicit override.
defn.is_explicit_override |= item.func.is_explicit_override
elif isinstance(item, FuncDef):
if i == len(defn.items) - 1 and not self.is_stub_file:
impl = item
Expand Down
103 changes: 103 additions & 0 deletions test-data/unit/check-functions.test
Original file line number Diff line number Diff line change
Expand Up @@ -2852,6 +2852,40 @@ class D(A):
[builtins fixtures/property.pyi]
[typing fixtures/typing-full.pyi]

[case explicitOverrideSettableProperty]
# flags: --python-version 3.12
from typing import override

class A:
@property
def f(self) -> str: pass

@f.setter
def f(self, value: str) -> None: pass

class B(A):
@property # E: Read-only property cannot override read-write property
@override
def f(self) -> str: pass

class C(A):
@override
@property
def f(self) -> str: pass

@f.setter
def f(self, value: str) -> None: pass

class D(A):
@override # E: Signature of "f" incompatible with supertype "A"
@property
def f(self) -> int: pass

@f.setter
def f(self, value: int) -> None: pass
[builtins fixtures/property.pyi]
[typing fixtures/typing-full.pyi]

[case invalidExplicitOverride]
# flags: --python-version 3.12
from typing import override
Expand Down Expand Up @@ -2896,3 +2930,72 @@ class B(A):
def f2(self, x: int) -> str: pass # E: Method "f2" is marked as an override, but no base method was found with this name
[typing fixtures/typing-full.pyi]
[builtins fixtures/tuple.pyi]

[case explicitOverrideOverloads]
# flags: --python-version 3.12
from typing import overload, override

class A:
def f(self, x: int) -> str: pass

class B(A):
@overload # E: Method "f2" is marked as an override, but no base method was found with this name
def f2(self, x: int) -> str: pass
@overload
def f2(self, x: str) -> str: pass
@override
def f2(self, x: int | str) -> str: pass
[typing fixtures/typing-full.pyi]
[builtins fixtures/tuple.pyi]

[case explicitOverrideNotOnOverloadsImplementation]
# flags: --python-version 3.12
from typing import overload, override

class A:
def f(self, x: int) -> str: pass

class B(A):
@overload # E: Method "f2" is marked as an override, but no base method was found with this name
def f2(self, x: int) -> str: pass
@override
@overload
def f2(self, x: str) -> str: pass
def f2(self, x: int | str) -> str: pass

class C(A):
@overload
def f(self, y: int) -> str: pass
@override
@overload
def f(self, y: str) -> str: pass
def f(self, y: int | str) -> str: pass
[typing fixtures/typing-full.pyi]
[builtins fixtures/tuple.pyi]

[case explicitOverrideOnMultipleOverloads]
# flags: --python-version 3.12
from typing import overload, override

class A:
def f(self, x: int) -> str: pass

class B(A):
@override # E: Method "f2" is marked as an override, but no base method was found with this name
@overload
def f2(self, x: int) -> str: pass
@override
@overload
def f2(self, x: str) -> str: pass
def f2(self, x: int | str) -> str: pass

class C(A):
@overload
def f(self, y: int) -> str: pass
@override
@overload
def f(self, y: str) -> str: pass
@override
def f(self, y: int | str) -> str: pass
[typing fixtures/typing-full.pyi]
[builtins fixtures/tuple.pyi]

0 comments on commit 192bc85

Please sign in to comment.