leftType: printType()

in packages/pyright-internal/src/analyzer/typeEvaluator.ts [11199:11764]


                                            leftType: printType(leftSubtypeExpanded),
                                            rightType: printType(rightSubtypeExpanded),
                                        })
                                    );
                                }
                                return resultType;
                            }
                        );
                    }
                );
            }
        }

        return type && isNever(type) ? undefined : type;
    }

    function getTypeFromMagicMethodReturn(
        objType: Type,
        args: Type[],
        magicMethodName: string,
        errorNode: ExpressionNode,
        expectedType: Type | undefined
    ): Type | undefined {
        let magicMethodSupported = true;

        // Create a helper lambda for object subtypes.
        const handleSubtype = (subtype: ClassType | TypeVarType) => {
            let magicMethodType: Type | undefined;
            const concreteSubtype = makeTopLevelTypeVarsConcrete(subtype);

            if (isClassInstance(concreteSubtype)) {
                magicMethodType = getTypeFromObjectMember(
                    errorNode,
                    concreteSubtype,
                    magicMethodName,
                    /* usage */ undefined,
                    /* diag */ undefined,
                    MemberAccessFlags.SkipAttributeAccessOverride,
                    subtype
                )?.type;
            } else if (isInstantiableClass(concreteSubtype)) {
                magicMethodType = getTypeFromClassMember(
                    errorNode,
                    concreteSubtype,
                    magicMethodName,
                    /* usage */ undefined,
                    /* diag */ undefined,
                    MemberAccessFlags.SkipAttributeAccessOverride | MemberAccessFlags.ConsiderMetaclassOnly
                )?.type;
            }

            if (magicMethodType) {
                const functionArgs = args.map((arg) => {
                    return {
                        argumentCategory: ArgumentCategory.Simple,
                        type: arg,
                    };
                });

                let callResult: CallResult | undefined;

                useSpeculativeMode(errorNode, () => {
                    callResult = validateCallArguments(
                        errorNode,
                        functionArgs,
                        magicMethodType!,
                        /* typeVarMap */ undefined,
                        /* skipUnknownArgCheck */ true,
                        expectedType
                    );
                });

                if (callResult!.argumentErrors) {
                    magicMethodSupported = false;
                }

                return callResult!.returnType;
            }

            magicMethodSupported = false;
            return undefined;
        };

        const returnType = mapSubtypes(objType, (subtype) => {
            if (isAnyOrUnknown(subtype)) {
                return subtype;
            }

            if (isClassInstance(subtype) || isInstantiableClass(subtype) || isTypeVar(subtype)) {
                return handleSubtype(subtype);
            } else if (isNoneInstance(subtype)) {
                // NoneType derives from 'object', so do the lookup on 'object'
                // in this case.
                const obj = getBuiltInObject(errorNode, 'object');
                if (isClassInstance(obj)) {
                    return handleSubtype(obj);
                }
            }

            magicMethodSupported = false;
            return undefined;
        });

        if (!magicMethodSupported) {
            return undefined;
        }

        return returnType;
    }

    // All functions in Python derive from object, so they inherit all
    // of the capabilities of an object. This function converts a function
    // to an object instance.
    function convertFunctionToObject(type: Type) {
        if (isFunction(type) || isOverloadedFunction(type)) {
            if (objectType) {
                return objectType;
            }
        }

        return type;
    }

    function getTypeFromArrowCallable(node: ArrowCallableNode, flags: EvaluatorFlags): TypeResult {
        // Emit an error if this will cause a runtime exception.
        const fileInfo = AnalyzerNodeInfo.getFileInfo(node);
        if (
            !fileInfo.isStubFile &&
            fileInfo.executionEnvironment.pythonVersion < PythonVersion.V3_11 &&
            (flags & EvaluatorFlags.NotParsedByInterpreter) === 0
        ) {
            addError(Localizer.Diagnostic.arrowCallableIllegal(), node);
        }

        const isIncomplete = false;
        const functionType = FunctionType.createInstantiable('', '', '', FunctionTypeFlags.None);

        const enclosingScope = ParseTreeUtils.getEnclosingClassOrFunction(node);

        // Handle the case where the Callable has no enclosing scope. This can
        // happen in the case where a generic function return type is annotated
        // with a generic type alias that includes a Callable in its definition.
        functionType.details.typeVarScopeId = enclosingScope
            ? getScopeIdForNode(enclosingScope)
            : WildcardTypeVarScopeId;

        const returnAnnotationOptions: AnnotationTypeOptions = {};
        if ((flags & EvaluatorFlags.AssociateTypeVarsWithCurrentScope) !== 0) {
            returnAnnotationOptions.associateTypeVarsWithScope = true;
        }

        let returnType = getTypeOfAnnotation(node.returnTypeAnnotation, returnAnnotationOptions);
        if (node.isAsync) {
            functionType.details.flags |= FunctionTypeFlags.Async;
            const awaitableType = getTypingType(node, 'Awaitable');
            if (awaitableType && isInstantiableClass(awaitableType)) {
                returnType = ClassType.cloneForSpecialization(
                    ClassType.cloneAsInstance(awaitableType),
                    [returnType],
                    /* isTypeArgumentExplicit */ true
                );
            } else {
                returnType = UnknownType.create();
            }
        }

        functionType.details.declaredReturnType = returnType;

        let addPositionalOnly = true;

        const paramAnnotationOptions: AnnotationTypeOptions = {
            allowParamSpec: true,
            allowTypeVarTuple: true,
        };

        if ((flags & EvaluatorFlags.AssociateTypeVarsWithCurrentScope) !== 0) {
            paramAnnotationOptions.associateTypeVarsWithScope = true;
        }

        node.parameters.forEach((param, paramIndex) => {
            const paramType = getTypeOfAnnotation(param.typeAnnotation, paramAnnotationOptions);

            if (isEllipsisType(paramType)) {
                if (param.category !== ParameterCategory.Simple || paramIndex !== 0 || node.parameters.length > 1) {
                    addError(Localizer.Diagnostic.ellipsisContext(), param.typeAnnotation);
                    FunctionType.addParameter(functionType, {
                        category: ParameterCategory.Simple,
                        name: `__p${paramIndex}`,
                        isNameSynthesized: true,
                        type: UnknownType.create(),
                        hasDeclaredType: true,
                    });
                } else {
                    functionType.details.flags |= FunctionTypeFlags.SkipArgsKwargsCompatibilityCheck;
                    FunctionType.addDefaultParameters(functionType);
                    addPositionalOnly = false;
                }
            } else if (param.category === ParameterCategory.Simple) {
                if (isTypeVar(paramType) && paramType.details.isParamSpec) {
                    addError(Localizer.Diagnostic.arrowCallableParamSpec(), param.typeAnnotation);
                }

                if (isTypeVar(paramType) && paramType.details.isVariadic && !paramType.isVariadicUnpacked) {
                    addError(Localizer.Diagnostic.arrowCallableVariadicTypeVar(), param.typeAnnotation);
                }

                FunctionType.addParameter(functionType, {
                    category: ParameterCategory.Simple,
                    name: `__p${paramIndex}`,
                    isNameSynthesized: true,
                    type: convertToInstance(paramType),
                    hasDeclaredType: true,
                });
            } else if (param.category === ParameterCategory.VarArgList) {
                if (!isVariadicTypeVar(paramType)) {
                    addError(Localizer.Diagnostic.arrowCallableNotVariadicTypeVar(), param.typeAnnotation);
                } else {
                    if (paramType.isVariadicUnpacked) {
                        addError(Localizer.Diagnostic.arrowCallableVariadicTypeVarUnpacked(), param.typeAnnotation);
                    }

                    FunctionType.addParameter(functionType, {
                        category: ParameterCategory.Simple,
                        name: `__p${paramIndex}`,
                        isNameSynthesized: true,
                        type: convertToInstance(TypeVarType.cloneForUnpacked(paramType)),
                        hasDeclaredType: true,
                    });
                }
            } else if (param.category === ParameterCategory.VarArgDictionary) {
                if (!isParamSpec(paramType)) {
                    addError(Localizer.Diagnostic.arrowCallableNotParamSpec(), param.typeAnnotation);
                } else if (paramIndex !== node.parameters.length - 1) {
                    addError(Localizer.Diagnostic.arrowCallableParamSpecNotLast(), param.typeAnnotation);
                } else {
                    functionType.details.paramSpec = paramType;
                }
            }
        });

        if (addPositionalOnly) {
            FunctionType.addParameter(functionType, {
                category: ParameterCategory.Simple,
                type: UnknownType.create(),
            });
        }

        return { node, type: functionType, isIncomplete };
    }

    function getTypeFromDictionary(node: DictionaryNode, expectedType: Type | undefined): TypeResult {
        // If the expected type is a union, analyze for each of the subtypes
        // to find one that matches.
        let effectiveExpectedType = expectedType;

        if (expectedType && isUnion(expectedType)) {
            let matchingSubtype: Type | undefined;

            doForEachSubtype(expectedType, (subtype) => {
                if (!matchingSubtype) {
                    const subtypeResult = useSpeculativeMode(node, () => {
                        return getTypeFromDictionaryExpected(node, subtype);
                    });

                    if (subtypeResult) {
                        matchingSubtype = subtype;
                    }
                }
            });

            effectiveExpectedType = matchingSubtype;
        }

        let expectedTypeDiagAddendum = undefined;
        if (effectiveExpectedType) {
            expectedTypeDiagAddendum = new DiagnosticAddendum();
            const result = getTypeFromDictionaryExpected(node, effectiveExpectedType, expectedTypeDiagAddendum);
            if (result) {
                return result;
            }
        }

        const result = getTypeFromDictionaryInferred(node, expectedType)!;
        return { ...result, expectedTypeDiagAddendum };
    }

    // Attempts to infer the type of a dictionary statement. If an expectedType
    // is provided, the resulting type must be compatible with the expected type.
    // If this isn't possible, undefined is returned.
    function getTypeFromDictionaryExpected(
        node: DictionaryNode,
        expectedType: Type,
        expectedDiagAddendum?: DiagnosticAddendum
    ): TypeResult | undefined {
        expectedType = transformPossibleRecursiveTypeAlias(expectedType);

        if (!isClassInstance(expectedType)) {
            return undefined;
        }

        const keyTypes: Type[] = [];
        const valueTypes: Type[] = [];
        let isIncomplete = false;

        // Handle TypedDict's as a special case.
        if (ClassType.isTypedDictClass(expectedType)) {
            const expectedTypedDictEntries = getTypedDictMembersForClass(evaluatorInterface, expectedType);

            // Infer the key and value types if possible.
            if (
                getKeyAndValueTypesFromDictionary(
                    node,
                    keyTypes,
                    valueTypes,
                    !!expectedType,
                    /* expectedKeyType */ undefined,
                    /* expectedValueType */ undefined,
                    expectedTypedDictEntries,
                    expectedDiagAddendum
                )
            ) {
                isIncomplete = true;
            }

            if (ClassType.isTypedDictClass(expectedType)) {
                const resultTypedDict = assignToTypedDict(
                    evaluatorInterface,
                    expectedType,
                    keyTypes,
                    valueTypes,
                    expectedDiagAddendum
                );
                if (resultTypedDict) {
                    return {
                        type: resultTypedDict,
                        node,
                        isIncomplete,
                    };
                }
            }

            return undefined;
        }

        const builtInDict = getBuiltInObject(node, 'dict');
        if (!isClassInstance(builtInDict)) {
            return undefined;
        }

        const dictTypeVarMap = new TypeVarMap(getTypeVarScopeId(builtInDict));
        if (
            !populateTypeVarMapBasedOnExpectedType(
                builtInDict,
                expectedType,
                dictTypeVarMap,
                getTypeVarScopesForNode(node)
            )
        ) {
            return undefined;
        }

        const specializedDict = applySolvedTypeVars(
            ClassType.cloneAsInstantiable(builtInDict),
            dictTypeVarMap
        ) as ClassType;
        if (!specializedDict.typeArguments || specializedDict.typeArguments.length !== 2) {
            return undefined;
        }

        const expectedKeyType = specializedDict.typeArguments[0];
        const expectedValueType = specializedDict.typeArguments[1];

        // Infer the key and value types if possible.
        if (
            getKeyAndValueTypesFromDictionary(
                node,
                keyTypes,
                valueTypes,
                !!expectedType,
                expectedKeyType,
                expectedValueType,
                undefined,
                expectedDiagAddendum
            )
        ) {
            isIncomplete = true;
        }

        // Dict and MutableMapping types have invariant value types, so they
        // cannot be narrowed further. Other super-types like Mapping, Collection,
        // and Iterable use covariant value types, so they can be narrowed.
        const isValueTypeInvariant =
            isClassInstance(expectedType) &&
            (ClassType.isBuiltIn(expectedType, 'dict') || ClassType.isBuiltIn(expectedType, 'MutableMapping'));

        const specializedKeyType = inferTypeArgFromExpectedType(expectedKeyType, keyTypes, /* isNarrowable */ false);
        const specializedValueType = inferTypeArgFromExpectedType(
            expectedValueType,
            valueTypes,
            /* isNarrowable */ !isValueTypeInvariant
        );
        if (!specializedKeyType || !specializedValueType) {
            return undefined;
        }

        const type = getBuiltInObject(node, 'dict', [specializedKeyType, specializedValueType]);
        return { type, node, isIncomplete };
    }

    // Attempts to infer the type of a dictionary statement. If an expectedType
    // is provided, the resulting type must be compatible with the expected type.
    // If this isn't possible, undefined is returned.
    function getTypeFromDictionaryInferred(node: DictionaryNode, expectedType: Type | undefined): TypeResult {
        let keyType: Type = expectedType ? AnyType.create() : UnknownType.create();
        let valueType: Type = expectedType ? AnyType.create() : UnknownType.create();

        let keyTypes: Type[] = [];
        let valueTypes: Type[] = [];

        let isEmptyContainer = false;
        let isIncomplete = false;

        // Infer the key and value types if possible.
        if (
            getKeyAndValueTypesFromDictionary(
                node,
                keyTypes,
                valueTypes,
                !expectedType,
                expectedType ? AnyType.create() : undefined,
                expectedType ? AnyType.create() : undefined
            )
        ) {
            isIncomplete = true;
        }

        // Strip any literal values.
        keyTypes = keyTypes.map((t) => stripLiteralValue(t));
        valueTypes = valueTypes.map((t) => stripLiteralValue(t));

        keyType = keyTypes.length > 0 ? combineTypes(keyTypes) : expectedType ? AnyType.create() : UnknownType.create();

        // If the value type differs and we're not using "strict inference mode",
        // we need to back off because we can't properly represent the mappings
        // between different keys and associated value types. If all the values
        // are the same type, we'll assume that all values in this dictionary should
        // be the same.
        if (valueTypes.length > 0) {
            if (AnalyzerNodeInfo.getFileInfo(node).diagnosticRuleSet.strictDictionaryInference || !!expectedType) {
                valueType = combineTypes(valueTypes);
            } else {
                valueType = areTypesSame(valueTypes, /* ignorePseudoGeneric */ true)
                    ? valueTypes[0]
                    : expectedType
                    ? AnyType.create()
                    : UnknownType.create();
            }
        } else {
            valueType = expectedType ? AnyType.create() : UnknownType.create();
            isEmptyContainer = true;
        }

        const dictClass = getBuiltInType(node, 'dict');
        const type = isInstantiableClass(dictClass)
            ? ClassType.cloneAsInstance(
                  ClassType.cloneForSpecialization(
                      dictClass,
                      [keyType, valueType],
                      /* isTypeArgumentExplicit */ true,
                      /* includeSubclasses */ undefined,
                      /* TupleTypeArguments */ undefined,
                      isEmptyContainer
                  )
              )
            : UnknownType.create();

        return { type, node, isIncomplete };
    }

    function getKeyAndValueTypesFromDictionary(
        node: DictionaryNode,
        keyTypes: Type[],
        valueTypes: Type[],
        limitEntryCount: boolean,
        expectedKeyType?: Type,
        expectedValueType?: Type,
        expectedTypedDictEntries?: Map<string, TypedDictEntry>,
        expectedDiagAddendum?: DiagnosticAddendum
    ): boolean {
        let isIncomplete = false;

        // Infer the key and value types if possible.
        node.entries.forEach((entryNode, index) => {
            let addUnknown = true;

            if (entryNode.nodeType === ParseNodeType.DictionaryKeyEntry) {
                const keyTypeResult = getTypeOfExpression(entryNode.keyExpression, expectedKeyType);
                if (keyTypeResult.isIncomplete) {
                    isIncomplete = true;
                }

                let keyType = keyTypeResult.type;
                if (expectedKeyType) {
                    const adjExpectedKeyType = makeTopLevelTypeVarsConcrete(expectedKeyType);
                    if (!isAnyOrUnknown(adjExpectedKeyType)) {
                        if (canAssignType(adjExpectedKeyType, keyType)) {
                            keyType = adjExpectedKeyType;
                        }
                    }
                }

                let valueTypeResult: TypeResult;

                if (
                    expectedTypedDictEntries &&
                    isClassInstance(keyType) &&
                    ClassType.isBuiltIn(keyType, 'str') &&
                    isLiteralType(keyType) &&
                    expectedTypedDictEntries.has(keyType.literalValue as string)
                ) {
                    valueTypeResult = getTypeOfExpression(
                        entryNode.valueExpression,
                        expectedTypedDictEntries.get(keyType.literalValue as string)!.valueType
                    );
                } else {
                    valueTypeResult = getTypeOfExpression(entryNode.valueExpression, expectedValueType);
                }

                if (expectedDiagAddendum && valueTypeResult.expectedTypeDiagAddendum) {
                    expectedDiagAddendum.addAddendum(valueTypeResult.expectedTypeDiagAddendum);
                }

                const valueType = valueTypeResult.type;
                if (valueTypeResult.isIncomplete) {
                    isIncomplete = true;
                }

                if (!limitEntryCount || index < maxEntriesToUseForInference) {
                    keyTypes.push(keyType);
                    valueTypes.push(valueType);
                }
                addUnknown = false;
            } else if (entryNode.nodeType === ParseNodeType.DictionaryExpandEntry) {
                const unexpandedTypeResult = getTypeOfExpression(entryNode.expandExpression);
                if (unexpandedTypeResult.isIncomplete) {
                    isIncomplete = true;
                }

                const unexpandedType = unexpandedTypeResult.type;
                if (isAnyOrUnknown(unexpandedType)) {
                    addUnknown = false;
                } else {
                    const mappingType = getTypingType(node, 'Mapping');
                    if (mappingType && isInstantiableClass(mappingType)) {
                        const mappingTypeVarMap = new TypeVarMap(getTypeVarScopeId(mappingType));
                        if (
                            canAssignType(
                                ClassType.cloneAsInstance(mappingType),
                                unexpandedType,
                                /* diag */ undefined,
                                mappingTypeVarMap
                            )
                        ) {
                            const specializedMapping = applySolvedTypeVars(mappingType, mappingTypeVarMap) as ClassType;
                            const typeArgs = specializedMapping.typeArguments;
                            if (typeArgs && typeArgs.length >= 2) {