Refactor and improve caching in getTypeOfSymbol callees (#25842)

* Refactor+improve caching in getTypeOfSymbol

1. Always cache calls to getTypeOfSymbol, even in the error case.
2. JS expando types are now cached on the original symbol as well as the
cloned symbol. Previously they were only cached on the cloned symbol.
3. Large callees of getTypeOfSymbol (variable/param/property,
func/class/enum/module, and accessors) now handle only caching and
delegate to -Worker functions whose return values are cached
unconditionally (unlike previously).

* Fix circularity detection in getTypeOfFuncClassEnumModule

Previously, successfully obtaining a type from a js special property
declaration would forget to pop the circularity detection stack and
check its value.
This commit is contained in:
Nathan Shively-Sanders 2018-07-31 13:07:16 -07:00 committed by GitHub
parent ab5f04721d
commit 4821f81ce7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -4993,39 +4993,42 @@ namespace ts {
function getTypeOfVariableOrParameterOrProperty(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
return links.type || (links.type = getTypeOfVariableOrParameterOrPropertyWorker(symbol));
}
function getTypeOfVariableOrParameterOrPropertyWorker(symbol: Symbol) {
// Handle prototype property
if (symbol.flags & SymbolFlags.Prototype) {
return links.type = getTypeOfPrototypeProperty(symbol);
return getTypeOfPrototypeProperty(symbol);
}
// CommonsJS require and module both have type any.
if (symbol === requireSymbol) {
return links.type = anyType;
return anyType;
}
if (symbol.flags & SymbolFlags.ModuleExports) {
const fileSymbol = getSymbolOfNode(getSourceFileOfNode(symbol.valueDeclaration));
const members = createSymbolTable();
members.set("exports" as __String, fileSymbol);
return links.type = createAnonymousType(symbol, members, emptyArray, emptyArray, undefined, undefined);
return createAnonymousType(symbol, members, emptyArray, emptyArray, undefined, undefined);
}
// Handle catch clause variables
const declaration = symbol.valueDeclaration;
if (isCatchClauseVariableDeclarationOrBindingElement(declaration)) {
return links.type = anyType;
return anyType;
}
// Handle export default expressions
if (isSourceFile(declaration)) {
const jsonSourceFile = cast(declaration, isJsonSourceFile);
return links.type = jsonSourceFile.statements.length ? checkExpression(jsonSourceFile.statements[0].expression) : emptyObjectType;
return jsonSourceFile.statements.length ? checkExpression(jsonSourceFile.statements[0].expression) : emptyObjectType;
}
if (declaration.kind === SyntaxKind.ExportAssignment) {
return links.type = checkExpression((<ExportAssignment>declaration).expression);
return checkExpression((<ExportAssignment>declaration).expression);
}
// Handle variable, parameter or property
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return errorType;
}
let type = getJSSpecialType(symbol, declaration);
if (!type) {
if (isJSDocPropertyLikeTag(declaration)
@ -5068,9 +5071,7 @@ namespace ts {
if (!popTypeResolution()) {
type = reportCircularityError(symbol);
}
links.type = type;
}
return links.type;
return type;
}
function getJSSpecialType(symbol: Symbol, decl: Declaration): Type | undefined {
@ -5148,14 +5149,17 @@ namespace ts {
function getTypeOfAccessors(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
return links.type || (links.type = getTypeOfAccessorsWorker(symbol));
}
function getTypeOfAccessorsWorker(symbol: Symbol): Type {
const getter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.GetAccessor);
const setter = getDeclarationOfKind<AccessorDeclaration>(symbol, SyntaxKind.SetAccessor);
if (getter && isInJavaScriptFile(getter)) {
const jsDocType = getTypeForDeclarationFromJSDocComment(getter);
if (jsDocType) {
return links.type = jsDocType;
return jsDocType;
}
}
@ -5203,9 +5207,7 @@ namespace ts {
error(getter, Diagnostics._0_implicitly_has_return_type_any_because_it_does_not_have_a_return_type_annotation_and_is_referenced_directly_or_indirectly_in_one_of_its_return_expressions, symbolToString(symbol));
}
}
links.type = type;
}
return links.type;
return type;
}
function getBaseTypeVariableOfClass(symbol: Symbol) {
@ -5215,6 +5217,7 @@ namespace ts {
function getTypeOfFuncClassEnumModule(symbol: Symbol): Type {
let links = getSymbolLinks(symbol);
const originalLinks = links;
if (!links.type) {
const jsDeclaration = getDeclarationOfJSInitializer(symbol.valueDeclaration);
if (jsDeclaration) {
@ -5233,48 +5236,47 @@ namespace ts {
}
}
}
if (symbol.flags & SymbolFlags.Module && isShorthandAmbientModuleSymbol(symbol)) {
links.type = anyType;
}
else if (symbol.valueDeclaration.kind === SyntaxKind.BinaryExpression ||
symbol.valueDeclaration.kind === SyntaxKind.PropertyAccessExpression && symbol.valueDeclaration.parent.kind === SyntaxKind.BinaryExpression) {
links.type = getWidenedTypeFromJSSpecialPropertyDeclarations(symbol);
}
else {
if (symbol.flags & SymbolFlags.ValueModule && symbol.valueDeclaration && isSourceFile(symbol.valueDeclaration) && symbol.valueDeclaration.commonJsModuleIndicator) {
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return errorType;
}
const resolvedModule = resolveExternalModuleSymbol(symbol);
if (resolvedModule !== symbol) {
const exportEquals = getMergedSymbol(symbol.exports!.get(InternalSymbolName.ExportEquals)!);
links.type = getWidenedTypeFromJSSpecialPropertyDeclarations(exportEquals, exportEquals === resolvedModule ? undefined : resolvedModule);
}
if (!popTypeResolution()) {
links.type = reportCircularityError(symbol);
}
}
if (!links.type) {
const type = createObjectType(ObjectFlags.Anonymous, symbol);
if (symbol.flags & SymbolFlags.Class) {
const baseTypeVariable = getBaseTypeVariableOfClass(symbol);
links.type = baseTypeVariable ? getIntersectionType([type, baseTypeVariable]) : type;
}
else {
links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ? getOptionalType(type) : type;
}
}
}
originalLinks.type = links.type = getTypeOfFuncClassEnumModuleWorker(symbol);
}
return links.type;
}
function getTypeOfFuncClassEnumModuleWorker(symbol: Symbol): Type {
const declaration = symbol.valueDeclaration;
if (symbol.flags & SymbolFlags.Module && isShorthandAmbientModuleSymbol(symbol)) {
return anyType;
}
else if (declaration.kind === SyntaxKind.BinaryExpression ||
declaration.kind === SyntaxKind.PropertyAccessExpression && declaration.parent.kind === SyntaxKind.BinaryExpression) {
return getWidenedTypeFromJSSpecialPropertyDeclarations(symbol);
}
else if (symbol.flags & SymbolFlags.ValueModule && declaration && isSourceFile(declaration) && declaration.commonJsModuleIndicator) {
const resolvedModule = resolveExternalModuleSymbol(symbol);
if (resolvedModule !== symbol) {
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return errorType;
}
const exportEquals = getMergedSymbol(symbol.exports!.get(InternalSymbolName.ExportEquals)!);
const type = getWidenedTypeFromJSSpecialPropertyDeclarations(exportEquals, exportEquals === resolvedModule ? undefined : resolvedModule);
if (!popTypeResolution()) {
return reportCircularityError(symbol);
}
return type;
}
}
const type = createObjectType(ObjectFlags.Anonymous, symbol);
if (symbol.flags & SymbolFlags.Class) {
const baseTypeVariable = getBaseTypeVariableOfClass(symbol);
return baseTypeVariable ? getIntersectionType([type, baseTypeVariable]) : type;
}
else {
return strictNullChecks && symbol.flags & SymbolFlags.Optional ? getOptionalType(type) : type;
}
}
function getTypeOfEnumMember(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.type) {
links.type = getDeclaredTypeOfEnumMember(symbol);
}
return links.type;
return links.type || (links.type = getDeclaredTypeOfEnumMember(symbol));
}
function getTypeOfAlias(symbol: Symbol): Type {
@ -5303,7 +5305,7 @@ namespace ts {
}
else {
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return errorType;
return links.type = errorType;
}
symbolInstantiationDepth++;
let type = instantiateType(getTypeOfSymbol(links.target!), links.mapper!);