function getTypeFromMemberAccessWithBaseType()

in packages/pyright-internal/src/analyzer/typeEvaluator.ts [4227:4696]


    function getTypeFromMemberAccessWithBaseType(
        node: MemberAccessNode,
        baseTypeResult: TypeResult,
        usage: EvaluatorUsage,
        flags: EvaluatorFlags
    ): TypeResult {
        let baseType = baseTypeResult.type;
        const memberName = node.memberName.value;
        let diag = new DiagnosticAddendum();
        const fileInfo = AnalyzerNodeInfo.getFileInfo(node);
        let type: Type | undefined;
        let isIncomplete = !!baseTypeResult.isIncomplete;
        let isAsymmetricDescriptor: boolean | undefined;

        // If the base type was incomplete and unbound, don't proceed
        // because false positive errors will be generated.
        if (baseTypeResult.isIncomplete && isUnbound(baseTypeResult.type)) {
            return { type: UnknownType.create(), node, isIncomplete: true };
        }

        // Handle the special case where the expression is an actual
        // UnionType special form.
        if (isUnion(baseType) && TypeBase.isSpecialForm(baseType)) {
            if (objectType) {
                baseType = objectType;
            }
        }

        const getTypeFromNoneBase = () => {
            if (noneType && isInstantiableClass(noneType)) {
                const typeResult = getTypeFromObjectMember(
                    node.memberName,
                    noneType,
                    memberName,
                    usage,
                    diag,
                    /* memberAccessFlags */ undefined,
                    baseTypeResult.bindToType
                );
                return typeResult;
            }
            return undefined;
        };

        if (isParamSpec(baseType) && baseType.paramSpecAccess) {
            baseType = makeTopLevelTypeVarsConcrete(baseType);
        }

        switch (baseType.category) {
            case TypeCategory.Any:
            case TypeCategory.Unknown: {
                type = baseType;
                break;
            }

            case TypeCategory.Never: {
                type = UnknownType.create();
                break;
            }

            case TypeCategory.TypeVar: {
                if (baseType.details.isParamSpec) {
                    if (memberName === 'args') {
                        const paramNode = ParseTreeUtils.getEnclosingParameter(node);
                        if (!paramNode || paramNode.category !== ParameterCategory.VarArgList) {
                            addError(Localizer.Diagnostic.paramSpecArgsUsage(), node);
                            return { type: UnknownType.create(), node, isIncomplete };
                        }
                        return { type: TypeVarType.cloneForParamSpecAccess(baseType, 'args'), node, isIncomplete };
                    }

                    if (memberName === 'kwargs') {
                        const paramNode = ParseTreeUtils.getEnclosingParameter(node);
                        if (!paramNode || paramNode.category !== ParameterCategory.VarArgDictionary) {
                            addError(Localizer.Diagnostic.paramSpecKwargsUsage(), node);
                            return { type: UnknownType.create(), node, isIncomplete };
                        }
                        return { type: TypeVarType.cloneForParamSpecAccess(baseType, 'kwargs'), node, isIncomplete };
                    }

                    if (!isIncomplete) {
                        addDiagnostic(
                            fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
                            DiagnosticRule.reportGeneralTypeIssues,
                            Localizer.Diagnostic.paramSpecUnknownMember().format({ name: memberName }),
                            node
                        );
                    }
                    return { type: UnknownType.create(), node, isIncomplete };
                }

                if (flags & EvaluatorFlags.ExpectingType) {
                    if (!isIncomplete) {
                        addDiagnostic(
                            AnalyzerNodeInfo.getFileInfo(node).diagnosticRuleSet.reportGeneralTypeIssues,
                            DiagnosticRule.reportGeneralTypeIssues,
                            Localizer.Diagnostic.typeVarNoMember().format({
                                type: printType(baseType),
                                name: memberName,
                            }),
                            node.leftExpression
                        );
                    }

                    return { type: UnknownType.create(), node, isIncomplete };
                }

                if (baseType.details.recursiveTypeAliasName) {
                    return { type: UnknownType.create(), node, isIncomplete: true };
                }

                return getTypeFromMemberAccessWithBaseType(
                    node,
                    {
                        type: makeTopLevelTypeVarsConcrete(baseType),
                        node,
                        bindToType: baseType,
                        isIncomplete,
                    },
                    usage,
                    EvaluatorFlags.None
                );
            }

            case TypeCategory.Class: {
                if (TypeBase.isInstantiable(baseType)) {
                    const typeResult = getTypeFromClassMember(
                        node.memberName,
                        baseType,
                        memberName,
                        usage,
                        diag,
                        MemberAccessFlags.None,
                        baseTypeResult.bindToType
                    );

                    type = typeResult?.type;
                    if (typeResult?.isIncomplete) {
                        isIncomplete = true;
                    }

                    if (typeResult?.isAsymmetricDescriptor) {
                        isAsymmetricDescriptor = true;
                    }
                } else if (
                    ClassType.isBuiltIn(baseType, 'type') &&
                    objectType &&
                    isClassInstance(objectType) &&
                    !baseTypeResult.isSuperCall
                ) {
                    // Handle the case where the base type is an instance of 'type'. We'll
                    // treat it as an instantiable subclass of 'object'.
                    const typeResult = getTypeFromClassMember(
                        node.memberName,
                        ClassType.cloneAsInstantiable(objectType),
                        memberName,
                        usage,
                        diag,
                        MemberAccessFlags.None,
                        baseTypeResult.bindToType
                            ? (convertToInstance(baseTypeResult.bindToType) as ClassType | TypeVarType)
                            : undefined
                    );

                    type = typeResult?.type;
                    if (typeResult?.isIncomplete) {
                        isIncomplete = true;
                    }

                    if (typeResult?.isAsymmetricDescriptor) {
                        isAsymmetricDescriptor = true;
                    }
                } else {
                    // Handle the special case of 'name' and 'value' members within an enum.
                    if (ClassType.isEnumClass(baseType)) {
                        const literalValue = baseType.literalValue;
                        if (literalValue instanceof EnumLiteral) {
                            if (memberName === 'name' || memberName === '_name_') {
                                const strClass = getBuiltInType(node, 'str');
                                if (isInstantiableClass(strClass)) {
                                    return {
                                        node,
                                        type: ClassType.cloneAsInstance(
                                            ClassType.cloneWithLiteral(strClass, literalValue.itemName)
                                        ),
                                        isIncomplete,
                                    };
                                }
                            } else if (memberName === 'value' || memberName === '_value_') {
                                return { node, type: literalValue.itemType, isIncomplete };
                            }
                        }
                    }

                    const typeResult = getTypeFromObjectMember(
                        node.memberName,
                        baseType,
                        memberName,
                        usage,
                        diag,
                        /* memberAccessFlags */ undefined,
                        baseTypeResult.bindToType
                    );

                    if (typeResult) {
                        type = addConditionToType(typeResult.type, getTypeCondition(baseType));
                    }

                    if (typeResult?.isIncomplete) {
                        isIncomplete = true;
                    }

                    if (typeResult?.isAsymmetricDescriptor) {
                        isAsymmetricDescriptor = true;
                    }
                }
                break;
            }

            case TypeCategory.Module: {
                const symbol = ModuleType.getField(baseType, memberName);
                if (symbol && !symbol.isExternallyHidden()) {
                    if (usage.method === 'get') {
                        setSymbolAccessed(AnalyzerNodeInfo.getFileInfo(node), symbol, node.memberName);
                    }

                    type = getEffectiveTypeOfSymbolForUsage(
                        symbol,
                        /* usageNode */ undefined,
                        /* useLastDecl */ true
                    ).type;

                    if (isTypeVar(type)) {
                        type = validateTypeVarUsage(node, type, flags);
                    }

                    // If the type resolved to "unbound", treat it as "unknown" in
                    // the case of a module reference because if it's truly unbound,
                    // that error will be reported within the module and should not
                    // leak into other modules that import it.
                    if (isUnbound(type)) {
                        type = UnknownType.create();
                    }

                    if (symbol.isPrivateMember()) {
                        addDiagnostic(
                            AnalyzerNodeInfo.getFileInfo(node).diagnosticRuleSet.reportPrivateUsage,
                            DiagnosticRule.reportPrivateUsage,
                            Localizer.Diagnostic.privateUsedOutsideOfModule().format({
                                name: memberName,
                            }),
                            node.memberName
                        );
                    }

                    if (symbol.isPrivatePyTypedImport()) {
                        addDiagnostic(
                            AnalyzerNodeInfo.getFileInfo(node).diagnosticRuleSet.reportPrivateImportUsage,
                            DiagnosticRule.reportPrivateImportUsage,
                            Localizer.Diagnostic.privateImportFromPyTypedModule().format({
                                name: memberName,
                                module: baseType.moduleName,
                            }),
                            node.memberName
                        );
                    }
                } else {
                    // Does the module export a top-level __getattr__ function?
                    if (usage.method === 'get') {
                        const getAttrSymbol = ModuleType.getField(baseType, '__getattr__');
                        if (getAttrSymbol) {
                            const isModuleGetAttrSupported =
                                fileInfo.executionEnvironment.pythonVersion >= PythonVersion.V3_7 ||
                                getAttrSymbol
                                    .getDeclarations()
                                    .some((decl) => decl.path.toLowerCase().endsWith('.pyi'));

                            if (isModuleGetAttrSupported) {
                                const getAttrTypeResult = getEffectiveTypeOfSymbolForUsage(getAttrSymbol);
                                if (isFunction(getAttrTypeResult.type)) {
                                    type = getFunctionEffectiveReturnType(getAttrTypeResult.type);
                                    if (getAttrTypeResult.isIncomplete) {
                                        isIncomplete = true;
                                    }
                                }
                            }
                        }
                    }

                    if (!type) {
                        if (!isIncomplete) {
                            addDiagnostic(
                                fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
                                DiagnosticRule.reportGeneralTypeIssues,
                                Localizer.Diagnostic.moduleUnknownMember().format({ name: memberName }),
                                node.memberName
                            );
                        }
                        type = evaluatorOptions.evaluateUnknownImportsAsAny ? AnyType.create() : UnknownType.create();
                    }
                }
                break;
            }

            case TypeCategory.Union: {
                type = mapSubtypes(baseType, (subtype) => {
                    if (isNoneInstance(subtype)) {
                        const typeResult = getTypeFromNoneBase();
                        if (typeResult) {
                            type = addConditionToType(typeResult.type, getTypeCondition(baseType));
                            if (typeResult.isIncomplete) {
                                isIncomplete = true;
                            }
                            return type;
                        } else {
                            if (!isIncomplete) {
                                addDiagnostic(
                                    AnalyzerNodeInfo.getFileInfo(node).diagnosticRuleSet.reportOptionalMemberAccess,
                                    DiagnosticRule.reportOptionalMemberAccess,
                                    Localizer.Diagnostic.noneUnknownMember().format({ name: memberName }),
                                    node.memberName
                                );
                            }
                            return undefined;
                        }
                    } else if (isUnbound(subtype)) {
                        // Don't do anything if it's unbound. The error will already
                        // be reported elsewhere.
                        return undefined;
                    } else {
                        const typeResult = getTypeFromMemberAccessWithBaseType(
                            node,
                            {
                                type: subtype,
                                node,
                                isIncomplete: baseTypeResult.isIncomplete,
                            },
                            usage,
                            EvaluatorFlags.None
                        );
                        if (typeResult.isIncomplete) {
                            isIncomplete = true;
                        }
                        return typeResult.type;
                    }
                });
                break;
            }

            case TypeCategory.Function:
            case TypeCategory.OverloadedFunction: {
                if (memberName === '__defaults__') {
                    // The "__defaults__" member is not currently defined in the "function"
                    // class, so we'll special-case it here.
                    type = AnyType.create();
                } else if (memberName === '__self__') {
                    // The "__self__" member is not currently defined in the "function"
                    // class, so we'll special-case it here.
                    const functionType = isFunction(baseType) ? baseType : baseType.overloads[0];
                    if (
                        functionType.preBoundFlags !== undefined &&
                        (functionType.preBoundFlags & FunctionTypeFlags.StaticMethod) === 0
                    ) {
                        type = functionType.boundToType;
                    }
                } else {
                    if (!functionObj) {
                        type = AnyType.create();
                    } else {
                        type = getTypeFromMemberAccessWithBaseType(
                            node,
                            { type: functionObj, node },
                            usage,
                            flags
                        ).type;
                    }
                }
                break;
            }

            case TypeCategory.None: {
                const typeResult = getTypeFromNoneBase();
                if (typeResult) {
                    type = addConditionToType(typeResult.type, getTypeCondition(baseType));
                    if (typeResult.isIncomplete) {
                        isIncomplete = true;
                    }
                }
                break;
            }

            default:
                diag.addMessage(Localizer.DiagnosticAddendum.typeUnsupported().format({ type: printType(baseType) }));
                break;
        }

        if (!type) {
            const isFunctionRule =
                isFunction(baseType) ||
                isOverloadedFunction(baseType) ||
                (isClassInstance(baseType) && ClassType.isBuiltIn(baseType, 'function'));

            if (!baseTypeResult.isIncomplete) {
                let diagMessage = Localizer.Diagnostic.memberAccess();
                if (usage.method === 'set') {
                    diagMessage = Localizer.Diagnostic.memberSet();
                } else if (usage.method === 'del') {
                    diagMessage = Localizer.Diagnostic.memberDelete();
                }

                // If there is an expected type diagnostic addendum (used for assignments),
                // use that rather than the local diagnostic addendum because it will be
                // more informative.
                if (usage.setExpectedTypeDiag) {
                    diag = usage.setExpectedTypeDiag;
                }

                const [ruleSet, rule] = isFunctionRule
                    ? [fileInfo.diagnosticRuleSet.reportFunctionMemberAccess, DiagnosticRule.reportFunctionMemberAccess]
                    : [fileInfo.diagnosticRuleSet.reportGeneralTypeIssues, DiagnosticRule.reportGeneralTypeIssues];

                addDiagnostic(
                    ruleSet,
                    rule,
                    diagMessage.format({ name: memberName, type: printType(baseType) }) + diag.getString(),
                    node.memberName
                );
            }

            // If this is member access on a function, use "Any" so if the
            // reportFunctionMemberAccess rule is disabled, we don't trigger
            // additional reportUnknownMemberType diagnostics.
            type = isFunctionRule ? AnyType.create() : UnknownType.create();
        }

        // Should we specialize the class?
        if ((flags & EvaluatorFlags.DoNotSpecialize) === 0) {
            if (isInstantiableClass(type) && !type.typeArguments) {
                type = createSpecializedClassType(type, undefined, flags, node);
            }
        }

        if (usage.method === 'get') {
            let skipPartialUnknownCheck = isIncomplete;

            // Don't report an error if the type is a partially-specialized
            // class being passed as an argument. This comes up frequently in
            // cases where a type is passed as an argument (e.g. "defaultdict(list)").
            // It can also come up in cases like "isinstance(x, (list, dict))".
            if (isInstantiableClass(type)) {
                const argNode = ParseTreeUtils.getParentNodeOfType(node, ParseNodeType.Argument);
                if (argNode && argNode?.parent?.nodeType === ParseNodeType.Call) {
                    skipPartialUnknownCheck = true;
                }
            }

            if (!skipPartialUnknownCheck) {
                reportPossibleUnknownAssignment(
                    fileInfo.diagnosticRuleSet.reportUnknownMemberType,
                    DiagnosticRule.reportUnknownMemberType,
                    node.memberName,
                    type,
                    node,
                    /* ignoreEmptyContainers */ false
                );
            }
        }

        return { type, node, isIncomplete, isAsymmetricDescriptor };
    }