Skip to content

Commit

Permalink
Fixed bug that results in a spurious "incompatible method override" e…
Browse files Browse the repository at this point in the history
…rror when two subclasses with an overloaded method are combined using multiple inheritance. This addresses #9625. (#9700)
  • Loading branch information
erictraut authored Jan 14, 2025
1 parent 4d8b903 commit 1593a7c
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 78 deletions.
22 changes: 3 additions & 19 deletions packages/pyright-internal/src/analyzer/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5892,34 +5892,18 @@ export class Checker extends ParseTreeWalker {

if (isFunction(overriddenType) || isOverloaded(overriddenType)) {
const diagAddendum = new DiagnosticAddendum();
let overrideFunction: FunctionType | undefined;

if (isFunction(overrideType)) {
overrideFunction = overrideType;
} else if (isOverloaded(overrideType)) {
// Use the last overload.
const impl = OverloadedType.getImplementation(overrideType);

// If the last overload isn't an implementation, skip the check for this symbol.
if (!impl || !isFunction(impl)) {
return;
}

overrideFunction = impl;
}

if (overrideFunction) {
if (isFunction(overrideType) || isOverloaded(overrideType)) {
if (
!this._evaluator.validateOverrideMethod(
overriddenType,
overrideFunction,
overrideType,
/* baseClass */ undefined,
diagAddendum,
/* enforceParamNameMatch */ true
)
) {
const decl = overrideFunction.shared.declaration;
if (decl && decl.type === DeclarationType.Function) {
if (overrideDecl && overrideDecl.type === DeclarationType.Function) {
diag = this._evaluator.addDiagnostic(
DiagnosticRule.reportIncompatibleMethodOverride,
LocMessage.baseClassMethodTypeIncompatible().format({
Expand Down
114 changes: 55 additions & 59 deletions packages/pyright-internal/src/tests/samples/methodOverride3.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,100 +3,82 @@
# diagnostic rule.


from typing import Generic, Iterable, ParamSpec, TypeVar
from typing import Generic, Iterable, ParamSpec, TypeVar, overload


class A1:
def func1(self, a: int) -> str:
...
def func1(self, a: int) -> str: ...


class A2:
def func1(self, a: int, b: int = 3) -> str:
...
def func1(self, a: int, b: int = 3) -> str: ...


# This should generate an error because func1 is incompatible.
class ASub(A1, A2):
...
class ASub(A1, A2): ...


class B1:
def func1(self) -> int:
...
def func1(self) -> int: ...


class B2:
def func1(self) -> float:
...
def func1(self) -> float: ...


class BSub(B1, B2):
...
class BSub(B1, B2): ...


class C1:
def func1(self) -> float:
...
def func1(self) -> float: ...


class C2:
def func1(self) -> int:
...
def func1(self) -> int: ...


# This should generate an error because func1 is incompatible.
class CSub(C1, C2):
...
class CSub(C1, C2): ...


class D1:
def func1(self, a: int) -> None:
...
def func1(self, a: int) -> None: ...


class D2:
def func1(self, b: int) -> None:
...
def func1(self, b: int) -> None: ...


# This should generate an error because func1 is incompatible.
class DSub(D1, D2):
...
class DSub(D1, D2): ...


_T_E = TypeVar("_T_E")


class E1(Generic[_T_E]):
def func1(self, a: _T_E) -> None:
...
def func1(self, a: _T_E) -> None: ...


class E2(Generic[_T_E]):
def func1(self, a: _T_E) -> None:
...
def func1(self, a: _T_E) -> None: ...


class ESub(E1[int], E2[int]):
...
class ESub(E1[int], E2[int]): ...


_T_F = TypeVar("_T_F")


class F1(Generic[_T_F]):
def do_stuff(self) -> Iterable[_T_F]:
...
def do_stuff(self) -> Iterable[_T_F]: ...


class F2(F1[_T_F]):
def do_stuff(self) -> Iterable[_T_F]:
...
def do_stuff(self) -> Iterable[_T_F]: ...


class F3(F1[_T_F]):
...
class F3(F1[_T_F]): ...


class FSub1(F3[int], F2[int]):
Expand All @@ -116,46 +98,36 @@ class FSub3(F2[int], F1[int]):


class G1(Generic[_P, _R]):
def f(self, *args: _P.args, **kwargs: _P.kwargs) -> _R:
...
def f(self, *args: _P.args, **kwargs: _P.kwargs) -> _R: ...

def g(self) -> _R:
...
def g(self) -> _R: ...


class G2(G1[_P, _R]):
# This should generate an error because f is missing ParamSpec parameters.
def f(self) -> _R:
...
def f(self) -> _R: ...

def g(self, *args: _P.args, **kwargs: _P.kwargs) -> _R:
...
def g(self, *args: _P.args, **kwargs: _P.kwargs) -> _R: ...


class G3(G1[[], _R]):
def f(self) -> _R:
...
def f(self) -> _R: ...

def g(self) -> _R:
...
def g(self) -> _R: ...


class G4(G1[[int, int], str]):
def f(self, a: int, b: int, /) -> str:
...
def f(self, a: int, b: int, /) -> str: ...

def g(self) -> str:
...
def g(self) -> str: ...


class G5(G1[[], str]):
# This should generate an error because the specialized
# signature of f in the base class has no positional parameters.
def f(self, a: int, b: int) -> str:
...
def f(self, a: int, b: int) -> str: ...

def g(self) -> str:
...
def g(self) -> str: ...


class H1:
Expand Down Expand Up @@ -199,5 +171,29 @@ def prop3(self, val: str) -> None:


# This should generate three errors: prop1, prop2 and prop3.
class H(H2, H1):
...
class H(H2, H1): ...


class I1:
@overload
def func1(self, x: int) -> int: ...

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

def func1(self, x: int | str) -> int | str:
return x


class I2:
@overload
def func1(self, x: int) -> int: ...

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

def func1(self, x: int | str) -> int | str:
return x


class I(I1, I2): ...

0 comments on commit 1593a7c

Please sign in to comment.