@@ -967,7 +967,7 @@ import {
967967 ScriptKind,
968968 ScriptTarget,
969969 SetAccessorDeclaration,
970- setCommentRange,
970+ setCommentRange as setCommentRangeWorker ,
971971 setEmitFlags,
972972 setIdentifierTypeArguments,
973973 setNodeFlags,
@@ -1094,7 +1094,7 @@ import {
10941094 VariableLikeDeclaration,
10951095 VariableStatement,
10961096 VarianceFlags,
1097- visitEachChild,
1097+ visitEachChild as visitEachChildWorker ,
10981098 visitNode,
10991099 visitNodes,
11001100 Visitor,
@@ -2426,7 +2426,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
24262426
24272427 function markAsSynthetic<T extends Node>(node: T): VisitResult<T> {
24282428 setTextRangePosEnd(node, -1, -1);
2429- return visitEachChild (node, markAsSynthetic, /*context*/ undefined);
2429+ return visitEachChildWorker (node, markAsSynthetic, /*context*/ undefined);
24302430 }
24312431
24322432 function getEmitResolver(sourceFile: SourceFile, cancellationToken: CancellationToken, skipDiagnostics?: boolean) {
@@ -6032,21 +6032,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
60326032 * Unlike the utilities `setTextRange`, this checks if the `location` we're trying to set on `range` is within the
60336033 * same file as the active context. If not, the range is not applied. This prevents us from copying ranges across files,
60346034 * which will confuse the node printer (as it assumes all node ranges are within the current file).
6035- * Additionally, if `range` _isn't synthetic_, and isn't in the current file, it will _copy_ it to _remove_ its' position
6035+ * Additionally, if `range` _isn't synthetic_, or isn't in the current file, it will _copy_ it to _remove_ its' position
60366036 * information.
60376037 *
60386038 * It also calls `setOriginalNode` to setup a `.original` pointer, since you basically *always* want these in the node builder.
60396039 */
60406040 function setTextRange<T extends Node>(context: NodeBuilderContext, range: T, location: Node | undefined): T {
6041- if (!nodeIsSynthesized(range) && !(range.flags & NodeFlags.Synthesized) && ( !context.enclosingFile || context.enclosingFile !== getSourceFileOfNode(range))) {
6042- range = factory.cloneNode(range);
6041+ if (!nodeIsSynthesized(range) || !(range.flags & NodeFlags.Synthesized) || !context.enclosingFile || context.enclosingFile !== getSourceFileOfNode(getOriginalNode (range))) {
6042+ range = factory.cloneNode(range); // if `range` is synthesized or originates in another file, copy it so it definitely has synthetic positions
60436043 }
60446044 if (range === location) return range;
60456045 if (!location) {
60466046 return range;
60476047 }
60486048 if (!context.enclosingFile || context.enclosingFile !== getSourceFileOfNode(getOriginalNode(location))) {
6049- return setOriginalNode(range, location);
6049+ return setOriginalNode(range, location); // if `location` is from another file, only set/update original pointer, and not positions, since copying text across files isn't supported by the emitter
60506050 }
60516051 return setTextRangeWorker(setOriginalNode(range, location), location);
60526052 }
@@ -6720,7 +6720,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
67206720 if (!nodeIsSynthesized(node) && getParseTreeNode(node) === node) {
67216721 return node;
67226722 }
6723- return setTextRange(context, factory.cloneNode(visitEachChild (node, deepCloneOrReuseNode, /*context*/ undefined, deepCloneOrReuseNodes, deepCloneOrReuseNode)), node);
6723+ return setTextRange(context, factory.cloneNode(visitEachChildWorker (node, deepCloneOrReuseNode, /*context*/ undefined, deepCloneOrReuseNodes, deepCloneOrReuseNode)), node);
67246724 }
67256725
67266726 function deepCloneOrReuseNodes(
@@ -7067,6 +7067,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
70677067 const getterSignature = getSignatureFromDeclaration(getterDeclaration);
70687068 typeElements.push(
70697069 setCommentRange(
7070+ context,
70707071 signatureToSignatureDeclarationHelper(getterSignature, SyntaxKind.GetAccessor, context, { name: propertyName }) as GetAccessorDeclaration,
70717072 getterDeclaration,
70727073 ),
@@ -7075,6 +7076,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
70757076 const setterSignature = getSignatureFromDeclaration(setterDeclaration);
70767077 typeElements.push(
70777078 setCommentRange(
7079+ context,
70787080 signatureToSignatureDeclarationHelper(setterSignature, SyntaxKind.SetAccessor, context, { name: propertyName }) as SetAccessorDeclaration,
70797081 setterDeclaration,
70807082 ),
@@ -7132,12 +7134,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
71327134 }
71337135 else if (propertySymbol.valueDeclaration) {
71347136 // Copy comments to node for declaration emit
7135- setCommentRange(node, propertySymbol.valueDeclaration);
7137+ setCommentRange(context, node, propertySymbol.valueDeclaration);
71367138 }
71377139 return node;
71387140 }
71397141 }
71407142
7143+ function setCommentRange<T extends Node>(context: NodeBuilderContext, node: T, range: Node): T {
7144+ if (context.enclosingFile && context.enclosingFile === getSourceFileOfNode(range)) {
7145+ // Copy comments to node for declaration emit
7146+ return setCommentRangeWorker(node, range);
7147+ }
7148+ return node;
7149+ }
7150+
71417151 function mapToTypeNodes(types: readonly Type[] | undefined, context: NodeBuilderContext, isBareList?: boolean): TypeNode[] | undefined {
71427152 if (some(types)) {
71437153 if (checkTruncationLength(context)) {
@@ -7579,7 +7589,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
75797589 if (context.tracker.canTrackSymbol && isComputedPropertyName(node) && isLateBindableName(node)) {
75807590 trackComputedName(node.expression, context.enclosingDeclaration, context);
75817591 }
7582- let visited = visitEachChild (node, elideInitializerAndSetEmitFlags, /*context*/ undefined, /*nodesVisitor*/ undefined, elideInitializerAndSetEmitFlags);
7592+ let visited = visitEachChildWorker (node, elideInitializerAndSetEmitFlags, /*context*/ undefined, /*nodesVisitor*/ undefined, elideInitializerAndSetEmitFlags);
75837593 if (isBindingElement(visited)) {
75847594 visited = factory.updateBindingElement(
75857595 visited,
@@ -7992,6 +8002,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
79928002 if (!(result.kind & SyntaxKind.Identifier)) {
79938003 return factory.createIdentifier("(Missing type parameter)");
79948004 }
8005+ const decl = type.symbol?.declarations?.[0];
8006+ if (decl && isTypeParameterDeclaration(decl)) {
8007+ result = setTextRange(context, result, decl.name);
8008+ }
79958009 if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams) {
79968010 const rawtext = result.escapedText as string;
79978011 let i = context.typeParameterNamesByTextNextNameCount?.get(rawtext) || 0;
@@ -8393,7 +8407,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
83938407 name.symbol = sym!; // for quickinfo, which uses identifier symbol information
83948408 return setTextRange(context, setEmitFlags(name, EmitFlags.NoAsciiEscaping), node);
83958409 }
8396- const updated = visitEachChild (node, c => attachSymbolToLeftmostIdentifier(c), /*context*/ undefined);
8410+ const updated = visitEachChildWorker (node, c => attachSymbolToLeftmostIdentifier(c), /*context*/ undefined);
83978411 if (updated !== node) {
83988412 setTextRange(context, updated, node);
83998413 }
@@ -8504,7 +8518,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
85048518 // is set to build for (even though we are reusing the node structure, the position information
85058519 // would make the printer print invalid spans for literals and identifiers, and the formatter would
85068520 // choke on the mismatched positonal spans between a parent and an injected child from another file).
8507- return result === node ? setTextRange(context, factory.cloneNode( result) , node) : result ;
8521+ return result ? setTextRange(context, result, node) : undefined ;
85088522 }
85098523
85108524 function createRecoveryBoundary() {
@@ -8769,7 +8783,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
87698783 || (isPropertySignature(node) && !node.type && !node.initializer)
87708784 || (isParameter(node) && !node.type && !node.initializer)
87718785 ) {
8772- let visited = visitEachChild(node, visitExistingNodeTreeSymbols, /*context*/ undefined );
8786+ let visited = visitEachChild(node, visitExistingNodeTreeSymbols);
87738787 if (visited === node) {
87748788 visited = setTextRange(context, factory.cloneNode(node), node);
87758789 }
@@ -8831,7 +8845,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
88318845 }
88328846
88338847 if (isTupleTypeNode(node) || isTypeLiteralNode(node) || isMappedTypeNode(node)) {
8834- const visited = visitEachChild(node, visitExistingNodeTreeSymbols, /*context*/ undefined );
8848+ const visited = visitEachChild(node, visitExistingNodeTreeSymbols);
88358849 const clone = setTextRange(context, visited === node ? factory.cloneNode(node) : visited, node);
88368850 const flags = getEmitFlags(clone);
88378851 setEmitFlags(clone, flags | (context.flags & NodeBuilderFlags.MultilineObjectLiterals && isTypeLiteralNode(node) ? 0 : EmitFlags.SingleLine));
@@ -8878,7 +8892,33 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
88788892 }
88798893 }
88808894
8881- return visitEachChild(node, visitExistingNodeTreeSymbols, /*context*/ undefined);
8895+ return visitEachChild(node, visitExistingNodeTreeSymbols);
8896+
8897+ function visitEachChild<T extends Node>(node: T, visitor: Visitor): T;
8898+ function visitEachChild<T extends Node>(node: T | undefined, visitor: Visitor): T | undefined;
8899+ function visitEachChild<T extends Node>(node: T | undefined, visitor: Visitor): T | undefined {
8900+ const nonlocalNode = !context.enclosingFile || context.enclosingFile !== getSourceFileOfNode(node);
8901+ return visitEachChildWorker(node, visitor, /*context*/ undefined, nonlocalNode ? visitNodesWithoutCopyingPositions : undefined);
8902+ }
8903+
8904+ function visitNodesWithoutCopyingPositions(
8905+ nodes: NodeArray<Node> | undefined,
8906+ visitor: Visitor,
8907+ test?: (node: Node) => boolean,
8908+ start?: number,
8909+ count?: number,
8910+ ): NodeArray<Node> | undefined {
8911+ let result = visitNodes(nodes, visitor, test, start, count);
8912+ if (result) {
8913+ if (result.pos !== -1 || result.end !== -1) {
8914+ if (result === nodes) {
8915+ result = factory.createNodeArray(nodes, nodes.hasTrailingComma);
8916+ }
8917+ setTextRangePosEnd(result, -1, -1);
8918+ }
8919+ }
8920+ return result;
8921+ }
88828922
88838923 function getEffectiveDotDotDotForParameter(p: ParameterDeclaration) {
88848924 return p.dotDotDotToken || (p.type && isJSDocVariadicType(p.type) ? factory.createToken(SyntaxKind.DotDotDotToken) : undefined);
0 commit comments