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,
};
}