in packages/pyright-internal/src/analyzer/typeEvaluator.ts [834:1232]
function getTypeOfExpression(node: ExpressionNode, expectedType?: Type, flags = EvaluatorFlags.None): TypeResult {
// Is this type already cached?
const cachedType = readTypeCache(node, flags);
if (cachedType) {
return { type: cachedType, node };
} else {
// Is it cached in the speculative type cache?
const speculativeCachedType = speculativeTypeTracker.getSpeculativeType(node, expectedType);
if (speculativeCachedType) {
return { type: speculativeCachedType, node };
}
}
// This is a frequently-called routine, so it's a good place to call
// the cancellation check. If the operation is canceled, an exception
// will be thrown at this point.
checkForCancellation();
const expectedTypeAlt = transformPossibleRecursiveTypeAlias(expectedType);
// If we haven't already fetched some core type definitions from the
// typeshed stubs, do so here. It would be better to fetch this when it's
// needed in canAssignType, but we don't have access to the parse tree
// at that point.
initializedBasicTypes(node);
let typeResult: TypeResult | undefined;
let reportExpectingTypeErrors = (flags & EvaluatorFlags.ExpectingType) !== 0;
switch (node.nodeType) {
case ParseNodeType.Name: {
typeResult = getTypeFromName(node, flags);
break;
}
case ParseNodeType.MemberAccess: {
typeResult = getTypeFromMemberAccess(node, flags);
// Cache the type information in the member name node as well.
if (!isTypeAliasPlaceholder(typeResult.type)) {
writeTypeCache(node.memberName, typeResult.type, flags, !!typeResult.isIncomplete);
}
break;
}
case ParseNodeType.Index: {
typeResult = getTypeFromIndex(node, flags);
break;
}
case ParseNodeType.Call: {
if ((flags & EvaluatorFlags.ExpectingTypeAnnotation) !== 0) {
// Evaluate the expression still so symbols are marked as accessed.
getTypeFromCall(node, expectedTypeAlt);
addDiagnostic(
AnalyzerNodeInfo.getFileInfo(node).diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
Localizer.Diagnostic.typeAnnotationCall(),
node
);
typeResult = { node, type: UnknownType.create() };
} else {
typeResult = getTypeFromCall(node, expectedTypeAlt);
}
break;
}
case ParseNodeType.Tuple: {
typeResult = getTypeFromTuple(node, expectedTypeAlt, flags);
break;
}
case ParseNodeType.Constant: {
typeResult = getTypeFromConstant(node, flags);
break;
}
case ParseNodeType.StringList: {
const expectingType =
(flags & EvaluatorFlags.EvaluateStringLiteralAsType) !== 0 && !isAnnotationLiteralValue(node);
if (expectingType) {
if (node.typeAnnotation) {
typeResult = getTypeOfExpression(
node.typeAnnotation,
undefined,
flags |
EvaluatorFlags.AllowForwardReferences |
EvaluatorFlags.NotParsedByInterpreter |
EvaluatorFlags.ExpectingType
);
} else if (!node.typeAnnotation && node.strings.length === 1) {
// We didn't know at parse time that this string node was going
// to be evaluated as a forward-referenced type. We need
// to re-invoke the parser at this stage.
const expr = parseStringAsTypeAnnotation(node);
if (expr) {
typeResult = getTypeOfExpression(
expr,
undefined,
flags |
EvaluatorFlags.AllowForwardReferences |
EvaluatorFlags.NotParsedByInterpreter |
EvaluatorFlags.ExpectingType
);
}
}
if (!typeResult) {
const fileInfo = AnalyzerNodeInfo.getFileInfo(node);
addDiagnostic(
fileInfo.diagnosticRuleSet.reportGeneralTypeIssues,
DiagnosticRule.reportGeneralTypeIssues,
Localizer.Diagnostic.expectedTypeNotString(),
node
);
typeResult = { node, type: UnknownType.create() };
}
// Don't report expecting type errors again. We will have already
// reported them when analyzing the contents of the string.
reportExpectingTypeErrors = false;
} else {
// Evaluate the format string expressions in this context.
node.strings.forEach((str) => {
if (str.nodeType === ParseNodeType.FormatString) {
str.expressions.forEach((expr) => {
getTypeOfExpression(expr);
});
}
});
const isBytes = (node.strings[0].token.flags & StringTokenFlags.Bytes) !== 0;
// Don't create a literal type if it's an f-string.
if (node.strings.some((str) => str.nodeType === ParseNodeType.FormatString)) {
typeResult = {
node,
type: getBuiltInObject(node, isBytes ? 'bytes' : 'str'),
};
} else {
typeResult = {
node,
type: cloneBuiltinObjectWithLiteral(
node,
isBytes ? 'bytes' : 'str',
node.strings.map((s) => s.value).join('')
),
};
}
}
break;
}
case ParseNodeType.Number: {
if (node.isImaginary) {
typeResult = { node, type: getBuiltInObject(node, 'complex') };
} else if (node.isInteger) {
typeResult = { node, type: cloneBuiltinObjectWithLiteral(node, 'int', node.value) };
} else {
typeResult = { node, type: getBuiltInObject(node, 'float') };
}
break;
}
case ParseNodeType.Ellipsis: {
if ((flags & EvaluatorFlags.ConvertEllipsisToAny) !== 0) {
typeResult = { type: AnyType.create(/* isEllipsis */ true), node };
} else if ((flags & EvaluatorFlags.ConvertEllipsisToUnknown) !== 0) {
typeResult = { type: UnknownType.create(), node };
} else {
const ellipsisType = getBuiltInObject(node, 'ellipsis') || AnyType.create();
typeResult = { type: ellipsisType, node };
}
break;
}
case ParseNodeType.UnaryOperation: {
typeResult = getTypeFromUnaryOperation(node, expectedTypeAlt);
break;
}
case ParseNodeType.BinaryOperation: {
typeResult = getTypeFromBinaryOperation(node, expectedTypeAlt, flags);
break;
}
case ParseNodeType.AugmentedAssignment: {
typeResult = getTypeFromAugmentedAssignment(node, expectedTypeAlt);
assignTypeToExpression(
node.destExpression,
typeResult.type,
!!typeResult.isIncomplete,
node.rightExpression
);
break;
}
case ParseNodeType.List:
case ParseNodeType.Set: {
typeResult = getTypeFromListOrSet(node, expectedTypeAlt);
break;
}
case ParseNodeType.Slice: {
typeResult = getTypeFromSlice(node);
break;
}
case ParseNodeType.Await: {
const effectiveExpectedType = expectedType
? createAwaitableReturnType(node, expectedType, /* isGenerator */ false)
: undefined;
const exprTypeResult = getTypeOfExpression(node.expression, effectiveExpectedType, flags);
typeResult = {
type: getTypeFromAwaitable(exprTypeResult.type, node.expression),
node,
};
if (exprTypeResult.isIncomplete) {
typeResult.isIncomplete = true;
}
break;
}
case ParseNodeType.Ternary: {
typeResult = getTypeFromTernary(node, flags, expectedTypeAlt);
break;
}
case ParseNodeType.ListComprehension: {
typeResult = getTypeFromListComprehension(node, expectedTypeAlt);
break;
}
case ParseNodeType.Dictionary: {
typeResult = getTypeFromDictionary(node, expectedTypeAlt);
break;
}
case ParseNodeType.Lambda: {
typeResult = getTypeFromLambda(node, expectedTypeAlt);
break;
}
case ParseNodeType.ArrowCallable: {
typeResult = getTypeFromArrowCallable(node, flags);
break;
}
case ParseNodeType.Assignment: {
typeResult = getTypeOfExpression(node.rightExpression);
assignTypeToExpression(
node.leftExpression,
typeResult.type,
/* isTypeIncomplete */ false,
node.rightExpression,
/* ignoreEmptyContainers */ true
);
break;
}
case ParseNodeType.AssignmentExpression: {
typeResult = getTypeOfExpression(node.rightExpression);
assignTypeToExpression(
node.name,
typeResult.type,
/* isTypeIncomplete */ false,
node.rightExpression,
/* ignoreEmptyContainers */ true
);
break;
}
case ParseNodeType.Yield: {
typeResult = getTypeFromYield(node);
break;
}
case ParseNodeType.YieldFrom: {
typeResult = getTypeFromYieldFrom(node);
break;
}
case ParseNodeType.Unpack: {
let iterExpectedType: Type | undefined;
if (expectedTypeAlt) {
const iterableType = getBuiltInType(node, 'Iterable');
if (iterableType && isInstantiableClass(iterableType)) {
iterExpectedType = ClassType.cloneAsInstance(
ClassType.cloneForSpecialization(
iterableType,
[expectedTypeAlt],
/* isTypeArgumentExplicit */ true
)
);
}
}
const iterTypeResult = getTypeOfExpression(node.expression, iterExpectedType, flags);
const iterType = iterTypeResult.type;
if (
(flags & EvaluatorFlags.TypeVarTupleDisallowed) === 0 &&
isVariadicTypeVar(iterType) &&
!iterType.isVariadicUnpacked
) {
typeResult = { type: TypeVarType.cloneForUnpacked(iterType), node };
} else {
const type = getTypeFromIterator(iterType, /* isAsync */ false, node) || UnknownType.create();
typeResult = { type, unpackedType: iterType, node, isIncomplete: iterTypeResult.isIncomplete };
}
break;
}
case ParseNodeType.TypeAnnotation: {
typeResult = getTypeOfExpression(
node.typeAnnotation,
undefined,
EvaluatorFlags.EvaluateStringLiteralAsType |
EvaluatorFlags.ParamSpecDisallowed |
EvaluatorFlags.TypeVarTupleDisallowed |
EvaluatorFlags.ExpectingType |
EvaluatorFlags.ExpectingTypeAnnotation |
EvaluatorFlags.VariableTypeAnnotation
);
break;
}
case ParseNodeType.Error: {
// Evaluate the child expression as best we can so the
// type information is cached for the completion handler.
suppressDiagnostics(node, () => {
if (node.child) {
getTypeOfExpression(node.child);
}
});
typeResult = { type: UnknownType.create(), node };
break;
}
}
if (!typeResult) {
// We shouldn't get here. If we do, report an error.
fail(`Unhandled expression type '${ParseTreeUtils.printExpression(node)}'`);
}
if (reportExpectingTypeErrors && !typeResult.isIncomplete) {
if (flags & EvaluatorFlags.TypeVarTupleDisallowed) {
if (
isTypeVar(typeResult.type) &&
typeResult.type.details.isVariadic &&
!typeResult.type.isVariadicInUnion
) {
addError(Localizer.Diagnostic.typeVarTupleContext(), node);
typeResult.type = UnknownType.create();
}
}
if (!TypeBase.isInstantiable(typeResult.type)) {
const isEmptyVariadic =
isClassInstance(typeResult.type) &&
ClassType.isTupleClass(typeResult.type) &&
typeResult.type.tupleTypeArguments?.length === 0;
if (!isEmptyVariadic) {
addExpectedClassDiagnostic(typeResult.type, node);
typeResult.type = UnknownType.create();
}
}
}
if (flags & EvaluatorFlags.DisallowRecursiveTypeAliasPlaceholder) {
if (isTypeAliasPlaceholder(typeResult.type)) {
typeResult.type.details.illegalRecursionDetected = true;
}
}
// Don't update the type cache with an unbound type that results from
// a resolution cycle. The cache will be updated when the stack unwinds
// and the type is fully evaluated.
if (!isTypeAliasPlaceholder(typeResult.type)) {
writeTypeCache(
node,
typeResult.type,
flags,
!!typeResult.isIncomplete,
expectedType,
/* allowSpeculativeCaching */ true
);
if (expectedType && !isAnyOrUnknown(expectedType)) {
expectedTypeCache.set(node.id, expectedType);
}
}
return typeResult;
}