sourceType: printType()

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
                                )
                            ) {