mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 16:06:39 +03:00
Fixed a false positive error introduced with abstract class instantiation test.
This commit is contained in:
parent
138abaa2dd
commit
f58ee2b8f4
@ -726,6 +726,7 @@ export class ExpressionEvaluator {
|
||||
flags &= ~EvaluatorFlags.ConvertClassToObject;
|
||||
}
|
||||
} else if (callType.isAbstractClass()) {
|
||||
// If the class is abstract, it can't be instantiated.
|
||||
const symbolTable = new SymbolTable();
|
||||
TypeUtils.getAbstractMethodsRecursive(callType, symbolTable);
|
||||
|
||||
|
@ -269,23 +269,25 @@ export class TypeAnalyzer extends ParseTreeWalker {
|
||||
} else if (index === 0 && (functionType.isInstanceMethod() || functionType.isClassMethod())) {
|
||||
// Specify type of "self" or "cls" parameter for instance or class methods
|
||||
// if the type is not explicitly provided.
|
||||
const classNode = ParseTreeUtils.getEnclosingClass(node);
|
||||
if (classNode) {
|
||||
const paramType = functionType.getParameters()[index].type;
|
||||
if (containingClassType) {
|
||||
const paramType = functionType.getParameters()[0].type;
|
||||
|
||||
if (paramType instanceof UnknownType) {
|
||||
const inferredClassType = AnalyzerNodeInfo.getExpressionType(classNode) as ClassType;
|
||||
|
||||
// Don't specialize the "self" for protocol classes because type
|
||||
// comparisons will fail during structural typing analysis.
|
||||
if (inferredClassType && !inferredClassType.isProtocol()) {
|
||||
const specializedClassType = TypeUtils.selfSpecializeClassType(inferredClassType);
|
||||
|
||||
if (containingClassType && !containingClassType.isProtocol()) {
|
||||
if (functionType.isInstanceMethod()) {
|
||||
const specializedClassType = TypeUtils.selfSpecializeClassType(
|
||||
containingClassType);
|
||||
if (functionType.setParameterType(index, new ObjectType(specializedClassType))) {
|
||||
this._setAnalysisChanged();
|
||||
}
|
||||
} else if (functionType.isClassMethod()) {
|
||||
// For class methods, the cls parameter is allowed to skip the
|
||||
// abstract class test because the caller is possibly passing
|
||||
// in a non-abstract subclass.
|
||||
const specializedClassType = TypeUtils.selfSpecializeClassType(
|
||||
containingClassType, true);
|
||||
if (functionType.setParameterType(index, specializedClassType)) {
|
||||
this._setAnalysisChanged();
|
||||
}
|
||||
|
@ -989,13 +989,13 @@ export class TypeUtils {
|
||||
// If the class is generic, the type is cloned, and its own
|
||||
// type parameters are used as type arguments. This is useful
|
||||
// for typing "self" or "cls" within a class's implementation.
|
||||
static selfSpecializeClassType(type: ClassType): ClassType {
|
||||
if (!type.isGeneric()) {
|
||||
static selfSpecializeClassType(type: ClassType, skipAbstractClassTest = false): ClassType {
|
||||
if (!type.isGeneric() && !skipAbstractClassTest) {
|
||||
return type;
|
||||
}
|
||||
|
||||
let typeArgs = type.getTypeParameters();
|
||||
return type.cloneForSpecialization(typeArgs);
|
||||
return type.cloneForSpecialization(typeArgs, skipAbstractClassTest);
|
||||
}
|
||||
|
||||
// Removes the first parameter of the function and returns a new function.
|
||||
|
@ -226,6 +226,8 @@ export class ClassType extends Type {
|
||||
// parameters are undefined.
|
||||
private _typeArguments?: Type[];
|
||||
|
||||
private _skipAbstractClassTest = false;
|
||||
|
||||
constructor(name: string, flags: ClassTypeFlags, typeSourceId: TypeSourceId) {
|
||||
super();
|
||||
|
||||
@ -242,11 +244,14 @@ export class ClassType extends Type {
|
||||
};
|
||||
}
|
||||
|
||||
cloneForSpecialization(typeArguments: Type[]): ClassType {
|
||||
cloneForSpecialization(typeArguments: Type[], skipAbstractClassTest = false): ClassType {
|
||||
let newClassType = new ClassType(this._classDetails.name,
|
||||
this._classDetails.flags, this._classDetails.typeSourceId);
|
||||
newClassType._classDetails = this._classDetails;
|
||||
newClassType.setTypeArguments(typeArguments);
|
||||
if (skipAbstractClassTest) {
|
||||
newClassType._setSkipAbstracClassTest();
|
||||
}
|
||||
return newClassType;
|
||||
}
|
||||
|
||||
@ -300,7 +305,8 @@ export class ClassType extends Type {
|
||||
}
|
||||
|
||||
isAbstractClass() {
|
||||
return this._classDetails.isAbstractClass;
|
||||
return this._classDetails.isAbstractClass &&
|
||||
!this._skipAbstractClassTest;
|
||||
}
|
||||
|
||||
getClassName() {
|
||||
@ -539,6 +545,10 @@ export class ClassType extends Type {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private _setSkipAbstracClassTest() {
|
||||
this._skipAbstractClassTest = true;
|
||||
}
|
||||
}
|
||||
|
||||
export class ObjectType extends Type {
|
||||
|
@ -15,6 +15,15 @@ class AbstractFoo(ABC):
|
||||
def foo3(self):
|
||||
return 3
|
||||
|
||||
@classmethod
|
||||
def foo4(cls):
|
||||
# This should not generate an error even though
|
||||
# it would appear to be attempting to instantiate
|
||||
# an abstract class. That's because we need to
|
||||
# assume that the caller is making this call on
|
||||
# a non-abstract subclass.
|
||||
return cls()
|
||||
|
||||
|
||||
# This should generate an error because AbstractFoo
|
||||
# is an abstract class.
|
||||
|
Loading…
Reference in New Issue
Block a user