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) {