function createSpecializedClassType()

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