Fixed a bug that results in a false negative when a class explicitly inherits from a protocol that defines an instance variable but the child re-declares as a ClassVar without an explicit type. This addresses #7455. (#7463)

This commit is contained in:
Eric Traut 2024-03-12 00:29:01 -06:00 committed by GitHub
parent 06bc912e38
commit 3ccd7b87b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 37 additions and 33 deletions

View File

@ -6132,9 +6132,11 @@ export class Checker extends ParseTreeWalker {
}
// If the symbol has no declaration, and the type is inferred,
// skip this check.
if (!symbol.hasTypedDeclarations() && !this._evaluator.isFinalVariable(symbol)) {
return;
// skip the type validation but still check for other issues like
// Final overrides and class/instance variable mismatches.
let validateType = true;
if (!symbol.hasTypedDeclarations()) {
validateType = false;
}
// Get the symbol type defined in this class.
@ -6169,7 +6171,13 @@ export class Checker extends ParseTreeWalker {
firstOverride = firstOverride ?? baseClassAndSymbol;
this._validateBaseClassOverride(baseClassAndSymbol, symbol, typeOfSymbol, classType, name);
this._validateBaseClassOverride(
baseClassAndSymbol,
symbol,
validateType ? typeOfSymbol : AnyType.create(),
classType,
name
);
}
if (!firstOverride) {

View File

@ -110,14 +110,12 @@ _Self = TypeVar("_Self")
class Class5:
@property
def real(self: _Self) -> _Self:
...
def real(self: _Self) -> _Self: ...
class MockClass5(Protocol[_T_co]):
@property
def real(self) -> _T_co:
...
def real(self) -> _T_co: ...
foo5 = Class5()
@ -130,14 +128,12 @@ C6 = TypeVar("C6", bound="Class6")
class MockClass6(Protocol):
@property
def bar(self: P6) -> ContextManager[P6]:
...
def bar(self: P6) -> ContextManager[P6]: ...
class Class6:
@property
def bar(self: C6) -> ContextManager[C6]:
...
def bar(self: C6) -> ContextManager[C6]: ...
i: MockClass6 = Class6()
@ -158,8 +154,7 @@ a: Proto7 = Class7("")
class Proto8(Protocol):
@property
def x(self) -> str:
...
def x(self) -> str: ...
class Class8(NamedTuple):
@ -171,12 +166,10 @@ b: Proto8 = Class8("")
class Proto9(Protocol):
@property
def x(self) -> str:
...
def x(self) -> str: ...
@x.setter
def x(self, n: str) -> None:
...
def x(self, n: str) -> None: ...
class Proto10(Protocol):
@ -231,11 +224,11 @@ p11_1: Proto11 = Concrete11()
class Proto12(Protocol):
val1: Sequence[int]
val1: list[int]
class Concrete12:
val1: ClassVar[Sequence[int]]
val1: ClassVar = [1, 2, 3]
# This should generate an error because of a ClassVar mismatch.
@ -255,12 +248,10 @@ T13 = TypeVar("T13", covariant=True)
class Proto13(Protocol[T13]):
@property
def prop1(self) -> T13:
...
def prop1(self) -> T13: ...
class Proto14(Proto13[T13], Protocol):
...
class Proto14(Proto13[T13], Protocol): ...
class Concrete14(Generic[T13]):
@ -268,8 +259,7 @@ class Concrete14(Generic[T13]):
self.prop1 = val
def func14(val: Proto14[T13]):
...
def func14(val: Proto14[T13]): ...
func14(Concrete14(1))

View File

@ -26,8 +26,7 @@ class Protocol3(Protocol2, Protocol):
cm11: int
class Concrete1(Protocol1):
...
class Concrete1(Protocol1): ...
# This should generate an error because some attributes are not implemented.
@ -67,8 +66,7 @@ Concrete4()
class Protocol5(Protocol):
def method1(self) -> int:
...
def method1(self) -> int: ...
# This should generate an error because "method1" is
@ -93,8 +91,7 @@ class Concrete6(Mixin, Protocol6):
class Protocol7(Protocol):
@abstractmethod
def method1(self):
...
def method1(self): ...
class Mixin7(Protocol7, ABC):
@ -112,3 +109,12 @@ class Concrete7A(Protocol7):
@final
class Concrete7B(Mixin7, Protocol7):
pass
class Protocol8(Protocol):
x: int
class Concrete8(Protocol8):
# This should generate an error because x is a ClassVar.
x: ClassVar = 1

View File

@ -1414,7 +1414,7 @@ test('Protocol48', () => {
test('ProtocolExplicit1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['protocolExplicit1.py']);
TestUtils.validateResults(analysisResults, 4);
TestUtils.validateResults(analysisResults, 5);
});
test('ProtocolExplicit3', () => {