Fixed false positive that occurs when overriding an abstract method (e.g. a property) with a field from a named tuple when both are used as base classes.

This commit is contained in:
Eric Traut 2022-06-21 13:27:59 -07:00
parent 44b813c761
commit 50d56824c0
5 changed files with 45 additions and 2 deletions

View File

@ -264,7 +264,10 @@ export function createNamedTupleType(
entryTypes.push(entryType);
matchArgsNames.push(entryName);
const newSymbol = Symbol.createWithType(SymbolFlags.InstanceMember, entryType);
const newSymbol = Symbol.createWithType(
SymbolFlags.InstanceMember | SymbolFlags.NamedTupleMember,
entryType
);
if (entryNameNode && entryNameNode.nodeType === ParseNodeType.StringList) {
const declaration: VariableDeclaration = {
type: DeclarationType.Variable,

View File

@ -52,6 +52,11 @@ export const enum SymbolFlags {
// Indicates that the symbol is an InitVar as specified in PEP 557.
InitVar = 1 << 10,
// Indicates that the symbol is a field in a NamedTuple class, which
// is modeled as an instance member but in some respects acts as a
// class member.
NamedTupleMember = 1 << 11,
}
let nextSymbolId = 1;
@ -167,6 +172,10 @@ export class Symbol {
return !!(this._flags & SymbolFlags.PrivatePyTypedImport);
}
isNamedTupleMemberMember() {
return !!(this._flags & SymbolFlags.NamedTupleMember);
}
addDeclaration(declaration: Declaration) {
if (this._declarations) {
// See if this node was already identified as a declaration. If so,

View File

@ -22986,7 +22986,11 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
// We do a quick-and-dirty evaluation of methods based on
// decorators to determine which ones are abstract. This allows
// us to avoid evaluating the full function types.
if (symbol.isClassMember()) {
// Handle named tuple fields as though they are class members here.
// We model them as instance variables, but they're actually implemented
// using class variable descriptors, and these overwrite a class variable
// abstract method.
if (symbol.isClassMember() || symbol.isNamedTupleMemberMember()) {
let isAbstract: boolean;
const decl = getLastTypedDeclaredForSymbol(symbol);

View File

@ -107,6 +107,12 @@ test('AbstractClass8', () => {
TestUtils.validateResults(analysisResults, 1);
});
test('AbstractClass9', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['abstractClass9.py']);
TestUtils.validateResults(analysisResults, 0);
});
test('Constants1', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['constants1.py']);

View File

@ -0,0 +1,21 @@
# This sample tests that named tuple fields override abstract methods.
from abc import ABC, abstractmethod
from typing import NamedTuple
class ClassA(ABC):
@property
@abstractmethod
def myproperty(self) -> str:
...
MixinB = NamedTuple("MixinB", [("myproperty", str)])
class ClassB(MixinB, ClassA):
pass
ClassB(myproperty="myproperty")