@@ -4487,7 +4487,7 @@ namespace ts {
4487
4487
enclosingDeclaration,
4488
4488
flags: flags || NodeBuilderFlags.None,
4489
4489
// 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 ? {
4491
4491
getCommonSourceDirectory: !!(host as Program).getCommonSourceDirectory ? () => (host as Program).getCommonSourceDirectory() : () => "",
4492
4492
getSourceFiles: () => host.getSourceFiles(),
4493
4493
getCurrentDirectory: () => host.getCurrentDirectory(),
@@ -4500,18 +4500,50 @@ namespace ts {
4500
4500
getFileIncludeReasons: () => host.getFileIncludeReasons(),
4501
4501
} : undefined },
4502
4502
encounteredError: false,
4503
+ reportedDiagnostic: false,
4503
4504
visitedTypes: undefined,
4504
4505
symbolDepth: undefined,
4505
4506
inferTypeParameters: undefined,
4506
4507
approximateLength: 0
4507
4508
};
4509
+ context.tracker = wrapSymbolTrackerToReportForContext(context, context.tracker);
4508
4510
const resultingNode = cb(context);
4509
4511
if (context.truncating && context.flags & NodeBuilderFlags.NoTruncation) {
4510
4512
context.tracker?.reportTruncationError?.();
4511
4513
}
4512
4514
return context.encounteredError ? undefined : resultingNode;
4513
4515
}
4514
4516
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
+
4515
4547
function checkTruncationLength(context: NodeBuilderContext): boolean {
4516
4548
if (context.truncating) return context.truncating;
4517
4549
return context.truncating = context.approximateLength > ((context.flags & NodeBuilderFlags.NoTruncation) ? noTruncationMaximumTruncationLength : defaultMaximumTruncationLength);
@@ -4838,7 +4870,7 @@ namespace ts {
4838
4870
}
4839
4871
}
4840
4872
4841
- function visitAndTransformType<T>(type: Type, transform: (type: Type) => T) {
4873
+ function visitAndTransformType<T extends TypeNode >(type: Type, transform: (type: Type) => T) {
4842
4874
const typeId = type.id;
4843
4875
const isConstructorObject = getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class;
4844
4876
const id = getObjectFlags(type) & ObjectFlags.Reference && (<TypeReference>type).node ? "N" + getNodeId((<TypeReference>type).node!) :
@@ -4853,6 +4885,20 @@ namespace ts {
4853
4885
context.symbolDepth = new Map();
4854
4886
}
4855
4887
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
+
4856
4902
let depth: number | undefined;
4857
4903
if (id) {
4858
4904
depth = context.symbolDepth!.get(id) || 0;
@@ -4862,12 +4908,28 @@ namespace ts {
4862
4908
context.symbolDepth!.set(id, depth + 1);
4863
4909
}
4864
4910
context.visitedTypes.add(typeId);
4911
+ const startLength = context.approximateLength;
4865
4912
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
+ }
4866
4921
context.visitedTypes.delete(typeId);
4867
4922
if (id) {
4868
4923
context.symbolDepth!.set(id, depth!);
4869
4924
}
4870
4925
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
+ }
4871
4933
}
4872
4934
4873
4935
function createTypeNodeFromObjectType(type: ObjectType): TypeNode {
@@ -5998,6 +6060,7 @@ namespace ts {
5998
6060
if (initial.typeParameterSymbolList) {
5999
6061
initial.typeParameterSymbolList = new Set(initial.typeParameterSymbolList);
6000
6062
}
6063
+ initial.tracker = wrapSymbolTrackerToReportForContext(initial, initial.tracker);
6001
6064
return initial;
6002
6065
}
6003
6066
@@ -6289,11 +6352,13 @@ namespace ts {
6289
6352
}
6290
6353
}
6291
6354
else if (oldcontext.tracker && oldcontext.tracker.trackSymbol) {
6292
- oldcontext.tracker.trackSymbol(sym, decl, meaning);
6355
+ return oldcontext.tracker.trackSymbol(sym, decl, meaning);
6293
6356
}
6294
- }
6295
- }
6357
+ return false;
6358
+ },
6359
+ },
6296
6360
};
6361
+ context.tracker = wrapSymbolTrackerToReportForContext(context, context.tracker);
6297
6362
forEachEntry(symbolTable, (symbol, name) => {
6298
6363
const baseName = unescapeLeadingUnderscores(name);
6299
6364
void getInternalSymbolName(symbol, baseName); // Called to cache values into `usedSymbolNames` and `remappedSymbolNames`
@@ -6513,6 +6578,9 @@ namespace ts {
6513
6578
const oldContext = context;
6514
6579
context = cloneNodeBuilderContext(context);
6515
6580
const result = serializeSymbolWorker(symbol, isPrivate, propertyAsAlias);
6581
+ if (context.reportedDiagnostic) {
6582
+ oldcontext.reportedDiagnostic = context.reportedDiagnostic; // hoist diagnostic result into outer context
6583
+ }
6516
6584
context = oldContext;
6517
6585
return result;
6518
6586
}
@@ -7286,7 +7354,7 @@ namespace ts {
7286
7354
// a visibility error here (as they're not visible within any scope), but we want to hoist them
7287
7355
// into the containing scope anyway, so we want to skip the visibility checks.
7288
7356
const oldTrack = context.tracker.trackSymbol;
7289
- context.tracker.trackSymbol = noop ;
7357
+ context.tracker.trackSymbol = () => false ;
7290
7358
if (isExportAssignmentCompatibleSymbolName) {
7291
7359
results.push(factory.createExportAssignment(
7292
7360
/*decorators*/ undefined,
@@ -7742,6 +7810,7 @@ namespace ts {
7742
7810
7743
7811
// State
7744
7812
encounteredError: boolean;
7813
+ reportedDiagnostic: boolean;
7745
7814
visitedTypes: Set<number> | undefined;
7746
7815
symbolDepth: ESMap<string, number> | undefined;
7747
7816
inferTypeParameters: TypeParameter[] | undefined;
0 commit comments