Reduced duplicated code in type alias code paths. No functional change.

This commit is contained in:
Eric Traut 2024-07-01 20:28:01 -07:00
parent 139d095cd6
commit 3da465b321

View File

@ -16123,19 +16123,8 @@ export function createTypeEvaluator(
flags |= EvaluatorFlags.DoNotSpecialize; flags |= EvaluatorFlags.DoNotSpecialize;
} }
if (isDeclaredTypeAlias(node.leftExpression)) {
flags |=
EvaluatorFlags.ExpectingInstantiableType |
EvaluatorFlags.ExpectingTypeAnnotation |
EvaluatorFlags.EvaluateStringLiteralAsType |
EvaluatorFlags.DisallowParamSpec |
EvaluatorFlags.DisallowTypeVarTuple |
EvaluatorFlags.DisallowClassVar;
flags &= ~EvaluatorFlags.DoNotSpecialize;
}
// Is this type already cached? // Is this type already cached?
let rightHandType = readTypeCache(node.rightExpression, flags); let rightHandType = readTypeCache(node.rightExpression, /* flags */ undefined);
let isIncomplete = false; let isIncomplete = false;
let expectedTypeDiagAddendum: DiagnosticAddendum | undefined; let expectedTypeDiagAddendum: DiagnosticAddendum | undefined;
@ -16152,14 +16141,21 @@ export function createTypeEvaluator(
if (!rightHandType) { if (!rightHandType) {
// Determine whether there is a declared type. // Determine whether there is a declared type.
const declaredType = getDeclaredTypeForExpression(node.leftExpression, { const declaredType = getDeclaredTypeForExpression(node.leftExpression, { method: 'set' });
method: 'set',
});
let typeAliasNameNode: NameNode | undefined; let typeAliasNameNode: NameNode | undefined;
let isSpeculativeTypeAlias = false; let isSpeculativeTypeAlias = false;
if (isDeclaredTypeAlias(node.leftExpression)) { if (isDeclaredTypeAlias(node.leftExpression)) {
flags |=
EvaluatorFlags.ExpectingInstantiableType |
EvaluatorFlags.ExpectingTypeAnnotation |
EvaluatorFlags.EvaluateStringLiteralAsType |
EvaluatorFlags.DisallowParamSpec |
EvaluatorFlags.DisallowTypeVarTuple |
EvaluatorFlags.DisallowClassVar;
flags &= ~EvaluatorFlags.DoNotSpecialize;
typeAliasNameNode = (node.leftExpression as TypeAnnotationNode).valueExpression as NameNode; typeAliasNameNode = (node.leftExpression as TypeAnnotationNode).valueExpression as NameNode;
if (!isLegalTypeAliasExpressionForm(node.rightExpression)) { if (!isLegalTypeAliasExpressionForm(node.rightExpression)) {
@ -16175,6 +16171,7 @@ export function createTypeEvaluator(
node.leftExpression.value, node.leftExpression.value,
/* honorCodeFlow */ false /* honorCodeFlow */ false
); );
if (symbolWithScope) { if (symbolWithScope) {
const decls = symbolWithScope.symbol.getDeclarations(); const decls = symbolWithScope.symbol.getDeclarations();
if (decls.length === 1 && isPossibleTypeAliasOrTypedDict(decls[0])) { if (decls.length === 1 && isPossibleTypeAliasOrTypedDict(decls[0])) {
@ -16188,17 +16185,12 @@ export function createTypeEvaluator(
// evaluating it. This allows us to handle recursive definitions. // evaluating it. This allows us to handle recursive definitions.
let typeAliasTypeVar: TypeVarType | undefined; let typeAliasTypeVar: TypeVarType | undefined;
if (typeAliasNameNode) { if (typeAliasNameNode) {
typeAliasTypeVar = TypeVarType.createInstantiable(`__type_alias_${typeAliasNameNode.value}`); typeAliasTypeVar = synthesizeTypeAliasPlaceholder(typeAliasNameNode);
typeAliasTypeVar.details.isSynthesized = true;
typeAliasTypeVar.details.recursiveTypeAliasName = typeAliasNameNode.value;
const scopeId = ParseTreeUtils.getScopeIdForNode(typeAliasNameNode);
typeAliasTypeVar.details.recursiveTypeAliasScopeId = scopeId;
typeAliasTypeVar.details.recursiveTypeAliasIsPep695Syntax = false;
typeAliasTypeVar.scopeId = scopeId;
// Write the type back to the type cache. It will be replaced below. // Write the type to the type cache to support recursive type alias definitions.
writeTypeCache(node, { type: typeAliasTypeVar }, /* flags */ undefined); writeTypeCache(node, { type: typeAliasTypeVar }, /* flags */ undefined);
writeTypeCache(node.leftExpression, { type: typeAliasTypeVar }, /* flags */ undefined); writeTypeCache(node.leftExpression, { type: typeAliasTypeVar }, /* flags */ undefined);
if (node.leftExpression.nodeType === ParseNodeType.TypeAnnotation) { if (node.leftExpression.nodeType === ParseNodeType.TypeAnnotation) {
writeTypeCache( writeTypeCache(
node.leftExpression.valueExpression, node.leftExpression.valueExpression,
@ -16281,39 +16273,18 @@ export function createTypeEvaluator(
writeTypeCache(node, { type: rightHandType, isIncomplete }, EvaluatorFlags.None); writeTypeCache(node, { type: rightHandType, isIncomplete }, EvaluatorFlags.None);
} }
function isPossibleTypeAliasOrTypedDict(decl: Declaration) { // Synthesize a TypeVar that acts as a placeholder for a type alias. This allows
if (isPossibleTypeAliasDeclaration(decl)) { // the type alias definition to refer to itself.
return true; function synthesizeTypeAliasPlaceholder(nameNode: NameNode): TypeVarType {
} const placeholder = TypeVarType.createInstantiable(`__type_alias_${nameNode.value}`);
placeholder.details.isSynthesized = true;
placeholder.details.recursiveTypeAliasName = nameNode.value;
const scopeId = ParseTreeUtils.getScopeIdForNode(nameNode);
placeholder.details.recursiveTypeAliasScopeId = scopeId;
placeholder.details.recursiveTypeAliasIsPep695Syntax = false;
placeholder.scopeId = scopeId;
if ( return placeholder;
decl.type === DeclarationType.Variable &&
decl.node.parent &&
decl.node.parent.nodeType === ParseNodeType.Assignment &&
decl.node.parent.rightExpression?.nodeType === ParseNodeType.Call
) {
const callLeftNode = decl.node.parent.rightExpression.leftExpression;
// Use a simple heuristic to determine whether this is potentially
// a call to the TypedDict call. This avoids the expensive (and potentially
// recursive) call to getTypeOfExpression in cases where it's not needed.
if (
(callLeftNode.nodeType === ParseNodeType.Name && callLeftNode.value) === 'TypedDict' ||
(callLeftNode.nodeType === ParseNodeType.MemberAccess &&
callLeftNode.memberName.value === 'TypedDict' &&
callLeftNode.leftExpression.nodeType === ParseNodeType.Name)
) {
// See if this is a call to TypedDict. We want to support
// recursive type references in a TypedDict call.
const callType = getTypeOfExpression(callLeftNode, EvaluatorFlags.CallBaseDefaults).type;
if (isInstantiableClass(callType) && ClassType.isBuiltIn(callType, 'TypedDict')) {
return true;
}
}
}
return false;
} }
// Evaluates the type of a type alias (i.e. "type") statement. This code // Evaluates the type of a type alias (i.e. "type") statement. This code
@ -16352,15 +16323,9 @@ export function createTypeEvaluator(
// Synthesize a type variable that represents the type alias while we're // Synthesize a type variable that represents the type alias while we're
// evaluating it. This allows us to handle recursive definitions. // evaluating it. This allows us to handle recursive definitions.
const typeAliasTypeVar = TypeVarType.createInstantiable(`__type_alias_${nameNode.value}`); const typeAliasTypeVar = synthesizeTypeAliasPlaceholder(nameNode);
typeAliasTypeVar.details.isSynthesized = true;
typeAliasTypeVar.details.recursiveTypeAliasName = nameNode.value;
const scopeId = ParseTreeUtils.getScopeIdForNode(nameNode);
typeAliasTypeVar.details.recursiveTypeAliasScopeId = scopeId;
typeAliasTypeVar.details.recursiveTypeAliasIsPep695Syntax = isPep695Syntax;
typeAliasTypeVar.scopeId = scopeId;
// Write the type to the type cache. It will be replaced below. // Write the type to the type cache to support recursive type alias definitions.
writeTypeCache(nameNode, { type: typeAliasTypeVar }, /* flags */ undefined); writeTypeCache(nameNode, { type: typeAliasTypeVar }, /* flags */ undefined);
// Set a partial type to handle recursive (self-referential) type aliases. // Set a partial type to handle recursive (self-referential) type aliases.
@ -27147,6 +27112,43 @@ export function createTypeEvaluator(
return isLegal; return isLegal;
} }
function isPossibleTypeAliasOrTypedDict(decl: Declaration) {
return isPossibleTypeAliasDeclaration(decl) || isPossibleTypeDictFactoryCall(decl);
}
function isPossibleTypeDictFactoryCall(decl: Declaration) {
if (
decl.type !== DeclarationType.Variable ||
!decl.node.parent ||
decl.node.parent.nodeType !== ParseNodeType.Assignment ||
decl.node.parent.rightExpression?.nodeType !== ParseNodeType.Call
) {
return false;
}
const callLeftNode = decl.node.parent.rightExpression.leftExpression;
// Use a simple heuristic to determine whether this is potentially
// a call to the TypedDict call. This avoids the expensive (and potentially
// recursive) call to getTypeOfExpression in cases where it's not needed.
if (
(callLeftNode.nodeType === ParseNodeType.Name && callLeftNode.value) === 'TypedDict' ||
(callLeftNode.nodeType === ParseNodeType.MemberAccess &&
callLeftNode.memberName.value === 'TypedDict' &&
callLeftNode.leftExpression.nodeType === ParseNodeType.Name)
) {
// See if this is a call to TypedDict. We want to support
// recursive type references in a TypedDict call.
const callType = getTypeOfExpression(callLeftNode, EvaluatorFlags.CallBaseDefaults).type;
if (isInstantiableClass(callType) && ClassType.isBuiltIn(callType, 'TypedDict')) {
return true;
}
}
return false;
}
function printObjectTypeForClass(type: ClassType): string { function printObjectTypeForClass(type: ClassType): string {
return TypePrinter.printObjectTypeForClass( return TypePrinter.printObjectTypeForClass(
type, type,