mirror of
https://github.com/microsoft/pyright.git
synced 2024-07-14 11:00:25 +03:00
Improved reportUninitializedInstanceVariable
check to handle dataclass variables that are initialized implicitly by the synthesized __init__
method.
This commit is contained in:
parent
3e6661a315
commit
ee12ebcccf
@ -95,6 +95,7 @@ import { OperatorType, StringTokenFlags, TokenType } from '../parser/tokenizerTy
|
|||||||
import { AnalyzerFileInfo } from './analyzerFileInfo';
|
import { AnalyzerFileInfo } from './analyzerFileInfo';
|
||||||
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
|
import * as AnalyzerNodeInfo from './analyzerNodeInfo';
|
||||||
import { getBoundCallMethod, getBoundInitMethod, getBoundNewMethod } from './constructors';
|
import { getBoundCallMethod, getBoundInitMethod, getBoundNewMethod } from './constructors';
|
||||||
|
import { addInheritedDataClassEntries } from './dataClasses';
|
||||||
import { Declaration, DeclarationType, isAliasDeclaration } from './declaration';
|
import { Declaration, DeclarationType, isAliasDeclaration } from './declaration';
|
||||||
import { getNameNodeForDeclaration } from './declarationUtils';
|
import { getNameNodeForDeclaration } from './declarationUtils';
|
||||||
import { deprecatedAliases, deprecatedSpecialForms } from './deprecatedSymbols';
|
import { deprecatedAliases, deprecatedSpecialForms } from './deprecatedSymbols';
|
||||||
@ -165,6 +166,7 @@ import {
|
|||||||
AnyType,
|
AnyType,
|
||||||
ClassType,
|
ClassType,
|
||||||
ClassTypeFlags,
|
ClassTypeFlags,
|
||||||
|
DataClassEntry,
|
||||||
EnumLiteral,
|
EnumLiteral,
|
||||||
FunctionType,
|
FunctionType,
|
||||||
FunctionTypeFlags,
|
FunctionTypeFlags,
|
||||||
@ -5294,6 +5296,13 @@ export class Checker extends ParseTreeWalker {
|
|||||||
getProtocolSymbolsRecursive(classType, abstractSymbols, ClassTypeFlags.SupportsAbstractMethods);
|
getProtocolSymbolsRecursive(classType, abstractSymbols, ClassTypeFlags.SupportsAbstractMethods);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is a dataclass, get all of the entries so we can tell which
|
||||||
|
// ones are initialized by the synthesized __init__ method.
|
||||||
|
const dataClassEntries: DataClassEntry[] = [];
|
||||||
|
if (ClassType.isDataClass(classType)) {
|
||||||
|
addInheritedDataClassEntries(classType, dataClassEntries);
|
||||||
|
}
|
||||||
|
|
||||||
ClassType.getSymbolTable(classType).forEach((localSymbol, name) => {
|
ClassType.getSymbolTable(classType).forEach((localSymbol, name) => {
|
||||||
abstractSymbols.delete(name);
|
abstractSymbols.delete(name);
|
||||||
|
|
||||||
@ -5376,20 +5385,30 @@ export class Checker extends ParseTreeWalker {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decls[0].type === DeclarationType.Variable) {
|
if (decls[0].type !== DeclarationType.Variable) {
|
||||||
// If none of the declarations involve assignments, assume it's
|
return;
|
||||||
// not implemented in the protocol.
|
}
|
||||||
if (!decls.some((decl) => decl.type === DeclarationType.Variable && !!decl.inferredTypeSource)) {
|
|
||||||
// This is a variable declaration that is not implemented in the
|
// Dataclass fields are typically exempted from this check because
|
||||||
// protocol base class. Make sure it's implemented in the derived class.
|
// they have synthesized __init__ methods that initialize these variables.
|
||||||
diagAddendum.addMessage(
|
const dcEntry = dataClassEntries?.find((entry) => entry.name === name);
|
||||||
LocAddendum.uninitializedAbstractVariable().format({
|
if (dcEntry) {
|
||||||
name,
|
if (dcEntry.includeInInit) {
|
||||||
classType: member.classType.details.name,
|
return;
|
||||||
})
|
}
|
||||||
);
|
} else {
|
||||||
|
// Do one or more declarations involve assignments?
|
||||||
|
if (decls.some((decl) => decl.type === DeclarationType.Variable && !!decl.inferredTypeSource)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diagAddendum.addMessage(
|
||||||
|
LocAddendum.uninitializedAbstractVariable().format({
|
||||||
|
name,
|
||||||
|
classType: member.classType.details.name,
|
||||||
|
})
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!diagAddendum.isEmpty()) {
|
if (!diagAddendum.isEmpty()) {
|
||||||
|
@ -995,7 +995,7 @@ function transformDescriptorType(evaluator: TypeEvaluator, type: Type): Type {
|
|||||||
// the specified class. These entries must be unique and in reverse-MRO
|
// the specified class. These entries must be unique and in reverse-MRO
|
||||||
// order. Returns true if all of the class types in the hierarchy are
|
// order. Returns true if all of the class types in the hierarchy are
|
||||||
// known, false if one or more are unknown.
|
// known, false if one or more are unknown.
|
||||||
function addInheritedDataClassEntries(classType: ClassType, entries: DataClassEntry[]) {
|
export function addInheritedDataClassEntries(classType: ClassType, entries: DataClassEntry[]) {
|
||||||
let allAncestorsAreKnown = true;
|
let allAncestorsAreKnown = true;
|
||||||
|
|
||||||
ClassType.getReverseMro(classType).forEach((mroClass) => {
|
ClassType.getReverseMro(classType).forEach((mroClass) => {
|
||||||
|
@ -493,7 +493,7 @@ test('UninitializedVariable2', () => {
|
|||||||
// Enable it as an error.
|
// Enable it as an error.
|
||||||
configOptions.diagnosticRuleSet.reportUninitializedInstanceVariable = 'error';
|
configOptions.diagnosticRuleSet.reportUninitializedInstanceVariable = 'error';
|
||||||
analysisResults = TestUtils.typeAnalyzeSampleFiles(['uninitializedVariable2.py'], configOptions);
|
analysisResults = TestUtils.typeAnalyzeSampleFiles(['uninitializedVariable2.py'], configOptions);
|
||||||
TestUtils.validateResults(analysisResults, 2);
|
TestUtils.validateResults(analysisResults, 3);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Deprecated1', () => {
|
test('Deprecated1', () => {
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
# to a concrete implementation of an abstract base class that defines
|
# to a concrete implementation of an abstract base class that defines
|
||||||
# (but does not assign) variables.
|
# (but does not assign) variables.
|
||||||
|
|
||||||
from abc import ABC
|
from abc import ABC, abstractmethod
|
||||||
|
from dataclasses import dataclass, field
|
||||||
from typing import NamedTuple, final
|
from typing import NamedTuple, final
|
||||||
|
|
||||||
|
|
||||||
@ -52,3 +53,16 @@ class G(Abstract3):
|
|||||||
|
|
||||||
class H(NamedTuple):
|
class H(NamedTuple):
|
||||||
x: int
|
x: int
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class IAbstract(ABC):
|
||||||
|
p1: str
|
||||||
|
p2: int = field(init=False)
|
||||||
|
|
||||||
|
|
||||||
|
@final
|
||||||
|
@dataclass
|
||||||
|
# This should generate an error because p2 is uninitialized.
|
||||||
|
class I(IAbstract):
|
||||||
|
p3: int
|
||||||
|
Loading…
Reference in New Issue
Block a user