Fixed a bug in bindFunctionToClassOrObject function that resulted in incorrect results when binding a class object to a method in its metaclass. Also renamed the treatConstructorAsClassMethod parameter for clarity.

This commit is contained in:
Eric Traut 2024-04-13 18:27:30 -07:00
parent 975c846895
commit 19183640d8
6 changed files with 23 additions and 16 deletions

View File

@ -837,7 +837,7 @@ function createFunctionFromNewMethod(
classType,
newSubtype,
newInfo && isInstantiableClass(newInfo.classType) ? newInfo.classType : undefined,
/* treatConstructorAsClassMember */ true,
/* treatConstructorAsClassMethod */ true,
selfType,
/* diag */ undefined,
recursionCount
@ -926,7 +926,7 @@ function createFunctionFromInitMethod(
objectType,
initSubtype,
initInfo && isInstantiableClass(initInfo.classType) ? initInfo.classType : undefined,
/* treatConstructorAsClassMember */ undefined,
/* treatConstructorAsClassMethod */ undefined,
selfType,
/* diag */ undefined,
recursionCount

View File

@ -545,7 +545,7 @@ export function assignProperty(
destObjectToBind,
destAccessType,
/* memberClass */ undefined,
/* treatConstructorAsClassMember */ undefined,
/* treatConstructorAsClassMethod */ undefined,
/* firstParamType */ undefined,
diag?.createAddendum(),
recursionCount
@ -555,7 +555,7 @@ export function assignProperty(
srcObjectToBind,
srcAccessType,
/* memberClass */ undefined,
/* treatConstructorAsClassMember */ undefined,
/* treatConstructorAsClassMethod */ undefined,
/* firstParamType */ undefined,
diag?.createAddendum(),
recursionCount

View File

@ -447,7 +447,7 @@ function assignClassToProtocolInternal(
: ClassType.cloneAsInstance(srcType),
srcMemberType,
isMemberFromMetaclass ? undefined : (srcMemberInfo.classType as ClassType),
/* treatConstructorAsClassMember */ undefined,
/* treatConstructorAsClassMethod */ undefined,
isMemberFromMetaclass ? srcType : selfType,
diag?.createAddendum(),
recursionCount
@ -493,7 +493,7 @@ function assignClassToProtocolInternal(
ClassType.cloneAsInstance(srcType),
destMemberType,
isMemberFromMetaclass ? undefined : (srcMemberInfo.classType as ClassType),
/* treatConstructorAsClassMember */ undefined,
/* treatConstructorAsClassMethod */ undefined,
isMemberFromMetaclass ? srcType : selfType,
diag,
recursionCount
@ -504,7 +504,7 @@ function assignClassToProtocolInternal(
ClassType.cloneAsInstance(destType),
destMemberType,
destType,
/* treatConstructorAsClassMember */ undefined,
/* treatConstructorAsClassMethod */ undefined,
/* firstParamType */ undefined,
diag,
recursionCount

View File

@ -26481,7 +26481,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
// If the memberType is an instance or class method, creates a new
// version of the function that has the "self" or "cls" parameter bound
// to it. If treatConstructorAsClassMember is true, the function is
// to it. If treatConstructorAsClassMethod is true, the function is
// treated like a class method even if it's not marked as such. That's
// needed to special-case the __new__ magic method when it's invoked as
// a constructor (as opposed to by name).
@ -26489,7 +26489,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
baseType: ClassType | undefined,
memberType: FunctionType | OverloadedFunctionType,
memberClass?: ClassType,
treatConstructorAsClassMember = false,
treatConstructorAsClassMethod = false,
selfType?: ClassType | TypeVarType,
diag?: DiagnosticAddendum,
recursionCount = 0
@ -26511,6 +26511,13 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
? baseType
: ClassType.cloneAsInstance(specializeClassType(baseType));
let stripFirstParam = false;
if (isClassInstance(baseType)) {
stripFirstParam = true;
} else if (memberClass && isInstantiableMetaclass(memberClass)) {
stripFirstParam = true;
}
return partiallySpecializeFunctionForBoundClassOrObject(
baseType,
functionType,
@ -26518,13 +26525,13 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
diag,
recursionCount,
selfType ?? baseObj,
/* stripFirstParam */ isClassInstance(baseType)
stripFirstParam
);
}
if (
FunctionType.isClassMethod(functionType) ||
(treatConstructorAsClassMember && FunctionType.isConstructorMethod(functionType))
(treatConstructorAsClassMethod && FunctionType.isConstructorMethod(functionType))
) {
const baseClass = isInstantiableClass(baseType) ? baseType : ClassType.cloneAsInstantiable(baseType);
const clsType = selfType ? (convertToInstantiable(selfType) as ClassType | TypeVarType) : undefined;

View File

@ -583,7 +583,7 @@ export interface TypeEvaluator {
baseType: ClassType | undefined,
memberType: FunctionType | OverloadedFunctionType,
memberClass?: ClassType,
treatConstructorAsClassMember?: boolean,
treatConstructorAsClassMethod?: boolean,
selfType?: ClassType | TypeVarType,
diag?: DiagnosticAddendum,
recursionCount?: number

View File

@ -442,13 +442,13 @@ export function getClassAndConstructorTypes(node: NameNode, evaluator: TypeEvalu
// Prefer `__new__` if it doesn't have default params (*args: Any, **kwargs: Any) or no params ().
if (isFunction(newMemberType) || isOverloadedFunction(newMemberType)) {
// Set `treatConstructorAsClassMember` to true to exclude `cls` as a parameter.
// Set `treatConstructorAsClassMethod` to true to exclude `cls` as a parameter.
methodType = bindFunctionToClassOrObjectToolTip(
evaluator,
node,
instanceType,
newMemberType,
/* treatConstructorAsClassMember */ true
/* treatConstructorAsClassMethod */ true
);
}
}
@ -462,13 +462,13 @@ export function bindFunctionToClassOrObjectToolTip(
node: ExpressionNode,
baseType: ClassType | undefined,
memberType: FunctionType | OverloadedFunctionType,
treatConstructorAsClassMember?: boolean
treatConstructorAsClassMethod?: boolean
): FunctionType | OverloadedFunctionType | undefined {
const methodType = evaluator.bindFunctionToClassOrObject(
baseType,
memberType,
/* memberClass */ undefined,
treatConstructorAsClassMember
treatConstructorAsClassMethod
);
if (!methodType) {