sourceType: printType()

in packages/pyright-internal/src/analyzer/typeEvaluator.ts [19937:20257]


                                sourceType: printType(updatedType),
                                destType: printType(destType.details.boundType),
                                name: TypeVarType.getReadableName(destType),
                            })
                        );
                    }
                }
                return false;
            }
        }

        if (!typeVarMap.isLocked() && isTypeVarInScope) {
            typeVarMap.setTypeVarType(destType, newNarrowTypeBound, newWideTypeBound, retainLiterals);
        }

        return true;
    }

    function canAssignTypeToParamSpec(
        destType: TypeVarType,
        srcType: Type,
        diag: DiagnosticAddendum | undefined,
        typeVarMap: TypeVarMap,
        recursionCount = 0
    ) {
        if (isTypeVar(srcType) && srcType.details.isParamSpec) {
            const existingEntry = typeVarMap.getParamSpec(destType);
            if (existingEntry) {
                if (existingEntry.parameters.length === 0 && existingEntry.paramSpec) {
                    // If there's an existing entry that matches, that's fine.
                    if (
                        isTypeSame(
                            existingEntry.paramSpec,
                            srcType,
                            /* ignorePseudoGeneric */ undefined,
                            /* ignoreTypeFlags */ undefined,
                            recursionCount
                        )
                    ) {
                        return true;
                    }
                }
            } else {
                if (!typeVarMap.isLocked() && typeVarMap.hasSolveForScope(destType.scopeId)) {
                    typeVarMap.setParamSpec(destType, {
                        flags: FunctionTypeFlags.None,
                        parameters: [],
                        typeVarScopeId: undefined,
                        docString: undefined,
                        paramSpec: srcType,
                    });
                }
                return true;
            }
        } else if (isFunction(srcType)) {
            const functionSrcType = srcType;
            const parameters = srcType.details.parameters.map((p, index) => {
                const paramSpecEntry: ParamSpecEntry = {
                    category: p.category,
                    name: p.name,
                    isNameSynthesized: p.isNameSynthesized,
                    hasDefault: !!p.hasDefault,
                    type: FunctionType.getEffectiveParameterType(functionSrcType, index),
                };
                return paramSpecEntry;
            });

            const existingEntry = typeVarMap.getParamSpec(destType);
            if (existingEntry) {
                // Verify that the existing entry matches the new entry.
                if (
                    !existingEntry.paramSpec &&
                    existingEntry.parameters.length === parameters.length &&
                    !existingEntry.parameters.some((existingParam, index) => {
                        const newParam = parameters[index];
                        return (
                            existingParam.category !== newParam.category ||
                            existingParam.name !== newParam.name ||
                            existingParam.hasDefault !== newParam.hasDefault ||
                            !isTypeSame(
                                existingParam.type,
                                newParam.type,
                                /* ignorePseudoGeneric */ undefined,
                                /* ignoreTypeFlags */ undefined,
                                recursionCount
                            )
                        );
                    })
                ) {
                    return true;
                }
            } else {
                if (!typeVarMap.isLocked() && typeVarMap.hasSolveForScope(destType.scopeId)) {
                    typeVarMap.setParamSpec(destType, {
                        parameters,
                        typeVarScopeId: srcType.details.typeVarScopeId,
                        flags: srcType.details.flags,
                        docString: srcType.details.docString,
                        paramSpec: undefined,
                    });
                }
                return true;
            }
        } else if (isAnyOrUnknown(srcType)) {
            return true;
        }

        if (diag) {
            diag.addMessage(
                Localizer.DiagnosticAddendum.typeParamSpec().format({
                    type: printType(srcType),
                    name: destType.details.name,
                })
            );
        }
        return false;
    }

    // Determines if the source type can be assigned to the dest type.
    // If typeVarMap is provided, type variables within the destType are
    // matched against existing type variables in the map. If a type variable
    // in the dest type is not in the type map already, it is assigned a type
    // and added to the map.
    function canAssignType(
        destType: Type,
        srcType: Type,
        diag?: DiagnosticAddendum,
        typeVarMap?: TypeVarMap,
        flags = CanAssignFlags.Default,
        recursionCount = 0
    ): boolean {
        destType = transformPossibleRecursiveTypeAlias(destType);
        srcType = transformPossibleRecursiveTypeAlias(srcType);

        // If this is a one-element union that contains a variadic type variable,
        // pull out the subtype.
        if (isUnion(destType) && destType.subtypes.length === 1 && isVariadicTypeVar(destType.subtypes[0])) {
            destType = destType.subtypes[0];
        }

        if (isUnion(srcType) && srcType.subtypes.length === 1 && isVariadicTypeVar(srcType.subtypes[0])) {
            srcType = srcType.subtypes[0];
        }

        if (destType === srcType) {
            // If the dest type is a TypeVar and a TypeVarMap was provided, we may
            // need to assign the TypeVar to itself under certain circumstances.
            // This is needed for cases where generic class A[T] calls its own
            // constructor with an argument of type T.
            if (
                isTypeVar(destType) &&
                !destType.details.isParamSpec &&
                !destType.details.isVariadic &&
                destType.scopeType === TypeVarScopeType.Class &&
                typeVarMap &&
                !typeVarMap.isLocked() &&
                typeVarMap.hasSolveForScope(destType.scopeId) &&
                !typeVarMap.getTypeVar(destType) &&
                (flags & (CanAssignFlags.SkipSolveTypeVars | CanAssignFlags.ReverseTypeVarMatching)) === 0
            ) {
                typeVarMap.setTypeVarType(destType, srcType);
            }

            return true;
        }

        if (recursionCount > maxTypeRecursionCount) {
            return true;
        }
        recursionCount++;

        // If the source or dest is unbound, allow the assignment. The
        // error will be reported elsewhere.
        if (isUnbound(destType) || isUnbound(srcType)) {
            return true;
        }

        // If we're in "overload overlap" mode, convert top-level type variables
        // to their concrete forms in the source.
        if ((flags & CanAssignFlags.OverloadOverlapCheck) !== 0) {
            srcType = makeTopLevelTypeVarsConcrete(srcType);
        }

        // Strip a few of the flags we don't want to propagate to other calls.
        const originalFlags = flags;
        flags &= ~(CanAssignFlags.AllowBoolTypeGuard | CanAssignFlags.AllowTypeVarNarrowing);

        // Before performing any other checks, see if the dest type is a
        // TypeVar that we are attempting to match.
        if (isTypeVar(destType)) {
            // If the dest is a constrained or bound type variable and all of the
            // types in the source are conditioned on that same type variable
            // and have compatible types, we'll consider it assignable.
            if (canAssignConditionalTypeToTypeVar(destType, srcType, recursionCount)) {
                return true;
            }

            if (isTypeSame(destType, srcType)) {
                return true;
            }

            // Handle the special case where both types are Self types. We'll allow
            // them to be treated as equivalent to handle certain common idioms.
            if (
                isTypeVar(srcType) &&
                srcType.details.isSynthesizedSelf &&
                srcType.details.boundType &&
                destType.details.isSynthesizedSelf &&
                destType.details.boundType
            ) {
                if ((flags & CanAssignFlags.ReverseTypeVarMatching) === 0 && typeVarMap) {
                    canAssignTypeToTypeVar(destType, srcType, diag, typeVarMap, originalFlags, recursionCount);
                }
                return true;
            }

            // If the dest is a variadic type variable, and the source is a tuple
            // with a single entry that is the same variadic type variable, it's a match.
            if (
                isVariadicTypeVar(destType) &&
                isClassInstance(srcType) &&
                isTupleClass(srcType) &&
                srcType.tupleTypeArguments &&
                srcType.tupleTypeArguments.length === 1
            ) {
                if (
                    isTypeSame(
                        destType,
                        srcType.tupleTypeArguments[0].type,
                        /* ignorePseudoGeneric */ undefined,
                        /* ignoreTypeFlags */ undefined,
                        recursionCount
                    )
                ) {
                    return true;
                }
            }

            // If we're using ReverseTypeVarMatching and the source is a TypeVar,
            // the logic below will handle this case.
            if ((flags & CanAssignFlags.ReverseTypeVarMatching) === 0 || !isTypeVar(srcType)) {
                if (flags & CanAssignFlags.SkipSolveTypeVars) {
                    return canAssignType(
                        makeTopLevelTypeVarsConcrete(destType),
                        makeTopLevelTypeVarsConcrete(srcType),
                        diag,
                        /* typeVarMap */ undefined,
                        originalFlags,
                        recursionCount
                    );
                } else {
                    if (
                        !canAssignTypeToTypeVar(
                            destType,
                            srcType,
                            diag,
                            typeVarMap ?? new TypeVarMap(),
                            originalFlags,
                            recursionCount
                        )
                    ) {
                        return false;
                    }

                    if (isAnyOrUnknown(srcType) && (flags & CanAssignFlags.OverloadOverlapCheck) !== 0) {
                        return false;
                    }

                    return true;
                }
            }
        }

        if (isTypeVar(srcType)) {
            if ((flags & CanAssignFlags.ReverseTypeVarMatching) !== 0) {
                // The caller has requested that we solve for source type variables
                // rather than dest. If the type variable is not in the scope of the
                // provided TypeVarMap, simply verify that the concrete types are
                // compatible.
                if (!typeVarMap || !typeVarMap.hasSolveForScope(getTypeVarScopeId(srcType))) {
                    return canAssignType(
                        makeTopLevelTypeVarsConcrete(destType),
                        makeTopLevelTypeVarsConcrete(srcType),
                        diag,
                        /* typeVarMap */ undefined,
                        originalFlags,
                        recursionCount
                    );
                } else {
                    // Reverse the order of assignment to populate the TypeVarMap for
                    // the source TypeVar. Normally we set the AllowTypeVarNarrowing flag
                    // so the wide type bound of the TypeVar is set rather than the narrow
                    // type bound. This allows the type to be further narrowed through other
                    // assignments. However, if we're populating the expected type in the
                    // TypeVarMap, we don't want to allow further narrowing.
                    let effectiveFlags = originalFlags;
                    if ((originalFlags & CanAssignFlags.PopulatingExpectedType) !== 0) {
                        effectiveFlags &= ~(
                            CanAssignFlags.ReverseTypeVarMatching | CanAssignFlags.AllowTypeVarNarrowing
                        );
                    } else {
                        effectiveFlags |= CanAssignFlags.AllowTypeVarNarrowing;
                    }

                    if (
                        canAssignTypeToTypeVar(
                            srcType as TypeVarType,
                            destType,
                            diag,
                            typeVarMap,
                            effectiveFlags,
                            recursionCount
                        )
                    ) {
                        return true;
                    }

                    // If the dest type is a union, only one of the subtypes needs to match.
                    let isAssignable = false;
                    if (isUnion(destType)) {
                        doForEachSubtype(destType, (destSubtype) => {