function validateCallArguments()

in packages/pyright-internal/src/analyzer/typeEvaluator.ts [7817:8338]


    function validateCallArguments(
        errorNode: ExpressionNode,
        argList: FunctionArgument[],
        callType: Type,
        typeVarMap?: TypeVarMap,
        skipUnknownArgCheck = false,
        expectedType?: Type,
        recursionCount = 0
    ): CallResult {
        let argumentErrors = false;
        let isTypeIncomplete = false;
        let specializedInitSelfType: Type | undefined;

        if (recursionCount > maxTypeRecursionCount) {
            return { returnType: UnknownType.create(), argumentErrors: true };
        }
        recursionCount++;

        if (TypeBase.isSpecialForm(callType)) {
            const exprNode = errorNode.nodeType === ParseNodeType.Call ? errorNode.leftExpression : errorNode;
            addDiagnostic(
                AnalyzerNodeInfo.getFileInfo(errorNode).diagnosticRuleSet.reportGeneralTypeIssues,
                DiagnosticRule.reportGeneralTypeIssues,
                Localizer.Diagnostic.typeNotCallable().format({
                    expression: ParseTreeUtils.printExpression(exprNode),
                    type: printType(callType, /* expandTypeAlias */ true),
                }),
                exprNode
            );
            return { returnType: UnknownType.create(), argumentErrors: true };
        }

        const returnType = mapSubtypesExpandTypeVars(
            callType,
            /* conditionFilter */ undefined,
            (expandedSubtype, unexpandedSubtype) => {
                switch (expandedSubtype.category) {
                    case TypeCategory.Unknown:
                    case TypeCategory.Any: {
                        // Touch all of the args so they're marked accessed.
                        argList.forEach((arg) => {
                            if (arg.valueExpression && !speculativeTypeTracker.isSpeculative(arg.valueExpression)) {
                                getTypeForArgument(arg);
                            }
                        });

                        return expandedSubtype;
                    }

                    case TypeCategory.Function: {
                        // The stdlib collections/__init__.pyi stub file defines namedtuple
                        // as a function rather than a class, so we need to check for it here.
                        if (expandedSubtype.details.builtInName === 'namedtuple') {
                            addDiagnostic(
                                AnalyzerNodeInfo.getFileInfo(errorNode).diagnosticRuleSet.reportUntypedNamedTuple,
                                DiagnosticRule.reportUntypedNamedTuple,
                                Localizer.Diagnostic.namedTupleNoTypes(),
                                errorNode
                            );
                            return createNamedTupleType(evaluatorInterface, errorNode, argList, false);
                        }

                        let effectiveTypeVarMap = typeVarMap;
                        if (!effectiveTypeVarMap) {
                            // If a typeVarMap wasn't provided by the caller, allocate one here.
                            effectiveTypeVarMap = new TypeVarMap(getTypeVarScopeId(expandedSubtype));

                            // There are certain cases, such as with super().__new__(cls) calls where
                            // the call is a constructor but the proper TypeVar scope has been lost.
                            // We'll add a wildcard TypeVar scope here. This is a bit of a hack and
                            // we may need to revisit this in the future.
                            if (
                                !effectiveTypeVarMap.getSolveForScopes() &&
                                FunctionType.isConstructorMethod(expandedSubtype)
                            ) {
                                effectiveTypeVarMap.addSolveForScope(WildcardTypeVarScopeId);
                            }
                        }

                        const functionResult = validateFunctionArguments(
                            errorNode,
                            argList,
                            expandedSubtype,
                            effectiveTypeVarMap,
                            skipUnknownArgCheck,
                            expectedType
                        );

                        if (functionResult.isTypeIncomplete) {
                            isTypeIncomplete = true;
                        }

                        if (functionResult.argumentErrors) {
                            argumentErrors = true;
                        } else {
                            specializedInitSelfType = functionResult.specializedInitSelfType;

                            // Call the function transform logic to handle special-cased functions.
                            const transformed = applyFunctionTransform(
                                evaluatorInterface,
                                errorNode,
                                argList,
                                expandedSubtype,
                                {
                                    argumentErrors: functionResult.argumentErrors,
                                    returnType: functionResult.returnType ?? UnknownType.create(),
                                    isTypeIncomplete,
                                }
                            );

                            functionResult.returnType = transformed.returnType;
                            if (transformed.isTypeIncomplete) {
                                isTypeIncomplete = true;
                            }
                            if (transformed.argumentErrors) {
                                argumentErrors = true;
                            }
                        }

                        // Handle the NewType specially, replacing the normal return type.
                        if (!functionResult.argumentErrors && expandedSubtype.details.builtInName === 'NewType') {
                            return createNewType(errorNode, argList);
                        }

                        if (expandedSubtype.details.builtInName === '__import__') {
                            // For the special __import__ type, we'll override the return type to be "Any".
                            // This is required because we don't know what module was imported, and we don't
                            // want to fail type checks when accessing members of the resulting module type.
                            return AnyType.create();
                        }

                        return functionResult.returnType;
                    }

                    case TypeCategory.OverloadedFunction: {
                        // Handle the 'cast' call as a special case.
                        if (expandedSubtype.overloads[0].details.builtInName === 'cast' && argList.length === 2) {
                            // Verify that the cast is necessary.
                            const castToType = getTypeForArgumentExpectingType(argList[0]).type;
                            const castFromType = getTypeForArgument(argList[1]).type;
                            if (isInstantiableClass(castToType) && isClassInstance(castFromType)) {
                                if (
                                    isTypeSame(
                                        castToType,
                                        ClassType.cloneAsInstantiable(castFromType),
                                        /* ignorePseudoGeneric */ true
                                    )
                                ) {
                                    addDiagnostic(
                                        AnalyzerNodeInfo.getFileInfo(errorNode).diagnosticRuleSet.reportUnnecessaryCast,
                                        DiagnosticRule.reportUnnecessaryCast,
                                        Localizer.Diagnostic.unnecessaryCast().format({
                                            type: printType(castFromType),
                                        }),
                                        errorNode
                                    );
                                }
                            }

                            return convertToInstance(castToType);
                        }

                        const functionResult = validateOverloadedFunctionArguments(
                            errorNode,
                            argList,
                            expandedSubtype,
                            typeVarMap,
                            skipUnknownArgCheck,
                            expectedType
                        );

                        if (functionResult.isTypeIncomplete) {
                            isTypeIncomplete = true;
                        }

                        if (functionResult.argumentErrors) {
                            argumentErrors = true;
                        } else {
                            specializedInitSelfType = functionResult.specializedInitSelfType;

                            // Call the function transform logic to handle special-cased functions.
                            const transformed = applyFunctionTransform(
                                evaluatorInterface,
                                errorNode,
                                argList,
                                expandedSubtype,
                                {
                                    argumentErrors: functionResult.argumentErrors,
                                    returnType: functionResult.returnType ?? UnknownType.create(),
                                    isTypeIncomplete,
                                }
                            );

                            functionResult.returnType = transformed.returnType;
                            if (transformed.isTypeIncomplete) {
                                isTypeIncomplete = true;
                            }
                            if (transformed.argumentErrors) {
                                argumentErrors = true;
                            }
                        }

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

                    case TypeCategory.Class: {
                        if (TypeBase.isInstantiable(expandedSubtype)) {
                            if (expandedSubtype.literalValue !== undefined) {
                                addDiagnostic(
                                    AnalyzerNodeInfo.getFileInfo(errorNode).diagnosticRuleSet.reportGeneralTypeIssues,
                                    DiagnosticRule.reportGeneralTypeIssues,
                                    Localizer.Diagnostic.literalNotCallable(),
                                    errorNode
                                );
                                argumentErrors = true;
                                return UnknownType.create();
                            }

                            if (ClassType.isBuiltIn(expandedSubtype)) {
                                const className = expandedSubtype.aliasName || expandedSubtype.details.name;

                                if (className === 'type') {
                                    // Validate the constructor arguments.
                                    validateConstructorArguments(
                                        errorNode,
                                        argList,
                                        expandedSubtype,
                                        skipUnknownArgCheck,
                                        expectedType
                                    );

                                    // Handle the 'type' call specially.
                                    if (argList.length === 1) {
                                        // The one-parameter form of "type" returns the class
                                        // for the specified object.
                                        const argType = getTypeForArgument(argList[0]).type;
                                        if (
                                            isClassInstance(argType) ||
                                            (isTypeVar(argType) && TypeBase.isInstance(argType)) ||
                                            isNoneInstance(argType)
                                        ) {
                                            return convertToInstantiable(stripLiteralValue(argType));
                                        }
                                    } else if (argList.length >= 2) {
                                        // The two-parameter form of "type" returns a new class type
                                        // built from the specified base types.
                                        return createType(errorNode, argList) || AnyType.create();
                                    }

                                    // If the parameter to type() is not statically known,
                                    // fall back to Any.
                                    return AnyType.create();
                                }

                                if (className === 'TypeVar') {
                                    return createTypeVarType(errorNode, argList);
                                }

                                if (className === 'TypeVarTuple') {
                                    return createTypeVarTupleType(errorNode, argList);
                                }

                                if (className === 'ParamSpec') {
                                    return createParamSpecType(errorNode, argList);
                                }

                                if (className === 'NamedTuple') {
                                    return createNamedTupleType(evaluatorInterface, errorNode, argList, true);
                                }

                                if (className === 'NewType') {
                                    return createNewType(errorNode, argList);
                                }

                                if (
                                    className === 'Protocol' ||
                                    className === 'Generic' ||
                                    className === 'Callable' ||
                                    className === 'Concatenate' ||
                                    className === 'Type'
                                ) {
                                    const fileInfo = AnalyzerNodeInfo.getFileInfo(errorNode);
                                    addDiagnostic(
                                        fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
                                        DiagnosticRule.reportGeneralTypeIssues,
                                        Localizer.Diagnostic.typeNotIntantiable().format({ type: className }),
                                        errorNode
                                    );
                                    return AnyType.create();
                                }

                                if (
                                    className === 'Enum' ||
                                    className === 'IntEnum' ||
                                    className === 'StrEnum' ||
                                    className === 'Flag' ||
                                    className === 'IntFlag'
                                ) {
                                    return createEnumType(errorNode, expandedSubtype, argList);
                                }

                                if (className === 'TypedDict') {
                                    return createTypedDictType(evaluatorInterface, errorNode, expandedSubtype, argList);
                                }

                                if (className === 'auto' && argList.length === 0) {
                                    return getBuiltInObject(errorNode, 'int');
                                }
                            }

                            if (ClassType.supportsAbstractMethods(expandedSubtype)) {
                                const abstractMethods = getAbstractMethods(expandedSubtype);
                                if (
                                    abstractMethods.length > 0 &&
                                    !expandedSubtype.includeSubclasses &&
                                    !isTypeVar(unexpandedSubtype)
                                ) {
                                    // If the class is abstract, it can't be instantiated.
                                    const diagAddendum = new DiagnosticAddendum();
                                    const errorsToDisplay = 2;

                                    abstractMethods.forEach((abstractMethod, index) => {
                                        if (index === errorsToDisplay) {
                                            diagAddendum.addMessage(
                                                Localizer.DiagnosticAddendum.memberIsAbstractMore().format({
                                                    count: abstractMethods.length - errorsToDisplay,
                                                })
                                            );
                                        } else if (index < errorsToDisplay) {
                                            if (isInstantiableClass(abstractMethod.classType)) {
                                                const className = abstractMethod.classType.details.name;
                                                diagAddendum.addMessage(
                                                    Localizer.DiagnosticAddendum.memberIsAbstract().format({
                                                        type: className,
                                                        name: abstractMethod.symbolName,
                                                    })
                                                );
                                            }
                                        }
                                    });

                                    addDiagnostic(
                                        AnalyzerNodeInfo.getFileInfo(errorNode).diagnosticRuleSet
                                            .reportGeneralTypeIssues,
                                        DiagnosticRule.reportGeneralTypeIssues,
                                        Localizer.Diagnostic.instantiateAbstract().format({
                                            type: expandedSubtype.details.name,
                                        }) + diagAddendum.getString(),
                                        errorNode
                                    );
                                }
                            }

                            if (ClassType.isProtocolClass(expandedSubtype) && !expandedSubtype.includeSubclasses) {
                                // If the class is a protocol, it can't be instantiated.
                                addDiagnostic(
                                    AnalyzerNodeInfo.getFileInfo(errorNode).diagnosticRuleSet.reportGeneralTypeIssues,
                                    DiagnosticRule.reportGeneralTypeIssues,
                                    Localizer.Diagnostic.instantiateProtocol().format({
                                        type: expandedSubtype.details.name,
                                    }),
                                    errorNode
                                );
                            }

                            // Assume this is a call to the constructor.
                            const constructorResult = validateConstructorArguments(
                                errorNode,
                                argList,
                                expandedSubtype,
                                skipUnknownArgCheck,
                                expectedType
                            );

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

                            if (constructorResult.isTypeIncomplete) {
                                isTypeIncomplete = true;
                            }

                            let returnType = constructorResult.returnType;

                            // If the expandedSubtype originated from a TypeVar, convert
                            // the constructed type back to the TypeVar. For example, if
                            // we have `cls: Type[_T]` followed by `_T()`.
                            if (isTypeVar(unexpandedSubtype)) {
                                returnType = convertToInstance(unexpandedSubtype);
                            }

                            // If we instantiated a type, transform it into a class.
                            // This can happen if someone directly instantiates a metaclass
                            // deriving from type.
                            if (
                                returnType &&
                                isClassInstance(returnType) &&
                                returnType.details.mro.some(
                                    (baseClass) =>
                                        isInstantiableClass(baseClass) && ClassType.isBuiltIn(baseClass, 'type')
                                )
                            ) {
                                let newClassName = '__class_' + returnType.details.name;
                                if (argList.length === 3) {
                                    const firstArgType = getTypeForArgument(argList[0]).type;
                                    if (
                                        isClassInstance(firstArgType) &&
                                        ClassType.isBuiltIn(firstArgType, 'str') &&
                                        typeof firstArgType.literalValue === 'string'
                                    ) {
                                        newClassName = firstArgType.literalValue;
                                    }
                                }

                                const newClassType = ClassType.createInstantiable(
                                    newClassName,
                                    '',
                                    '',
                                    AnalyzerNodeInfo.getFileInfo(errorNode).filePath,
                                    ClassTypeFlags.None,
                                    ParseTreeUtils.getTypeSourceId(errorNode),
                                    ClassType.cloneAsInstantiable(returnType),
                                    ClassType.cloneAsInstantiable(returnType)
                                );
                                newClassType.details.baseClasses.push(getBuiltInType(errorNode, 'object'));
                                newClassType.details.effectiveMetaclass = expandedSubtype;
                                computeMroLinearization(newClassType);
                                return newClassType;
                            }

                            return returnType;
                        } else {
                            let memberType = getTypeFromObjectMember(errorNode, expandedSubtype, '__call__')?.type;

                            if (memberType && (isFunction(memberType) || isOverloadedFunction(memberType))) {
                                memberType = removeParamSpecVariadicsFromSignature(memberType);

                                const functionResult = validateCallArguments(
                                    errorNode,
                                    argList,
                                    memberType,
                                    typeVarMap,
                                    skipUnknownArgCheck,
                                    expectedType,
                                    recursionCount
                                );
                                if (functionResult.argumentErrors) {
                                    argumentErrors = true;
                                }
                                return functionResult.returnType || UnknownType.create();
                            }

                            if (!memberType || !isAnyOrUnknown(memberType)) {
                                addDiagnostic(
                                    AnalyzerNodeInfo.getFileInfo(errorNode).diagnosticRuleSet.reportGeneralTypeIssues,
                                    DiagnosticRule.reportGeneralTypeIssues,
                                    Localizer.Diagnostic.objectNotCallable().format({
                                        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,
        };
    }