type: printType()

in packages/pyright-internal/src/analyzer/typeEvaluator.ts [8274:8683]


                                        type: printType(expandedSubtype),
                                    }),
                                    errorNode
                                );
                            }
                            return UnknownType.create();
                        }
                    }

                    case TypeCategory.None: {
                        addDiagnostic(
                            AnalyzerNodeInfo.getFileInfo(errorNode).diagnosticRuleSet.reportOptionalCall,
                            DiagnosticRule.reportOptionalCall,
                            Localizer.Diagnostic.noneNotCallable(),
                            errorNode
                        );
                        return undefined;
                    }

                    // TypeVars should have been expanded in most cases,
                    // but we still need to handle the case of Type[T] where
                    // T is a constrained type that contains a union. We also
                    // need to handle recursive type aliases.
                    case TypeCategory.TypeVar: {
                        expandedSubtype = transformPossibleRecursiveTypeAlias(expandedSubtype);

                        const callResult = validateCallArguments(
                            errorNode,
                            argList,
                            expandedSubtype,
                            typeVarMap,
                            skipUnknownArgCheck,
                            expectedType,
                            recursionCount
                        );

                        if (callResult.argumentErrors) {
                            argumentErrors = true;
                        }

                        return callResult.returnType || UnknownType.create();
                    }

                    case TypeCategory.Module: {
                        addDiagnostic(
                            AnalyzerNodeInfo.getFileInfo(errorNode).diagnosticRuleSet.reportGeneralTypeIssues,
                            DiagnosticRule.reportGeneralTypeIssues,
                            Localizer.Diagnostic.moduleNotCallable(),
                            errorNode
                        );
                        return undefined;
                    }
                }

                return undefined;
            }
        );

        return {
            argumentErrors,
            returnType: isNever(returnType) ? undefined : returnType,
            isTypeIncomplete,
            specializedInitSelfType,
        };
    }

    // Matches the arguments passed to a function to the corresponding parameters in that
    // function. This matching is done based on positions and keywords. Type evaluation and
    // validation is left to the caller.
    // This logic is based on PEP 3102: https://www.python.org/dev/peps/pep-3102/
    function matchFunctionArgumentsToParameters(
        errorNode: ExpressionNode,
        argList: FunctionArgument[],
        type: FunctionType,
        overloadIndex: number
    ): MatchArgsToParamsResult {
        const paramDetails = getParameterListDetails(type);
        let argIndex = 0;
        let matchedUnpackedListOfUnknownLength = false;
        let reportedArgError = false;
        let isTypeIncomplete = false;
        let isVariadicTypeVarFullyMatched = false;

        // Build a map of parameters by name.
        const paramMap = new Map<string, ParamAssignmentInfo>();
        paramDetails.params.forEach((paramInfo) => {
            const param = paramInfo.param;
            if (param.name && param.category === ParameterCategory.Simple) {
                paramMap.set(param.name, {
                    argsNeeded: param.category === ParameterCategory.Simple && !param.hasDefault ? 1 : 0,
                    argsReceived: 0,
                    isPositionalOnly: paramInfo.source === ParameterSource.PositionOnly,
                });
            }
        });

        let positionalOnlyLimitIndex = paramDetails.positionOnlyParamCount;
        let positionParamLimitIndex = paramDetails.firstKeywordOnlyIndex ?? paramDetails.params.length;

        const varArgListParamIndex = paramDetails.argsIndex;
        const varArgDictParamIndex = paramDetails.kwargsIndex;

        // Is this an function that uses the *args and **kwargs
        // from a param spec? If so, we need to treat all positional parameters
        // prior to the *args as positional-only according to PEP 612.
        let paramSpecArgList: FunctionArgument[] | undefined;
        let paramSpecTarget: TypeVarType | undefined;
        let hasParamSpecArgsKwargs = false;

        if (varArgListParamIndex !== undefined && varArgDictParamIndex !== undefined) {
            const varArgListParam = paramDetails.params[varArgListParamIndex].param;
            const varArgDictParam = paramDetails.params[varArgDictParamIndex].param;

            if (
                isParamSpec(varArgListParam.type) &&
                varArgListParam.type.paramSpecAccess === 'args' &&
                isParamSpec(varArgDictParam.type) &&
                varArgDictParam.type.paramSpecAccess === 'kwargs' &&
                varArgListParam.type.details.name === varArgDictParam.type.details.name
            ) {
                hasParamSpecArgsKwargs = true;

                // Does this function define the param spec, or is it an inner
                // function nested within another function that defines the param
                // spec? We need to handle these two cases differently.
                if (varArgListParam.type.scopeId === type.details.typeVarScopeId) {
                    paramSpecArgList = [];
                    paramSpecTarget = TypeVarType.cloneForParamSpecAccess(varArgListParam.type, undefined);
                } else {
                    positionalOnlyLimitIndex = varArgListParamIndex;
                }
            }
        }

        // If there are keyword arguments present, they may target one or
        // more parameters that are positional. In this case, we will limit
        // the number of positional parameters.
        argList.forEach((arg) => {
            if (arg.name) {
                const keywordParamIndex = paramDetails.params.findIndex(
                    (paramInfo) =>
                        paramInfo.param.name === arg.name!.value &&
                        paramInfo.param.category === ParameterCategory.Simple
                );

                // Is this a parameter that can be interpreted as either a keyword or a positional?
                // If so, we'll treat it as a keyword parameter in this case because it's being
                // targeted by a keyword argument.
                if (keywordParamIndex >= 0 && keywordParamIndex >= positionalOnlyLimitIndex) {
                    if (positionParamLimitIndex < 0 || keywordParamIndex < positionParamLimitIndex) {
                        positionParamLimitIndex = keywordParamIndex;
                    }
                }
            }
        });

        // If we didn't see any special cases, then all parameters are positional.
        if (positionParamLimitIndex < 0) {
            positionParamLimitIndex = paramDetails.params.length;
        }

        // Determine how many positional args are being passed before
        // we see a keyword arg.
        let positionalArgCount = argList.findIndex(
            (arg) => arg.argumentCategory === ArgumentCategory.UnpackedDictionary || arg.name !== undefined
        );
        if (positionalArgCount < 0) {
            positionalArgCount = argList.length;
        }

        let validateArgTypeParams: ValidateArgTypeParams[] = [];

        let activeParam: FunctionParameter | undefined;
        function trySetActive(arg: FunctionArgument, param: FunctionParameter) {
            if (arg.active) {
                activeParam = param;
            }
        }

        let foundUnpackedListArg =
            argList.find((arg) => arg.argumentCategory === ArgumentCategory.UnpackedList) !== undefined;

        // Map the positional args to parameters.
        let paramIndex = 0;
        let unpackedArgIndex = 0;

        while (argIndex < positionalArgCount) {
            if (argIndex < positionalOnlyLimitIndex && argList[argIndex].name) {
                const fileInfo = AnalyzerNodeInfo.getFileInfo(argList[argIndex].name!);
                addDiagnostic(
                    fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
                    DiagnosticRule.reportGeneralTypeIssues,
                    Localizer.Diagnostic.argPositional(),
                    argList[argIndex].name!
                );
                reportedArgError = true;
            }

            if (paramIndex >= positionParamLimitIndex) {
                if (!foundUnpackedListArg || argList[argIndex].argumentCategory !== ArgumentCategory.UnpackedList) {
                    addDiagnostic(
                        AnalyzerNodeInfo.getFileInfo(errorNode).diagnosticRuleSet.reportGeneralTypeIssues,
                        DiagnosticRule.reportGeneralTypeIssues,
                        positionParamLimitIndex === 1
                            ? Localizer.Diagnostic.argPositionalExpectedOne()
                            : Localizer.Diagnostic.argPositionalExpectedCount().format({
                                  expected: positionParamLimitIndex,
                              }),
                        argList[argIndex].valueExpression || errorNode
                    );
                    reportedArgError = true;
                }
                break;
            }

            if (paramIndex >= paramDetails.params.length) {
                break;
            }

            const paramType = paramDetails.params[paramIndex].type;
            if (argList[argIndex].argumentCategory === ArgumentCategory.UnpackedList) {
                if (!argList[argIndex].valueExpression) {
                    break;
                }

                const isParamVariadic =
                    paramDetails.params[paramIndex].param.category === ParameterCategory.VarArgList &&
                    isVariadicTypeVar(paramType);
                let isArgCompatibleWithVariadic = false;
                const argTypeResult = getTypeForArgument(argList[argIndex]);
                const argType = argTypeResult.type;
                let listElementType: Type | undefined;
                let advanceToNextArg = false;

                // Handle the case where *args is being passed to a function defined
                // with a ParamSpec and a Concatenate operator. PEP 612 indicates that
                // all positional parameters specified in the Concatenate must be
                // filled explicitly.
                if (type.details.paramSpec && paramIndex < positionParamLimitIndex) {
                    addDiagnostic(
                        AnalyzerNodeInfo.getFileInfo(errorNode).diagnosticRuleSet.reportGeneralTypeIssues,
                        DiagnosticRule.reportGeneralTypeIssues,
                        positionParamLimitIndex === 1
                            ? Localizer.Diagnostic.argPositionalExpectedOne()
                            : Localizer.Diagnostic.argPositionalExpectedCount().format({
                                  expected: positionParamLimitIndex,
                              }),
                        argList[argIndex].valueExpression || errorNode
                    );
                    reportedArgError = true;
                }

                // If this is a tuple with specified element types, use those
                // specified types rather than using the more generic iterator
                // type which will be a union of all element types.
                const combinedTupleType = combineSameSizedTuples(makeTopLevelTypeVarsConcrete(argType), tupleClassType);

                if (
                    !isParamVariadic &&
                    combinedTupleType &&
                    isClassInstance(combinedTupleType) &&
                    combinedTupleType.tupleTypeArguments &&
                    combinedTupleType.tupleTypeArguments.length > 0 &&
                    unpackedArgIndex < combinedTupleType.tupleTypeArguments.length
                ) {
                    listElementType = combinedTupleType.tupleTypeArguments[unpackedArgIndex].type;

                    // Determine if there are any more unpacked list arguments after
                    // this one. If not, we'll clear this flag because this unpacked
                    // list arg is bounded in length.
                    foundUnpackedListArg =
                        argList.find(
                            (arg, index) => index > argIndex && arg.argumentCategory === ArgumentCategory.UnpackedList
                        ) !== undefined;

                    unpackedArgIndex++;
                    if (unpackedArgIndex >= combinedTupleType.tupleTypeArguments.length) {
                        unpackedArgIndex = 0;
                        advanceToNextArg = true;
                    }
                } else if (isParamVariadic && isVariadicTypeVar(argType)) {
                    // Allow an unpacked variadic type variable to satisfy an
                    // unpacked variadic type variable.
                    listElementType = argType;
                    isArgCompatibleWithVariadic = true;
                    advanceToNextArg = true;
                    isVariadicTypeVarFullyMatched = true;
                } else if (
                    isClassInstance(argType) &&
                    isTupleClass(argType) &&
                    argType.tupleTypeArguments &&
                    argType.tupleTypeArguments.length === 1 &&
                    isVariadicTypeVar(argType.tupleTypeArguments[0].type)
                ) {
                    // Handle the case where an unpacked variadic type var has
                    // been packaged into a tuple.
                    listElementType = argType.tupleTypeArguments[0].type;
                    isArgCompatibleWithVariadic = true;
                    advanceToNextArg = true;
                    isVariadicTypeVarFullyMatched = true;
                } else if (isParamSpec(argType) && argType.paramSpecAccess === 'args') {
                    listElementType = undefined;
                } else {
                    listElementType =
                        getTypeFromIterator(argType, /* isAsync */ false, argList[argIndex].valueExpression!) ||
                        UnknownType.create();

                    if (paramDetails.params[paramIndex].param.category !== ParameterCategory.VarArgList) {
                        matchedUnpackedListOfUnknownLength = true;
                    }
                }

                const funcArg: FunctionArgument | undefined = listElementType
                    ? {
                          argumentCategory: ArgumentCategory.Simple,
                          type: listElementType,
                      }
                    : undefined;
                if (funcArg && argTypeResult.isIncomplete) {
                    isTypeIncomplete = true;
                }

                const paramName = paramDetails.params[paramIndex].param.name;

                // It's not allowed to use unpacked arguments with a variadic *args
                // parameter unless the argument is a variadic arg as well.
                if (isParamVariadic && !isArgCompatibleWithVariadic) {
                    addDiagnostic(
                        AnalyzerNodeInfo.getFileInfo(errorNode).diagnosticRuleSet.reportGeneralTypeIssues,
                        DiagnosticRule.reportGeneralTypeIssues,
                        Localizer.Diagnostic.unpackedArgWithVariadicParam(),
                        argList[argIndex].valueExpression || errorNode
                    );
                    reportedArgError = true;
                } else {
                    if (paramSpecArgList) {
                        paramSpecArgList.push(argList[argIndex]);
                    }

                    if (funcArg) {
                        validateArgTypeParams.push({
                            paramCategory: paramDetails.params[paramIndex].param.category,
                            paramType,
                            requiresTypeVarMatching: requiresSpecialization(paramType),
                            argument: funcArg,
                            errorNode: argList[argIndex].valueExpression || errorNode,
                            paramName: paramDetails.params[paramIndex].param.isNameSynthesized ? undefined : paramName,
                        });
                    }
                }

                trySetActive(argList[argIndex], paramDetails.params[paramIndex].param);

                // Note that the parameter has received an argument.
                if (
                    paramName &&
                    paramDetails.params[paramIndex].param.category === ParameterCategory.Simple &&
                    paramMap.has(paramName)
                ) {
                    paramMap.get(paramName)!.argsReceived++;
                }

                if (
                    advanceToNextArg ||
                    paramDetails.params[paramIndex].param.category === ParameterCategory.VarArgList
                ) {
                    argIndex++;
                }

                if (
                    isVariadicTypeVarFullyMatched ||
                    paramDetails.params[paramIndex].param.category !== ParameterCategory.VarArgList
                ) {
                    paramIndex++;
                }
            } else if (paramDetails.params[paramIndex].param.category === ParameterCategory.VarArgList) {
                trySetActive(argList[argIndex], paramDetails.params[paramIndex].param);

                if (paramSpecArgList) {
                    paramSpecArgList.push(argList[argIndex]);
                    argIndex++;
                } else {
                    let paramCategory = paramDetails.params[paramIndex].param.category;
                    let effectiveParamType = paramType;
                    const paramName = paramDetails.params[paramIndex].param.name;

                    if (
                        isUnpackedTuple(paramType) &&
                        paramType.tupleTypeArguments &&
                        paramType.tupleTypeArguments.length > 0
                    ) {
                        effectiveParamType = paramType.tupleTypeArguments[0].type;
                    }

                    paramCategory = isVariadicTypeVar(effectiveParamType)
                        ? ParameterCategory.VarArgList
                        : ParameterCategory.Simple;

                    const remainingArgCount = positionalArgCount - argIndex;
                    const remainingParamCount = positionParamLimitIndex - paramIndex - 1;

                    if (remainingArgCount <= remainingParamCount) {
                        if (remainingArgCount < remainingParamCount) {
                            // Have we run out of arguments and still have parameters left to fill?
                            addDiagnostic(
                                AnalyzerNodeInfo.getFileInfo(errorNode).diagnosticRuleSet.reportGeneralTypeIssues,
                                DiagnosticRule.reportGeneralTypeIssues,
                                remainingArgCount === 1
                                    ? Localizer.Diagnostic.argMorePositionalExpectedOne()
                                    : Localizer.Diagnostic.argMorePositionalExpectedCount().format({