From 83e3d6ae590d76a4400e9bd529c665cd865ef103 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 4 Apr 2024 09:38:21 -0700 Subject: [PATCH] Skip visits to child nodes of entity names in visitExistingNodeTreeSymbols (#58067) --- src/compiler/checker.ts | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 85e50c54270..2e8deeeb5d7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8679,15 +8679,28 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { else { context.tracker.trackSymbol(sym, context.enclosingDeclaration, meaning); } - if (isIdentifier(node)) { - const type = getDeclaredTypeOfSymbol(sym); - const name = sym.flags & SymbolFlags.TypeParameter ? typeParameterToName(type, context) : factory.cloneNode(node); - name.symbol = sym; // for quickinfo, which uses identifier symbol information - return { introducesError, node: setTextRange(setEmitFlags(setOriginalNode(name, node), EmitFlags.NoAsciiEscaping), node) }; - } + return { introducesError, node: attachSymbolToLeftmostIdentifier(node) as T }; } return { introducesError, node }; + + /** + * Attaches a `.symbol` member to an identifier, cloning it to do so, so symbol information + * is smuggled out for symbol display information. + */ + function attachSymbolToLeftmostIdentifier(node: Node): Node { + if (node === leftmost) { + const type = getDeclaredTypeOfSymbol(sym!); + const name = sym!.flags & SymbolFlags.TypeParameter ? typeParameterToName(type, context) : factory.cloneNode(node as Identifier); + name.symbol = sym!; // for quickinfo, which uses identifier symbol information + return setTextRange(setEmitFlags(setOriginalNode(name, node), EmitFlags.NoAsciiEscaping), node); + } + const updated = visitEachChild(node, c => attachSymbolToLeftmostIdentifier(c), /*context*/ undefined); + if (updated !== node) { + setTextRange(updated, node); + } + return updated; + } } /** @@ -8856,11 +8869,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (isEntityName(node) || isEntityNameExpression(node)) { + if (isDeclarationName(node)) { + return node; + } const { introducesError, node: result } = trackExistingEntityName(node, context); hadError = hadError || introducesError; - if (result !== node) { - return result; - } + // We should not go to child nodes of the entity name, they will not be accessible + return result; } if (isTupleTypeNode(node) || isTypeLiteralNode(node) || isMappedTypeNode(node)) {