in packages/pyright-internal/src/analyzer/typeEvaluator.ts [16679:17025]
function createSpecializedClassType(
classType: ClassType,
typeArgs: TypeResult[] | undefined,
flags: EvaluatorFlags,
errorNode: ParseNode
): Type {
// Handle the special-case classes that are not defined
// in the type stubs.
if (ClassType.isSpecialBuiltIn(classType)) {
const aliasedName = classType.aliasName || classType.details.name;
switch (aliasedName) {
case 'Callable': {
return createCallableType(typeArgs, errorNode);
}
case 'Optional': {
return createOptionalType(classType, errorNode, typeArgs, flags);
}
case 'Type': {
// PEP 484 says that Type[Any] should be considered
// equivalent to type.
if (
typeArgs?.length === 1 &&
isAnyOrUnknown(typeArgs[0].type) &&
typeClassType &&
isInstantiableClass(typeClassType)
) {
return typeClassType;
}
let typeType = createSpecialType(classType, typeArgs, 1);
if (isInstantiableClass(typeType)) {
typeType = explodeGenericClass(typeType);
}
return typeType;
}
case 'ClassVar': {
return createClassVarType(classType, errorNode, typeArgs, flags);
}
case 'Protocol': {
return createSpecialType(
classType,
typeArgs,
/* paramLimit */ undefined,
/* allowParamSpec */ true
);
}
case 'Tuple': {
return createSpecialType(classType, typeArgs, /* paramLimit */ undefined);
}
case 'Union': {
return createUnionType(classType, errorNode, typeArgs, flags);
}
case 'Generic': {
return createGenericType(classType, errorNode, typeArgs, flags);
}
case 'Final': {
return createFinalType(classType, errorNode, typeArgs, flags);
}
case 'Annotated': {
return createAnnotatedType(errorNode, typeArgs);
}
case 'Concatenate': {
return createConcatenateType(errorNode, classType, typeArgs);
}
case 'TypeGuard':
case 'StrictTypeGuard': {
return createTypeGuardType(errorNode, classType, typeArgs);
}
case 'Unpack': {
return createUnpackType(errorNode, classType, typeArgs);
}
case 'Required':
case 'NotRequired': {
return createRequiredType(classType, errorNode, aliasedName === 'Required', typeArgs, flags);
}
case 'Self': {
return createSelfType(classType, errorNode, typeArgs);
}
}
}
const fileInfo = AnalyzerNodeInfo.getFileInfo(errorNode);
if (
fileInfo.isStubFile ||
fileInfo.executionEnvironment.pythonVersion >= PythonVersion.V3_9 ||
isAnnotationEvaluationPostponed(AnalyzerNodeInfo.getFileInfo(errorNode)) ||
(flags & EvaluatorFlags.AllowForwardReferences) !== 0
) {
// Handle "type" specially, since it needs to act like "Type"
// in Python 3.9 and newer.
if (ClassType.isBuiltIn(classType, 'type') && typeArgs) {
// PEP 484 says that type[Any] should be considered
// equivalent to type.
if (typeArgs.length === 1 && isAnyOrUnknown(typeArgs[0].type)) {
return classType;
}
const typeClass = getTypingType(errorNode, 'Type');
if (typeClass && isInstantiableClass(typeClass)) {
let typeType = createSpecialType(
typeClass,
typeArgs,
1,
/* allowParamSpec */ undefined,
/* isCallable */ true
);
if (isInstantiableClass(typeType)) {
typeType = explodeGenericClass(typeType);
}
return typeType;
}
}
// Handle "tuple" specially, since it needs to act like "Tuple"
// in Python 3.9 and newer.
if (isTupleClass(classType)) {
return createSpecialType(
classType,
typeArgs,
/* paramLimit */ undefined,
/* allowParamSpec */ undefined,
/* isCallable */ true
);
}
}
let typeArgCount = typeArgs ? typeArgs.length : 0;
// Make sure the argument list count is correct.
const typeParameters = ClassType.isPseudoGenericClass(classType) ? [] : ClassType.getTypeParameters(classType);
// If there are no type parameters or args, the class is already specialized.
// No need to do any more work.
if (typeParameters.length === 0 && typeArgCount === 0) {
return classType;
}
const variadicTypeParamIndex = typeParameters.findIndex((param) => isVariadicTypeVar(param));
if (typeArgs) {
if (typeArgCount > typeParameters.length) {
if (!ClassType.isPartiallyConstructed(classType) && !ClassType.isTupleClass(classType)) {
const fileInfo = AnalyzerNodeInfo.getFileInfo(errorNode);
if (typeParameters.length === 0) {
addDiagnostic(
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
Localizer.Diagnostic.typeArgsExpectingNone().format({
name: classType.aliasName || classType.details.name,
}),
typeArgs[typeParameters.length].node
);
} else if (typeParameters.length !== 1 || !isParamSpec(typeParameters[0])) {
addDiagnostic(
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
Localizer.Diagnostic.typeArgsTooMany().format({
name: classType.aliasName || classType.details.name,
expected: typeParameters.length,
received: typeArgCount,
}),
typeArgs[typeParameters.length].node
);
}
}
typeArgCount = typeParameters.length;
} else if (typeArgCount < typeParameters.length) {
const fileInfo = AnalyzerNodeInfo.getFileInfo(errorNode);
addDiagnostic(
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
Localizer.Diagnostic.typeArgsTooFew().format({
name: classType.aliasName || classType.details.name,
expected: typeParameters.length,
received: typeArgCount,
}),
typeArgs.length > 0 ? typeArgs[0].node.parent! : errorNode
);
}
typeArgs.forEach((typeArg, index) => {
if (index === variadicTypeParamIndex) {
// The types that make up the tuple that maps to the variadic
// type variable have already been validated when the tuple
// object was created in adjustTypeArgumentsForVariadicTypeVar.
if (isClassInstance(typeArg.type) && isTupleClass(typeArg.type)) {
return;
}
if (isVariadicTypeVar(typeArg.type)) {
validateVariadicTypeVarIsUnpacked(typeArg.type, typeArg.node);
return;
}
}
const typeParam = index < typeParameters.length ? typeParameters[index] : undefined;
const isParamSpecTarget = typeParam?.details.isParamSpec;
validateTypeArg(typeArg, {
allowParamSpec: true,
allowTypeArgList: isParamSpecTarget,
});
});
}
// Handle ParamSpec arguments and fill in any missing type arguments with Unknown.
const typeArgTypes: Type[] = [];
const fullTypeParams = ClassType.getTypeParameters(classType);
// PEP 612 says that if the class has only one type parameter consisting
// of a ParamSpec, the list of arguments does not need to be enclosed in
// a list. We'll handle that case specially here.
if (fullTypeParams.length === 1 && fullTypeParams[0].details.isParamSpec && typeArgs) {
if (
typeArgs.every(
(typeArg) => !isEllipsisType(typeArg.type) && !typeArg.typeList && !isParamSpec(typeArg.type)
)
) {
if (
typeArgs.length !== 1 ||
!isInstantiableClass(typeArgs[0].type) ||
!ClassType.isBuiltIn(typeArgs[0].type, 'Concatenate')
) {
// Package up the type arguments into a typeList.
typeArgs = [
{
type: UnknownType.create(),
node: typeArgs[0].node,
typeList: typeArgs,
},
];
}
} else if (typeArgs.length > 1) {
const paramSpecTypeArg = typeArgs.find((typeArg) => isParamSpec(typeArg.type));
if (paramSpecTypeArg) {
addError(Localizer.Diagnostic.paramSpecContext(), paramSpecTypeArg.node);
}
const listTypeArg = typeArgs.find((typeArg) => !!typeArg.typeList);
if (listTypeArg) {
addError(Localizer.Diagnostic.typeArgListNotAllowed(), listTypeArg.node);
}
}
}
fullTypeParams.forEach((typeParam, index) => {
if (typeArgs && index < typeArgs.length) {
if (typeParam.details.isParamSpec) {
const typeArg = typeArgs[index];
const functionType = FunctionType.createInstantiable('', '', '', FunctionTypeFlags.ParamSpecValue);
TypeBase.setSpecialForm(functionType);
if (isEllipsisType(typeArg.type)) {
FunctionType.addDefaultParameters(functionType);
functionType.details.flags |= FunctionTypeFlags.SkipArgsKwargsCompatibilityCheck;
typeArgTypes.push(functionType);
return;
}
if (typeArg.typeList) {
typeArg.typeList!.forEach((paramType, paramIndex) => {
FunctionType.addParameter(functionType, {
category: ParameterCategory.Simple,
name: `__p${paramIndex}`,
isNameSynthesized: true,
type: convertToInstance(paramType.type),
hasDeclaredType: true,
});
});
typeArgTypes.push(functionType);
return;
}
if (isInstantiableClass(typeArg.type) && ClassType.isBuiltIn(typeArg.type, 'Concatenate')) {
const concatTypeArgs = typeArg.type.typeArguments;
if (concatTypeArgs && concatTypeArgs.length > 0) {
concatTypeArgs.forEach((typeArg, index) => {
if (index === concatTypeArgs.length - 1) {
if (isParamSpec(typeArg)) {
functionType.details.paramSpec = typeArg;
}
} else {
FunctionType.addParameter(functionType, {
category: ParameterCategory.Simple,
name: `__p${index}`,
isNameSynthesized: true,
hasDeclaredType: true,
type: typeArg,
});
}
});
}
typeArgTypes.push(functionType);
return;
}
}
typeArgTypes.push(convertToInstance(typeArgs[index].type));
return;
}
typeArgTypes.push(UnknownType.create());
});
typeArgTypes.forEach((typeArgType, index) => {
if (index < typeArgCount) {
const diag = new DiagnosticAddendum();
if (!canAssignToTypeVar(typeParameters[index], typeArgType, diag)) {
// Avoid emitting this error for a partially-constructed class.
if (!isClassInstance(typeArgType) || !ClassType.isPartiallyConstructed(typeArgType)) {
const fileInfo = AnalyzerNodeInfo.getFileInfo(typeArgs![index].node);
addDiagnostic(
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
Localizer.Diagnostic.typeVarAssignmentMismatch().format({
type: printType(typeArgType),
name: TypeVarType.getReadableName(typeParameters[index]),
}) + diag.getString(),
typeArgs![index].node
);
}
}
}
});
const specializedClass = ClassType.cloneForSpecialization(classType, typeArgTypes, typeArgs !== undefined);
return specializedClass;
}