Skip to content

Commit 52499b1

Browse files
committed
Add node result caching internal to typeToTypeNodeHelper
1 parent 60be8f1 commit 52499b1

File tree

7 files changed

+89
-15
lines changed

7 files changed

+89
-15
lines changed

src/compiler/checker.ts

+75-6
Original file line numberDiff line numberDiff line change
@@ -4487,7 +4487,7 @@ namespace ts {
44874487
enclosingDeclaration,
44884488
flags: flags || NodeBuilderFlags.None,
44894489
// If no full tracker is provided, fake up a dummy one with a basic limited-functionality moduleResolverHost
4490-
tracker: tracker && tracker.trackSymbol ? tracker : { trackSymbol: noop, moduleResolverHost: flags! & NodeBuilderFlags.DoNotIncludeSymbolChain ? {
4490+
tracker: tracker && tracker.trackSymbol ? tracker : { trackSymbol: () => false, moduleResolverHost: flags! & NodeBuilderFlags.DoNotIncludeSymbolChain ? {
44914491
getCommonSourceDirectory: !!(host as Program).getCommonSourceDirectory ? () => (host as Program).getCommonSourceDirectory() : () => "",
44924492
getSourceFiles: () => host.getSourceFiles(),
44934493
getCurrentDirectory: () => host.getCurrentDirectory(),
@@ -4500,18 +4500,50 @@ namespace ts {
45004500
getFileIncludeReasons: () => host.getFileIncludeReasons(),
45014501
} : undefined },
45024502
encounteredError: false,
4503+
reportedDiagnostic: false,
45034504
visitedTypes: undefined,
45044505
symbolDepth: undefined,
45054506
inferTypeParameters: undefined,
45064507
approximateLength: 0
45074508
};
4509+
context.tracker = wrapSymbolTrackerToReportForContext(context, context.tracker);
45084510
const resultingNode = cb(context);
45094511
if (context.truncating && context.flags & NodeBuilderFlags.NoTruncation) {
45104512
context.tracker?.reportTruncationError?.();
45114513
}
45124514
return context.encounteredError ? undefined : resultingNode;
45134515
}
45144516

4517+
function wrapSymbolTrackerToReportForContext(context: NodeBuilderContext, tracker: SymbolTracker): SymbolTracker {
4518+
const oldTrackSymbol = tracker.trackSymbol;
4519+
return {
4520+
...tracker,
4521+
reportCyclicStructureError: wrapReportedDiagnostic(tracker.reportCyclicStructureError),
4522+
reportInaccessibleThisError: wrapReportedDiagnostic(tracker.reportInaccessibleThisError),
4523+
reportInaccessibleUniqueSymbolError: wrapReportedDiagnostic(tracker.reportInaccessibleUniqueSymbolError),
4524+
reportLikelyUnsafeImportRequiredError: wrapReportedDiagnostic(tracker.reportLikelyUnsafeImportRequiredError),
4525+
reportNonlocalAugmentation: wrapReportedDiagnostic(tracker.reportNonlocalAugmentation),
4526+
reportPrivateInBaseOfClassExpression: wrapReportedDiagnostic(tracker.reportPrivateInBaseOfClassExpression),
4527+
trackSymbol: oldTrackSymbol && ((...args) => {
4528+
const result = oldTrackSymbol(...args);
4529+
if (result) {
4530+
context.reportedDiagnostic = true;
4531+
}
4532+
return result;
4533+
}),
4534+
};
4535+
4536+
function wrapReportedDiagnostic<T extends (...args: any[]) => any>(method: T | undefined): T | undefined {
4537+
if (!method) {
4538+
return method;
4539+
}
4540+
return (((...args) => {
4541+
context.reportedDiagnostic = true;
4542+
return method(...args);
4543+
}) as T);
4544+
}
4545+
}
4546+
45154547
function checkTruncationLength(context: NodeBuilderContext): boolean {
45164548
if (context.truncating) return context.truncating;
45174549
return context.truncating = context.approximateLength > ((context.flags & NodeBuilderFlags.NoTruncation) ? noTruncationMaximumTruncationLength : defaultMaximumTruncationLength);
@@ -4838,7 +4870,7 @@ namespace ts {
48384870
}
48394871
}
48404872

4841-
function visitAndTransformType<T>(type: Type, transform: (type: Type) => T) {
4873+
function visitAndTransformType<T extends TypeNode>(type: Type, transform: (type: Type) => T) {
48424874
const typeId = type.id;
48434875
const isConstructorObject = getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class;
48444876
const id = getObjectFlags(type) & ObjectFlags.Reference && (<TypeReference>type).node ? "N" + getNodeId((<TypeReference>type).node!) :
@@ -4853,6 +4885,20 @@ namespace ts {
48534885
context.symbolDepth = new Map();
48544886
}
48554887

4888+
const links = context.enclosingDeclaration && getNodeLinks(context.enclosingDeclaration);
4889+
const key = `${getTypeId(type)}|${context.flags}`;
4890+
if (links) {
4891+
links.serializedTypes ||= new Map();
4892+
}
4893+
const cachedResult = links?.serializedTypes?.get(key);
4894+
if (cachedResult) {
4895+
if (cachedResult.truncating) {
4896+
context.truncating = true;
4897+
}
4898+
context.approximateLength += cachedResult.addedLength;
4899+
return deepCloneOrReuseNode(cachedResult) as TypeNode as T;
4900+
}
4901+
48564902
let depth: number | undefined;
48574903
if (id) {
48584904
depth = context.symbolDepth!.get(id) || 0;
@@ -4862,12 +4908,28 @@ namespace ts {
48624908
context.symbolDepth!.set(id, depth + 1);
48634909
}
48644910
context.visitedTypes.add(typeId);
4911+
const startLength = context.approximateLength;
48654912
const result = transform(type);
4913+
const addedLength = context.approximateLength - startLength;
4914+
if (!context.reportedDiagnostic && !context.encounteredError) {
4915+
if (context.truncating) {
4916+
(result as any).truncating = true;
4917+
}
4918+
(result as any).addedLength = addedLength;
4919+
links?.serializedTypes?.set(key, result as TypeNode as TypeNode & {truncating?: boolean, addedLength: number});
4920+
}
48664921
context.visitedTypes.delete(typeId);
48674922
if (id) {
48684923
context.symbolDepth!.set(id, depth!);
48694924
}
48704925
return result;
4926+
4927+
function deepCloneOrReuseNode(node: Node): Node {
4928+
if (!nodeIsSynthesized(node) && getParseTreeNode(node) === node) {
4929+
return node;
4930+
}
4931+
return setTextRange(factory.cloneNode(visitEachChild(node, deepCloneOrReuseNode, nullTransformationContext)), node);
4932+
}
48714933
}
48724934

48734935
function createTypeNodeFromObjectType(type: ObjectType): TypeNode {
@@ -5998,6 +6060,7 @@ namespace ts {
59986060
if (initial.typeParameterSymbolList) {
59996061
initial.typeParameterSymbolList = new Set(initial.typeParameterSymbolList);
60006062
}
6063+
initial.tracker = wrapSymbolTrackerToReportForContext(initial, initial.tracker);
60016064
return initial;
60026065
}
60036066

@@ -6289,11 +6352,13 @@ namespace ts {
62896352
}
62906353
}
62916354
else if (oldcontext.tracker && oldcontext.tracker.trackSymbol) {
6292-
oldcontext.tracker.trackSymbol(sym, decl, meaning);
6355+
return oldcontext.tracker.trackSymbol(sym, decl, meaning);
62936356
}
6294-
}
6295-
}
6357+
return false;
6358+
},
6359+
},
62966360
};
6361+
context.tracker = wrapSymbolTrackerToReportForContext(context, context.tracker);
62976362
forEachEntry(symbolTable, (symbol, name) => {
62986363
const baseName = unescapeLeadingUnderscores(name);
62996364
void getInternalSymbolName(symbol, baseName); // Called to cache values into `usedSymbolNames` and `remappedSymbolNames`
@@ -6513,6 +6578,9 @@ namespace ts {
65136578
const oldContext = context;
65146579
context = cloneNodeBuilderContext(context);
65156580
const result = serializeSymbolWorker(symbol, isPrivate, propertyAsAlias);
6581+
if (context.reportedDiagnostic) {
6582+
oldcontext.reportedDiagnostic = context.reportedDiagnostic; // hoist diagnostic result into outer context
6583+
}
65166584
context = oldContext;
65176585
return result;
65186586
}
@@ -7286,7 +7354,7 @@ namespace ts {
72867354
// a visibility error here (as they're not visible within any scope), but we want to hoist them
72877355
// into the containing scope anyway, so we want to skip the visibility checks.
72887356
const oldTrack = context.tracker.trackSymbol;
7289-
context.tracker.trackSymbol = noop;
7357+
context.tracker.trackSymbol = () => false;
72907358
if (isExportAssignmentCompatibleSymbolName) {
72917359
results.push(factory.createExportAssignment(
72927360
/*decorators*/ undefined,
@@ -7742,6 +7810,7 @@ namespace ts {
77427810

77437811
// State
77447812
encounteredError: boolean;
7813+
reportedDiagnostic: boolean;
77457814
visitedTypes: Set<number> | undefined;
77467815
symbolDepth: ESMap<string, number> | undefined;
77477816
inferTypeParameters: TypeParameter[] | undefined;

src/compiler/transformers/declarations.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,10 @@ namespace ts {
145145
symbolAccessibilityResult.errorSymbolName,
146146
symbolAccessibilityResult.errorModuleName));
147147
}
148+
return true;
148149
}
149150
}
151+
return false;
150152
}
151153

152154
function trackExternalModuleSymbolOfImportTypeNode(symbol: Symbol) {
@@ -156,9 +158,10 @@ namespace ts {
156158
}
157159

158160
function trackSymbol(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags) {
159-
if (symbol.flags & SymbolFlags.TypeParameter) return;
160-
handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning, /*shouldComputeAliasesToMakeVisible*/ true));
161+
if (symbol.flags & SymbolFlags.TypeParameter) return false;
162+
const issuedDiagnostic = handleSymbolAccessibilityError(resolver.isSymbolAccessible(symbol, enclosingDeclaration, meaning, /*shouldComputeAliasesToMakeVisible*/ true));
161163
recordTypeReferenceDirectivesIfNecessary(resolver.getTypeReferenceDirectivesForSymbol(symbol, meaning));
164+
return issuedDiagnostic;
162165
}
163166

164167
function reportPrivateInBaseOfClassExpression(propertyName: string) {

src/compiler/types.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -4987,6 +4987,7 @@ namespace ts {
49874987
isExhaustive?: boolean; // Is node an exhaustive switch statement
49884988
skipDirectInference?: true; // Flag set by the API `getContextualType` call on a node when `Completions` is passed to force the checker to skip making inferences to a node's type
49894989
declarationRequiresScopeChange?: boolean; // Set by `useOuterVariableScopeInParameter` in checker when downlevel emit would change the name resolution scope inside of a parameter.
4990+
serializedTypes?: ESMap<string, TypeNode & {truncating?: boolean, addedLength: number}>; // Collection of types serialized at this location
49904991
}
49914992

49924993
export const enum TypeFlags {
@@ -8110,7 +8111,7 @@ namespace ts {
81108111
// Called when the symbol writer encounters a symbol to write. Currently only used by the
81118112
// declaration emitter to help determine if it should patch up the final declaration file
81128113
// with import statements it previously saw (but chose not to emit).
8113-
trackSymbol?(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags): void;
8114+
trackSymbol?(symbol: Symbol, enclosingDeclaration: Node | undefined, meaning: SymbolFlags): boolean;
81148115
reportInaccessibleThisError?(): void;
81158116
reportPrivateInBaseOfClassExpression?(propertyName: string): void;
81168117
reportInaccessibleUniqueSymbolError?(): void;

src/compiler/utilities.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ namespace ts {
8383
increaseIndent: noop,
8484
decreaseIndent: noop,
8585
clear: () => str = "",
86-
trackSymbol: noop,
86+
trackSymbol: () => false,
8787
reportInaccessibleThisError: noop,
8888
reportInaccessibleUniqueSymbolError: noop,
8989
reportPrivateInBaseOfClassExpression: noop,
@@ -4023,7 +4023,7 @@ namespace ts {
40234023
reportInaccessibleThisError: noop,
40244024
reportPrivateInBaseOfClassExpression: noop,
40254025
reportInaccessibleUniqueSymbolError: noop,
4026-
trackSymbol: noop,
4026+
trackSymbol: () => false,
40274027
writeKeyword: write,
40284028
writeOperator: write,
40294029
writeParameter: write,

src/services/codefixes/helpers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace ts.codefix {
1818

1919
export function getNoopSymbolTrackerWithResolver(context: TypeConstructionContext): SymbolTracker {
2020
return {
21-
trackSymbol: noop,
21+
trackSymbol: () => false,
2222
moduleResolverHost: getModuleSpecifierResolverHost(context.program, context.host),
2323
};
2424
}

src/services/utilities.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2093,7 +2093,7 @@ namespace ts {
20932093
increaseIndent: () => { indent++; },
20942094
decreaseIndent: () => { indent--; },
20952095
clear: resetWriter,
2096-
trackSymbol: noop,
2096+
trackSymbol: () => false,
20972097
reportInaccessibleThisError: noop,
20982098
reportInaccessibleUniqueSymbolError: noop,
20992099
reportPrivateInBaseOfClassExpression: noop,
@@ -2619,6 +2619,7 @@ namespace ts {
26192619
const res = checker.typeToTypeNode(type, enclosingScope, NodeBuilderFlags.NoTruncation, {
26202620
trackSymbol: (symbol, declaration, meaning) => {
26212621
typeIsAccessible = typeIsAccessible && checker.isSymbolAccessible(symbol, declaration, meaning, /*shouldComputeAliasToMarkVisible*/ false).accessibility === SymbolAccessibility.Accessible;
2622+
return !typeIsAccessible;
26222623
},
26232624
reportInaccessibleThisError: notAccessible,
26242625
reportPrivateInBaseOfClassExpression: notAccessible,

0 commit comments

Comments
 (0)