Skip to content

Commit

Permalink
Added @override enforcement for an overloaded method that has no im…
Browse files Browse the repository at this point in the history
…plementation. This addresses #9746.
  • Loading branch information
erictraut committed Jan 23, 2025
1 parent ce4e143 commit 426f294
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 22 deletions.
8 changes: 8 additions & 0 deletions packages/pyright-internal/src/analyzer/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6562,6 +6562,14 @@ export class Checker extends ParseTreeWalker {
if (impl && isFunction(impl)) {
overrideFunction = impl;
}

// If there is no implementation present, use the first overload.
if (!impl) {
const overloads = OverloadedType.getOverloads(overrideType);
if (overloads.length > 0) {
overrideFunction = overloads[0];
}
}
} else if (isClassInstance(overrideType) && ClassType.isPropertyClass(overrideType)) {
if (overrideType.priv.fgetInfo) {
overrideFunction = overrideType.priv.fgetInfo.methodType;
Expand Down
52 changes: 31 additions & 21 deletions packages/pyright-internal/src/tests/samples/override1.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# This sample tests the handling of the @override decorator as described
# in PEP 698.

from typing import Callable
from typing import Callable, Protocol
from typing_extensions import ( # pyright: ignore[reportMissingModuleSource]
Any,
overload,
Expand All @@ -19,15 +19,12 @@ def method3(self) -> None:
pass

@overload
def method5(self, x: int) -> int:
...
def method5(self, x: int) -> int: ...

@overload
def method5(self, x: str) -> str:
...
def method5(self, x: str) -> str: ...

def method5(self, x: int | str) -> int | str:
...
def method5(self, x: int | str) -> int | str: ...


class ClassC(ClassA, ClassB):
Expand Down Expand Up @@ -56,34 +53,27 @@ def method4(self) -> None:
pass

@overload
def method5(self, x: int) -> int:
...
def method5(self, x: int) -> int: ...

@overload
def method5(self, x: str) -> str:
...
def method5(self, x: str) -> str: ...

@override
def method5(self, x: int | str) -> int | str:
...
def method5(self, x: int | str) -> int | str: ...

@overload
def method6(self, x: int) -> int:
...
def method6(self, x: int) -> int: ...

@overload
def method6(self, x: str) -> str:
...
def method6(self, x: str) -> str: ...

@override
# This should generate an error because method6 does not
# override anything in a base class.
def method6(self, x: int | str) -> int | str:
...
def method6(self, x: int | str) -> int | str: ...


class ClassD(Any):
...
class ClassD(Any): ...


class ClassE(ClassD):
Expand All @@ -109,3 +99,23 @@ class G(F):
@evil_wrapper
def method1(self):
pass


class H(Protocol):
pass


class I(H, Protocol):
@override
# This should generate an error because method1 isn't present
# in the base.
def method1(self):
pass

@overload
@override
# This should generate an error because method2 isn't present
# in the base.
def method2(self, x: int) -> int: ...
@overload
def method2(self, x: str) -> str: ...
2 changes: 1 addition & 1 deletion packages/pyright-internal/src/tests/typeEvaluator5.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ test('Hashability3', () => {

test('Override1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['override1.py']);
TestUtils.validateResults(analysisResults, 3);
TestUtils.validateResults(analysisResults, 5);
});

test('Override2', () => {
Expand Down

0 comments on commit 426f294

Please sign in to comment.