in packages/pyright-internal/src/analyzer/typeEvaluator.ts [20519:20860]
sourceType: printType(srcType),
destType: printType(destType),
})
);
}
return false;
}
}
if (
!canAssignClass(
ClassType.cloneAsInstantiable(destType),
ClassType.cloneAsInstantiable(concreteSrcType),
diag,
typeVarMap,
flags,
recursionCount,
/* reportErrorsUsingObjType */ true
)
) {
return false;
}
return true;
} else if (isFunction(concreteSrcType) || isOverloadedFunction(concreteSrcType)) {
// Is the destination a callback protocol (defined in PEP 544)?
const destCallbackType = getCallbackProtocolType(destType);
if (destCallbackType) {
return canAssignType(destCallbackType, concreteSrcType, diag, typeVarMap, flags, recursionCount);
}
// All functions are objects, so try to assign as an object.
if (objectType && isClassInstance(objectType)) {
return canAssignType(destType, objectType, diag, typeVarMap, flags, recursionCount);
}
} else if (isModule(concreteSrcType)) {
// Is the destination the built-in "ModuleType"?
if (ClassType.isBuiltIn(destType, 'ModuleType')) {
return true;
}
if (ClassType.isProtocolClass(destType)) {
return canAssignModuleToProtocol(
ClassType.cloneAsInstantiable(destType),
concreteSrcType,
diag,
typeVarMap,
flags,
recursionCount
);
}
} else if (isInstantiableClass(concreteSrcType)) {
// See if the destType is an instantiation of a Protocol
// class that is effectively a function.
const callbackType = getCallbackProtocolType(destType);
if (callbackType) {
return canAssignType(callbackType, concreteSrcType, diag, typeVarMap, flags, recursionCount);
}
// If the destType is an instantiation of a Protocol,
// see if the class type itself satisfies the protocol.
if (ClassType.isProtocolClass(destType)) {
return canAssignClassToProtocol(
ClassType.cloneAsInstantiable(destType),
concreteSrcType,
diag,
typeVarMap,
flags,
/* treatSourceAsInstantiable */ true,
recursionCount
);
}
// Determine if the metaclass can be assigned to the object.
const metaclass = concreteSrcType.details.effectiveMetaclass;
if (metaclass) {
if (isAnyOrUnknown(metaclass)) {
return true;
} else {
return canAssignClass(
ClassType.cloneAsInstantiable(destType),
metaclass,
diag,
typeVarMap,
flags,
recursionCount,
/* reportErrorsUsingObjType */ false
);
}
}
} else if (isAnyOrUnknown(concreteSrcType)) {
return (flags & CanAssignFlags.OverloadOverlapCheck) === 0;
} else if (isUnion(concreteSrcType)) {
return canAssignType(destType, concreteSrcType, diag, typeVarMap, flags, recursionCount);
}
}
if (isFunction(destType)) {
let srcFunction: FunctionType | undefined;
let concreteSrcType = makeTopLevelTypeVarsConcrete(srcType);
if (isClassInstance(concreteSrcType)) {
const callMember = lookUpObjectMember(concreteSrcType, '__call__');
if (callMember) {
const memberType = getTypeOfMember(callMember);
if (isFunction(memberType) || isOverloadedFunction(memberType)) {
const boundMethod = bindFunctionToClassOrObject(
concreteSrcType,
memberType,
/* memberClass */ undefined,
/* errorNode */ undefined,
recursionCount
);
if (boundMethod) {
concreteSrcType = removeParamSpecVariadicsFromSignature(boundMethod);
}
}
}
}
// If it's a class, use the constructor for type compatibility checking.
if (isInstantiableClass(concreteSrcType) && concreteSrcType.literalValue === undefined) {
const constructor = createFunctionFromConstructor(concreteSrcType);
if (constructor) {
concreteSrcType = constructor;
}
}
if (isOverloadedFunction(concreteSrcType)) {
// Overloads are not compatible with ParamSpec.
if (destType.details.paramSpec) {
if (diag) {
diag.addMessage(Localizer.DiagnosticAddendum.paramSpecOverload());
}
return false;
}
// Find first overloaded function that matches the parameters.
// We don't want to pollute the current typeVarMap, so we'll
// make a copy of the existing one if it's specified.
const overloads = concreteSrcType.overloads;
const overloadIndex = overloads.findIndex((overload) => {
if (!FunctionType.isOverloaded(overload)) {
return false;
}
const typeVarMapClone = typeVarMap ? typeVarMap.clone() : undefined;
return canAssignType(
destType,
overload,
diag?.createAddendum(),
typeVarMapClone,
flags,
recursionCount
);
});
if (overloadIndex < 0) {
if (diag) {
diag.addMessage(
Localizer.DiagnosticAddendum.noOverloadAssignable().format({ type: printType(destType) })
);
}
return false;
}
srcFunction = overloads[overloadIndex];
} else if (isFunction(concreteSrcType)) {
srcFunction = concreteSrcType;
} else if (isAnyOrUnknown(concreteSrcType)) {
return (flags & CanAssignFlags.OverloadOverlapCheck) === 0;
}
if (srcFunction) {
if (
canAssignFunction(
destType,
srcFunction,
diag?.createAddendum(),
typeVarMap ?? new TypeVarMap(getTypeVarScopeId(destType)),
flags,
recursionCount
)
) {
return true;
}
}
}
if (isOverloadedFunction(destType)) {
const overloadDiag = diag?.createAddendum();
// All overloads in the dest must be assignable.
const isAssignable = destType.overloads.every((destOverload) => {
if (!FunctionType.isOverloaded(destOverload)) {
return true;
}
if (typeVarMap) {
typeVarMap.addSolveForScope(getTypeVarScopeId(destOverload));
}
const result = canAssignType(
destOverload,
srcType,
overloadDiag?.createAddendum(),
typeVarMap || new TypeVarMap(getTypeVarScopeId(destOverload)),
flags,
recursionCount
);
return result;
});
if (!isAssignable) {
if (overloadDiag) {
overloadDiag.addMessage(
Localizer.DiagnosticAddendum.overloadNotAssignable().format({
name: destType.overloads[0].details.name,
})
);
}
return false;
}
return true;
}
if (isClassInstance(destType) && ClassType.isBuiltIn(destType, 'object')) {
if ((flags & CanAssignFlags.EnforceInvariance) === 0) {
// All types (including None, Module, OverloadedFunction) derive from object.
return true;
}
}
// Are we trying to assign None to a protocol?
if (isNoneInstance(srcType) && isClassInstance(destType) && ClassType.isProtocolClass(destType)) {
if (noneType && isInstantiableClass(noneType)) {
return canAssignClassToProtocol(
ClassType.cloneAsInstantiable(destType),
noneType,
diag,
typeVarMap,
flags,
/* treatSourceAsInstantiable */ false,
recursionCount
);
}
}
if (isNoneInstance(destType)) {
if (diag) {
diag.addMessage(Localizer.DiagnosticAddendum.assignToNone());
}
return false;
}
if (diag) {
diag.addMessage(
Localizer.DiagnosticAddendum.typeAssignmentMismatch().format({
sourceType: printType(srcType),
destType: printType(destType),
})
);
}
return false;
}
function canAssignFromUnionType(
destType: Type,
srcType: UnionType,
diag: DiagnosticAddendum | undefined,
typeVarMap: TypeVarMap | undefined,
flags: CanAssignFlags,
recursionCount: number
): boolean {
// Start by checking for an exact match. This is needed to handle unions
// that contain recursive type aliases.
if (
isTypeSame(
srcType,
destType,
/* ignorePseudoGeneric */ undefined,
/* ignoreTypeFlags */ undefined,
recursionCount
)
) {
return true;
}
// Handle the case where the source and dest are both unions and
// invariance is being enforced and the dest contains type variables.
if (flags & CanAssignFlags.EnforceInvariance) {
if (isUnion(destType)) {
const remainingDestSubtypes: Type[] = [];
let remainingSrcSubtypes: Type[] = [...srcType.subtypes];
let isIncompatible = false;
// First attempt to match all of the non-generic types in the dest
// to non-generic types in the source.
destType.subtypes.forEach((destSubtype) => {
if (requiresSpecialization(destSubtype)) {
remainingDestSubtypes.push(destSubtype);
} else {
const srcTypeIndex = remainingSrcSubtypes.findIndex((srcSubtype) =>
isTypeSame(
srcSubtype,
destSubtype,
/* ignorePseudoGeneric */ undefined,
/* ignoreTypeFlags */ undefined,
recursionCount
)
);
if (srcTypeIndex >= 0) {
remainingSrcSubtypes.splice(srcTypeIndex, 1);
} else {
isIncompatible = true;
}
}
});
// For all remaining source subtypes, attempt to find a dest subtype
// whose primary type matches.
if (!isIncompatible) {
[...remainingSrcSubtypes].forEach((srcSubtype) => {
const destTypeIndex = remainingDestSubtypes.findIndex(
(destSubtype) =>
isClass(srcSubtype) &&
isClass(destSubtype) &&
TypeBase.isInstance(srcSubtype) === TypeBase.isInstance(destSubtype) &&
ClassType.isSameGenericClass(srcSubtype, destSubtype)
);
if (destTypeIndex >= 0) {
if (
!canAssignType(
remainingDestSubtypes[destTypeIndex],
srcSubtype,
diag?.createAddendum(),
typeVarMap,
flags,
recursionCount
)
) {