• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* @internal */
2namespace ts.FindAllReferences {
3    export interface SymbolAndEntries {
4        readonly definition: Definition | undefined;
5        readonly references: readonly Entry[];
6    }
7
8    export const enum DefinitionKind { Symbol, Label, Keyword, This, String, TripleSlashReference }
9    export type Definition =
10        | { readonly type: DefinitionKind.Symbol; readonly symbol: Symbol }
11        | { readonly type: DefinitionKind.Label; readonly node: Identifier }
12        | { readonly type: DefinitionKind.Keyword; readonly node: Node }
13        | { readonly type: DefinitionKind.This; readonly node: Node }
14        | { readonly type: DefinitionKind.String; readonly node: StringLiteralLike }
15        | { readonly type: DefinitionKind.TripleSlashReference; readonly reference: FileReference, readonly file: SourceFile };
16
17    export const enum EntryKind { Span, Node, StringLiteral, SearchedLocalFoundProperty, SearchedPropertyFoundLocal }
18    export type NodeEntryKind = EntryKind.Node | EntryKind.StringLiteral | EntryKind.SearchedLocalFoundProperty | EntryKind.SearchedPropertyFoundLocal;
19    export type Entry = NodeEntry | SpanEntry;
20    export interface ContextWithStartAndEndNode {
21        start: Node;
22        end: Node;
23    }
24    export type ContextNode = Node | ContextWithStartAndEndNode;
25    export interface NodeEntry {
26        readonly kind: NodeEntryKind;
27        readonly node: Node;
28        readonly context?: ContextNode;
29    }
30    export interface SpanEntry {
31        readonly kind: EntryKind.Span;
32        readonly fileName: string;
33        readonly textSpan: TextSpan;
34    }
35    export function nodeEntry(node: Node, kind: NodeEntryKind = EntryKind.Node): NodeEntry {
36        return {
37            kind,
38            node: (node as NamedDeclaration).name || node,
39            context: getContextNodeForNodeEntry(node)
40        };
41    }
42
43    export function isContextWithStartAndEndNode(node: ContextNode): node is ContextWithStartAndEndNode {
44        return node && (node as Node).kind === undefined;
45    }
46
47    function getContextNodeForNodeEntry(node: Node): ContextNode | undefined {
48        if (isDeclaration(node)) {
49            return getContextNode(node);
50        }
51
52        if (!node.parent) return undefined;
53
54        if (!isDeclaration(node.parent) && !isExportAssignment(node.parent)) {
55            // Special property assignment in javascript
56            if (isInJSFile(node)) {
57                const binaryExpression = isBinaryExpression(node.parent) ?
58                    node.parent :
59                    isAccessExpression(node.parent) &&
60                        isBinaryExpression(node.parent.parent) &&
61                        node.parent.parent.left === node.parent ?
62                        node.parent.parent :
63                        undefined;
64                if (binaryExpression && getAssignmentDeclarationKind(binaryExpression) !== AssignmentDeclarationKind.None) {
65                    return getContextNode(binaryExpression);
66                }
67            }
68
69            // Jsx Tags
70            if (isJsxOpeningElement(node.parent) || isJsxClosingElement(node.parent)) {
71                return node.parent.parent;
72            }
73            else if (isJsxSelfClosingElement(node.parent) ||
74                isLabeledStatement(node.parent) ||
75                isBreakOrContinueStatement(node.parent)) {
76                return node.parent;
77            }
78            else if (isStringLiteralLike(node)) {
79                const validImport = tryGetImportFromModuleSpecifier(node);
80                if (validImport) {
81                    const declOrStatement = findAncestor(validImport, node =>
82                        isDeclaration(node) ||
83                        isStatement(node) ||
84                        isJSDocTag(node)
85                    )! as NamedDeclaration | Statement | JSDocTag;
86                    return isDeclaration(declOrStatement) ?
87                        getContextNode(declOrStatement) :
88                        declOrStatement;
89                }
90            }
91
92            // Handle computed property name
93            const propertyName = findAncestor(node, isComputedPropertyName);
94            return propertyName ?
95                getContextNode(propertyName.parent) :
96                undefined;
97        }
98
99        if (node.parent.name === node || // node is name of declaration, use parent
100            isConstructorDeclaration(node.parent) ||
101            isExportAssignment(node.parent) ||
102            // Property name of the import export specifier or binding pattern, use parent
103            ((isImportOrExportSpecifier(node.parent) || isBindingElement(node.parent))
104                && node.parent.propertyName === node) ||
105            // Is default export
106            (node.kind === SyntaxKind.DefaultKeyword && hasSyntacticModifier(node.parent, ModifierFlags.ExportDefault))) {
107            return getContextNode(node.parent);
108        }
109
110        return undefined;
111    }
112
113    export function getContextNode(node: NamedDeclaration | BinaryExpression | ForInOrOfStatement | undefined): ContextNode | undefined {
114        if (!node) return undefined;
115        switch (node.kind) {
116            case SyntaxKind.VariableDeclaration:
117                return !isVariableDeclarationList(node.parent) || node.parent.declarations.length !== 1 ?
118                    node :
119                    isVariableStatement(node.parent.parent) ?
120                        node.parent.parent :
121                        isForInOrOfStatement(node.parent.parent) ?
122                            getContextNode(node.parent.parent) :
123                            node.parent;
124
125            case SyntaxKind.BindingElement:
126                return getContextNode(node.parent.parent as NamedDeclaration);
127
128            case SyntaxKind.ImportSpecifier:
129                return node.parent.parent.parent;
130
131            case SyntaxKind.ExportSpecifier:
132            case SyntaxKind.NamespaceImport:
133                return node.parent.parent;
134
135            case SyntaxKind.ImportClause:
136            case SyntaxKind.NamespaceExport:
137                return node.parent;
138
139            case SyntaxKind.BinaryExpression:
140                return isExpressionStatement(node.parent) ?
141                    node.parent :
142                    node;
143
144            case SyntaxKind.ForOfStatement:
145            case SyntaxKind.ForInStatement:
146                return {
147                    start: (node as ForInOrOfStatement).initializer,
148                    end: (node as ForInOrOfStatement).expression
149                };
150
151            case SyntaxKind.PropertyAssignment:
152            case SyntaxKind.ShorthandPropertyAssignment:
153                return isArrayLiteralOrObjectLiteralDestructuringPattern(node.parent) ?
154                    getContextNode(
155                        findAncestor(node.parent, node =>
156                            isBinaryExpression(node) || isForInOrOfStatement(node)
157                        ) as BinaryExpression | ForInOrOfStatement
158                    ) :
159                    node;
160
161            default:
162                return node;
163        }
164    }
165
166    export function toContextSpan(textSpan: TextSpan, sourceFile: SourceFile, context?: ContextNode): { contextSpan: TextSpan } | undefined {
167        if (!context) return undefined;
168        const contextSpan = isContextWithStartAndEndNode(context) ?
169            getTextSpan(context.start, sourceFile, context.end) :
170            getTextSpan(context, sourceFile);
171        return contextSpan.start !== textSpan.start || contextSpan.length !== textSpan.length ?
172            { contextSpan } :
173            undefined;
174    }
175
176    export const enum FindReferencesUse {
177        /**
178         * When searching for references to a symbol, the location will not be adjusted (this is the default behavior when not specified).
179         */
180        Other,
181        /**
182         * When searching for references to a symbol, the location will be adjusted if the cursor was on a keyword.
183         */
184        References,
185        /**
186         * When searching for references to a symbol, the location will be adjusted if the cursor was on a keyword.
187         * Unlike `References`, the location will only be adjusted keyword belonged to a declaration with a valid name.
188         * If set, we will find fewer references -- if it is referenced by several different names, we still only find references for the original name.
189         */
190        Rename,
191    }
192
193    export interface Options {
194        readonly findInStrings?: boolean;
195        readonly findInComments?: boolean;
196        readonly use?: FindReferencesUse;
197        /** True if we are searching for implementations. We will have a different method of adding references if so. */
198        readonly implementations?: boolean;
199        /**
200         * True to opt in for enhanced renaming of shorthand properties and import/export specifiers.
201         * The options controls the behavior for the whole rename operation; it cannot be changed on a per-file basis.
202         * Default is false for backwards compatibility.
203         */
204        readonly providePrefixAndSuffixTextForRename?: boolean;
205    }
206
207    export function findReferencedSymbols(program: Program, cancellationToken: CancellationToken, sourceFiles: readonly SourceFile[], sourceFile: SourceFile, position: number): ReferencedSymbol[] | undefined {
208        const node = getTouchingPropertyName(sourceFile, position);
209        const referencedSymbols = Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, { use: FindReferencesUse.References });
210        const checker = program.getTypeChecker();
211        return !referencedSymbols || !referencedSymbols.length ? undefined : mapDefined<SymbolAndEntries, ReferencedSymbol>(referencedSymbols, ({ definition, references }) =>
212            // Only include referenced symbols that have a valid definition.
213            definition && {
214                definition: checker.runWithCancellationToken(cancellationToken, checker => definitionToReferencedSymbolDefinitionInfo(definition, checker, node)),
215                references: references.map(toReferenceEntry)
216            });
217    }
218
219    export function getImplementationsAtPosition(program: Program, cancellationToken: CancellationToken, sourceFiles: readonly SourceFile[], sourceFile: SourceFile, position: number): ImplementationLocation[] | undefined {
220        const node = getTouchingPropertyName(sourceFile, position);
221        let referenceEntries: Entry[] | undefined;
222        const entries = getImplementationReferenceEntries(program, cancellationToken, sourceFiles, node, position);
223
224        if (
225            node.parent.kind === SyntaxKind.PropertyAccessExpression
226            || node.parent.kind === SyntaxKind.BindingElement
227            || node.parent.kind === SyntaxKind.ElementAccessExpression
228            || node.kind === SyntaxKind.SuperKeyword
229        ) {
230            referenceEntries = entries && [...entries];
231        }
232        else {
233            const queue = entries && [...entries];
234            const seenNodes = new Map<string, true>();
235            while (queue && queue.length) {
236                const entry = queue.shift() as NodeEntry;
237                if (!addToSeen(seenNodes, getNodeId(entry.node))) {
238                    continue;
239                }
240                referenceEntries = append(referenceEntries, entry);
241                const entries = getImplementationReferenceEntries(program, cancellationToken, sourceFiles, entry.node, entry.node.pos);
242                if (entries) {
243                    queue.push(...entries);
244                }
245            }
246        }
247        const checker = program.getTypeChecker();
248        return map(referenceEntries, entry => toImplementationLocation(entry, checker));
249    }
250
251    function getImplementationReferenceEntries(program: Program, cancellationToken: CancellationToken, sourceFiles: readonly SourceFile[], node: Node, position: number): readonly Entry[] | undefined {
252        if (node.kind === SyntaxKind.SourceFile) {
253            return undefined;
254        }
255
256        const checker = program.getTypeChecker();
257        // If invoked directly on a shorthand property assignment, then return
258        // the declaration of the symbol being assigned (not the symbol being assigned to).
259        if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
260            const result: NodeEntry[] = [];
261            Core.getReferenceEntriesForShorthandPropertyAssignment(node, checker, node => result.push(nodeEntry(node)));
262            return result;
263        }
264        else if (node.kind === SyntaxKind.SuperKeyword || isSuperProperty(node.parent)) {
265            // References to and accesses on the super keyword only have one possible implementation, so no
266            // need to "Find all References"
267            const symbol = checker.getSymbolAtLocation(node)!;
268            return symbol.valueDeclaration && [nodeEntry(symbol.valueDeclaration)];
269        }
270        else {
271            // Perform "Find all References" and retrieve only those that are implementations
272            return getReferenceEntriesForNode(position, node, program, sourceFiles, cancellationToken, { implementations: true, use: FindReferencesUse.References });
273        }
274    }
275
276    export function findReferenceOrRenameEntries<T>(
277        program: Program, cancellationToken: CancellationToken, sourceFiles: readonly SourceFile[], node: Node, position: number, options: Options | undefined,
278        convertEntry: ToReferenceOrRenameEntry<T>,
279    ): T[] | undefined {
280        return map(flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options)), entry => convertEntry(entry, node, program.getTypeChecker()));
281    }
282
283    export type ToReferenceOrRenameEntry<T> = (entry: Entry, originalNode: Node, checker: TypeChecker) => T;
284
285    export function getReferenceEntriesForNode(
286        position: number,
287        node: Node,
288        program: Program,
289        sourceFiles: readonly SourceFile[],
290        cancellationToken: CancellationToken,
291        options: Options = {},
292        sourceFilesSet: ReadonlySet<string> = new Set(sourceFiles.map(f => f.fileName)),
293    ): readonly Entry[] | undefined {
294        return flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options, sourceFilesSet));
295    }
296
297    function flattenEntries(referenceSymbols: readonly SymbolAndEntries[] | undefined): readonly Entry[] | undefined {
298        return referenceSymbols && flatMap(referenceSymbols, r => r.references);
299    }
300
301    function definitionToReferencedSymbolDefinitionInfo(def: Definition, checker: TypeChecker, originalNode: Node): ReferencedSymbolDefinitionInfo {
302        const info = ((): { sourceFile: SourceFile, textSpan: TextSpan, name: string, kind: ScriptElementKind, displayParts: SymbolDisplayPart[], context?: Node | ContextWithStartAndEndNode } => {
303            switch (def.type) {
304                case DefinitionKind.Symbol: {
305                    const { symbol } = def;
306                    const { displayParts, kind } = getDefinitionKindAndDisplayParts(symbol, checker, originalNode);
307                    const name = displayParts.map(p => p.text).join("");
308                    const declaration = symbol.declarations && firstOrUndefined(symbol.declarations);
309                    const node = declaration ? (getNameOfDeclaration(declaration) || declaration) : originalNode;
310                    return {
311                        ...getFileAndTextSpanFromNode(node),
312                        name,
313                        kind,
314                        displayParts,
315                        context: getContextNode(declaration)
316                    };
317                }
318                case DefinitionKind.Label: {
319                    const { node } = def;
320                    return { ...getFileAndTextSpanFromNode(node), name: node.text, kind: ScriptElementKind.label, displayParts: [displayPart(node.text, SymbolDisplayPartKind.text)] };
321                }
322                case DefinitionKind.Keyword: {
323                    const { node } = def;
324                    const name = tokenToString(node.kind)!;
325                    return { ...getFileAndTextSpanFromNode(node), name, kind: ScriptElementKind.keyword, displayParts: [{ text: name, kind: ScriptElementKind.keyword }] };
326                }
327                case DefinitionKind.This: {
328                    const { node } = def;
329                    const symbol = checker.getSymbolAtLocation(node);
330                    const displayParts = symbol && SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(
331                        checker, symbol, node.getSourceFile(), getContainerNode(node), node).displayParts || [textPart("this")];
332                    return { ...getFileAndTextSpanFromNode(node), name: "this", kind: ScriptElementKind.variableElement, displayParts };
333                }
334                case DefinitionKind.String: {
335                    const { node } = def;
336                    return {
337                        ...getFileAndTextSpanFromNode(node),
338                        name: node.text,
339                        kind: ScriptElementKind.variableElement,
340                        displayParts: [displayPart(getTextOfNode(node), SymbolDisplayPartKind.stringLiteral)]
341                    };
342                }
343                case DefinitionKind.TripleSlashReference: {
344                    return {
345                        textSpan: createTextSpanFromRange(def.reference),
346                        sourceFile: def.file,
347                        name: def.reference.fileName,
348                        kind: ScriptElementKind.string,
349                        displayParts: [displayPart(`"${def.reference.fileName}"`, SymbolDisplayPartKind.stringLiteral)]
350                    };
351                }
352                default:
353                    return Debug.assertNever(def);
354            }
355        })();
356
357        const { sourceFile, textSpan, name, kind, displayParts, context } = info;
358        return {
359            containerKind: ScriptElementKind.unknown,
360            containerName: "",
361            fileName: sourceFile.fileName,
362            kind,
363            name,
364            textSpan,
365            displayParts,
366            ...toContextSpan(textSpan, sourceFile, context)
367        };
368    }
369
370    function getFileAndTextSpanFromNode(node: Node) {
371        const sourceFile = node.getSourceFile();
372        return {
373            sourceFile,
374            textSpan: getTextSpan(isComputedPropertyName(node) ? node.expression : node, sourceFile)
375        };
376    }
377
378    function getDefinitionKindAndDisplayParts(symbol: Symbol, checker: TypeChecker, node: Node): { displayParts: SymbolDisplayPart[], kind: ScriptElementKind } {
379        const meaning = Core.getIntersectingMeaningFromDeclarations(node, symbol);
380        const enclosingDeclaration = symbol.declarations && firstOrUndefined(symbol.declarations) || node;
381        const { displayParts, symbolKind } =
382            SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(checker, symbol, enclosingDeclaration.getSourceFile(), enclosingDeclaration, enclosingDeclaration, meaning);
383        return { displayParts, kind: symbolKind };
384    }
385
386    export function toRenameLocation(entry: Entry, originalNode: Node, checker: TypeChecker, providePrefixAndSuffixText: boolean): RenameLocation {
387        return { ...entryToDocumentSpan(entry), ...(providePrefixAndSuffixText && getPrefixAndSuffixText(entry, originalNode, checker)) };
388    }
389
390    export function toReferenceEntry(entry: Entry): ReferenceEntry {
391        const documentSpan = entryToDocumentSpan(entry);
392        if (entry.kind === EntryKind.Span) {
393            return { ...documentSpan, isWriteAccess: false, isDefinition: false };
394        }
395        const { kind, node } = entry;
396        return {
397            ...documentSpan,
398            isWriteAccess: isWriteAccessForReference(node),
399            isDefinition: isDefinitionForReference(node),
400            isInString: kind === EntryKind.StringLiteral ? true : undefined,
401        };
402    }
403
404    function entryToDocumentSpan(entry: Entry): DocumentSpan {
405        if (entry.kind === EntryKind.Span) {
406            return { textSpan: entry.textSpan, fileName: entry.fileName };
407        }
408        else {
409            const sourceFile = entry.node.getSourceFile();
410            const textSpan = getTextSpan(entry.node, sourceFile);
411            return {
412                textSpan,
413                fileName: sourceFile.fileName,
414                ...toContextSpan(textSpan, sourceFile, entry.context)
415            };
416        }
417    }
418
419    interface PrefixAndSuffix { readonly prefixText?: string; readonly suffixText?: string; }
420    function getPrefixAndSuffixText(entry: Entry, originalNode: Node, checker: TypeChecker): PrefixAndSuffix {
421        if (entry.kind !== EntryKind.Span && isIdentifier(originalNode)) {
422            const { node, kind } = entry;
423            const parent = node.parent;
424            const name = originalNode.text;
425            const isShorthandAssignment = isShorthandPropertyAssignment(parent);
426            if (isShorthandAssignment || (isObjectBindingElementWithoutPropertyName(parent) && parent.name === node && parent.dotDotDotToken === undefined)) {
427                const prefixColon: PrefixAndSuffix = { prefixText: name + ": " };
428                const suffixColon: PrefixAndSuffix = { suffixText: ": " + name };
429                if (kind === EntryKind.SearchedLocalFoundProperty) {
430                    return prefixColon;
431                }
432                if (kind === EntryKind.SearchedPropertyFoundLocal) {
433                    return suffixColon;
434                }
435
436                // In `const o = { x }; o.x`, symbolAtLocation at `x` in `{ x }` is the property symbol.
437                // For a binding element `const { x } = o;`, symbolAtLocation at `x` is the property symbol.
438                if (isShorthandAssignment) {
439                    const grandParent = parent.parent;
440                    if (isObjectLiteralExpression(grandParent) &&
441                        isBinaryExpression(grandParent.parent) &&
442                        isModuleExportsAccessExpression(grandParent.parent.left)) {
443                        return prefixColon;
444                    }
445                    return suffixColon;
446                }
447                else {
448                    return prefixColon;
449                }
450            }
451            else if (isImportSpecifier(parent) && !parent.propertyName) {
452                // If the original symbol was using this alias, just rename the alias.
453                const originalSymbol = isExportSpecifier(originalNode.parent) ? checker.getExportSpecifierLocalTargetSymbol(originalNode.parent) : checker.getSymbolAtLocation(originalNode);
454                return contains(originalSymbol!.declarations, parent) ? { prefixText: name + " as " } : emptyOptions;
455            }
456            else if (isExportSpecifier(parent) && !parent.propertyName) {
457                // If the symbol for the node is same as declared node symbol use prefix text
458                return originalNode === entry.node || checker.getSymbolAtLocation(originalNode) === checker.getSymbolAtLocation(entry.node) ?
459                    { prefixText: name + " as " } :
460                    { suffixText: " as " + name };
461            }
462        }
463
464        return emptyOptions;
465    }
466
467    function toImplementationLocation(entry: Entry, checker: TypeChecker): ImplementationLocation {
468        const documentSpan = entryToDocumentSpan(entry);
469        if (entry.kind !== EntryKind.Span) {
470            const { node } = entry;
471            return {
472                ...documentSpan,
473                ...implementationKindDisplayParts(node, checker)
474            };
475        }
476        else {
477            return { ...documentSpan, kind: ScriptElementKind.unknown, displayParts: [] };
478        }
479    }
480
481    function implementationKindDisplayParts(node: Node, checker: TypeChecker): { kind: ScriptElementKind, displayParts: SymbolDisplayPart[] } {
482        const symbol = checker.getSymbolAtLocation(isDeclaration(node) && node.name ? node.name : node);
483        if (symbol) {
484            return getDefinitionKindAndDisplayParts(symbol, checker, node);
485        }
486        else if (node.kind === SyntaxKind.ObjectLiteralExpression) {
487            return {
488                kind: ScriptElementKind.interfaceElement,
489                displayParts: [punctuationPart(SyntaxKind.OpenParenToken), textPart("object literal"), punctuationPart(SyntaxKind.CloseParenToken)]
490            };
491        }
492        else if (node.kind === SyntaxKind.ClassExpression) {
493            return {
494                kind: ScriptElementKind.localClassElement,
495                displayParts: [punctuationPart(SyntaxKind.OpenParenToken), textPart("anonymous local class"), punctuationPart(SyntaxKind.CloseParenToken)]
496            };
497        }
498        else {
499            return { kind: getNodeKind(node), displayParts: [] };
500        }
501    }
502
503    export function toHighlightSpan(entry: Entry): { fileName: string, span: HighlightSpan } {
504        const documentSpan = entryToDocumentSpan(entry);
505        if (entry.kind === EntryKind.Span) {
506            return {
507                fileName: documentSpan.fileName,
508                span: {
509                    textSpan: documentSpan.textSpan,
510                    kind: HighlightSpanKind.reference
511                }
512            };
513        }
514
515        const writeAccess = isWriteAccessForReference(entry.node);
516        const span: HighlightSpan = {
517            textSpan: documentSpan.textSpan,
518            kind: writeAccess ? HighlightSpanKind.writtenReference : HighlightSpanKind.reference,
519            isInString: entry.kind === EntryKind.StringLiteral ? true : undefined,
520            ...documentSpan.contextSpan && { contextSpan: documentSpan.contextSpan }
521        };
522        return { fileName: documentSpan.fileName, span };
523    }
524
525    function getTextSpan(node: Node, sourceFile: SourceFile, endNode?: Node): TextSpan {
526        let start = node.getStart(sourceFile);
527        let end = (endNode || node).getEnd();
528        if (isStringLiteralLike(node)) {
529            Debug.assert(endNode === undefined);
530            start += 1;
531            end -= 1;
532        }
533        return createTextSpanFromBounds(start, end);
534    }
535
536    export function getTextSpanOfEntry(entry: Entry) {
537        return entry.kind === EntryKind.Span ? entry.textSpan :
538            getTextSpan(entry.node, entry.node.getSourceFile());
539    }
540
541    /** A node is considered a writeAccess iff it is a name of a declaration or a target of an assignment */
542    function isWriteAccessForReference(node: Node): boolean {
543        const decl = getDeclarationFromName(node);
544        return !!decl && declarationIsWriteAccess(decl) || node.kind === SyntaxKind.DefaultKeyword || isWriteAccess(node);
545    }
546
547    function isDefinitionForReference(node: Node): boolean {
548        return node.kind === SyntaxKind.DefaultKeyword
549            || !!getDeclarationFromName(node)
550            || isLiteralComputedPropertyDeclarationName(node)
551            || (node.kind === SyntaxKind.ConstructorKeyword && isConstructorDeclaration(node.parent));
552    }
553
554    /**
555     * True if 'decl' provides a value, as in `function f() {}`;
556     * false if 'decl' is just a location for a future write, as in 'let x;'
557     */
558    function declarationIsWriteAccess(decl: Declaration): boolean {
559        // Consider anything in an ambient declaration to be a write access since it may be coming from JS.
560        if (!!(decl.flags & NodeFlags.Ambient)) return true;
561
562        switch (decl.kind) {
563            case SyntaxKind.BinaryExpression:
564            case SyntaxKind.BindingElement:
565            case SyntaxKind.ClassDeclaration:
566            case SyntaxKind.ClassExpression:
567            case SyntaxKind.StructDeclaration:
568            case SyntaxKind.DefaultKeyword:
569            case SyntaxKind.EnumDeclaration:
570            case SyntaxKind.EnumMember:
571            case SyntaxKind.ExportSpecifier:
572            case SyntaxKind.ImportClause: // default import
573            case SyntaxKind.ImportEqualsDeclaration:
574            case SyntaxKind.ImportSpecifier:
575            case SyntaxKind.InterfaceDeclaration:
576            case SyntaxKind.JSDocCallbackTag:
577            case SyntaxKind.JSDocTypedefTag:
578            case SyntaxKind.JsxAttribute:
579            case SyntaxKind.ModuleDeclaration:
580            case SyntaxKind.NamespaceExportDeclaration:
581            case SyntaxKind.NamespaceImport:
582            case SyntaxKind.NamespaceExport:
583            case SyntaxKind.Parameter:
584            case SyntaxKind.ShorthandPropertyAssignment:
585            case SyntaxKind.TypeAliasDeclaration:
586            case SyntaxKind.TypeParameter:
587                return true;
588
589            case SyntaxKind.PropertyAssignment:
590                // In `({ x: y } = 0);`, `x` is not a write access. (Won't call this function for `y`.)
591                return !isArrayLiteralOrObjectLiteralDestructuringPattern((decl as PropertyAssignment).parent);
592
593            case SyntaxKind.FunctionDeclaration:
594            case SyntaxKind.FunctionExpression:
595            case SyntaxKind.Constructor:
596            case SyntaxKind.MethodDeclaration:
597            case SyntaxKind.GetAccessor:
598            case SyntaxKind.SetAccessor:
599                return !!(decl as FunctionDeclaration | FunctionExpression | ConstructorDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration).body;
600
601            case SyntaxKind.VariableDeclaration:
602            case SyntaxKind.PropertyDeclaration:
603                return !!(decl as VariableDeclaration | PropertyDeclaration).initializer || isCatchClause(decl.parent);
604
605            case SyntaxKind.MethodSignature:
606            case SyntaxKind.PropertySignature:
607            case SyntaxKind.JSDocPropertyTag:
608            case SyntaxKind.JSDocParameterTag:
609                return false;
610
611            default:
612                return Debug.failBadSyntaxKind(decl);
613        }
614    }
615
616    /** Encapsulates the core find-all-references algorithm. */
617    export namespace Core {
618        /** Core find-all-references algorithm. Handles special cases before delegating to `getReferencedSymbolsForSymbol`. */
619        export function getReferencedSymbolsForNode(position: number, node: Node, program: Program, sourceFiles: readonly SourceFile[], cancellationToken: CancellationToken, options: Options = {}, sourceFilesSet: ReadonlySet<string> = new Set(sourceFiles.map(f => f.fileName))): readonly SymbolAndEntries[] | undefined {
620            if (options.use === FindReferencesUse.References) {
621                node = getAdjustedReferenceLocation(node);
622            }
623            else if (options.use === FindReferencesUse.Rename) {
624                node = getAdjustedRenameLocation(node);
625            }
626            if (isSourceFile(node)) {
627                const resolvedRef = GoToDefinition.getReferenceAtPosition(node, position, program);
628                if (!resolvedRef) {
629                    return undefined;
630                }
631                const moduleSymbol = program.getTypeChecker().getMergedSymbol(resolvedRef.file.symbol);
632                if (moduleSymbol) {
633                    return getReferencedSymbolsForModule(program, moduleSymbol, /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet);
634                }
635                const fileIncludeReasons = program.getFileIncludeReasons();
636                if (!fileIncludeReasons) {
637                    return undefined;
638                }
639                return [{
640                    definition: { type: DefinitionKind.TripleSlashReference, reference: resolvedRef.reference, file: node },
641                    references: getReferencesForNonModule(resolvedRef.file, fileIncludeReasons, program) || emptyArray
642                }];
643            }
644
645            if (!options.implementations) {
646                const special = getReferencedSymbolsSpecial(node, sourceFiles, cancellationToken);
647                if (special) {
648                    return special;
649                }
650            }
651
652            const checker = program.getTypeChecker();
653            // constructors should use the class symbol, detected by name, if present
654            const symbol = checker.getSymbolAtLocation(isConstructorDeclaration(node) && node.parent.name || node);
655
656            // Could not find a symbol e.g. unknown identifier
657            if (!symbol) {
658                // String literal might be a property (and thus have a symbol), so do this here rather than in getReferencedSymbolsSpecial.
659                if (!options.implementations && isStringLiteralLike(node)) {
660                    if (isRequireCall(node.parent, /*requireStringLiteralLikeArgument*/ true) || isExternalModuleReference(node.parent) || isImportDeclaration(node.parent) || isImportCall(node.parent)) {
661                        const fileIncludeReasons = program.getFileIncludeReasons();
662                        const referencedFileName = node.getSourceFile().resolvedModules?.get(node.text)?.resolvedFileName;
663                        const referencedFile = referencedFileName ? program.getSourceFile(referencedFileName) : undefined;
664                        if (referencedFile) {
665                            return [{ definition: { type: DefinitionKind.String, node }, references: getReferencesForNonModule(referencedFile, fileIncludeReasons, program) || emptyArray }];
666                        }
667                        // Fall through to string literal references. This is not very likely to return
668                        // anything useful, but I guess it's better than nothing, and there's an existing
669                        // test that expects this to happen (fourslash/cases/untypedModuleImport.ts).
670                    }
671                    return getReferencesForStringLiteral(node, sourceFiles, checker, cancellationToken);
672                }
673                return undefined;
674            }
675
676            if (symbol.escapedName === InternalSymbolName.ExportEquals) {
677                return getReferencedSymbolsForModule(program, symbol.parent!, /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet);
678            }
679
680            const moduleReferences = getReferencedSymbolsForModuleIfDeclaredBySourceFile(symbol, program, sourceFiles, cancellationToken, options, sourceFilesSet);
681            if (moduleReferences && !(symbol.flags & SymbolFlags.Transient)) {
682                return moduleReferences;
683            }
684
685            const aliasedSymbol = getMergedAliasedSymbolOfNamespaceExportDeclaration(node, symbol, checker);
686            const moduleReferencesOfExportTarget = aliasedSymbol &&
687                getReferencedSymbolsForModuleIfDeclaredBySourceFile(aliasedSymbol, program, sourceFiles, cancellationToken, options, sourceFilesSet);
688
689            const references = getReferencedSymbolsForSymbol(symbol, node, sourceFiles, sourceFilesSet, checker, cancellationToken, options);
690            return mergeReferences(program, moduleReferences, references, moduleReferencesOfExportTarget);
691        }
692
693        export function getReferencesForFileName(fileName: string, program: Program, sourceFiles: readonly SourceFile[], sourceFilesSet: ReadonlySet<string> = new Set(sourceFiles.map(f => f.fileName))): readonly Entry[] {
694            const moduleSymbol = program.getSourceFile(fileName)?.symbol;
695            if (moduleSymbol) {
696                return getReferencedSymbolsForModule(program, moduleSymbol, /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet)[0]?.references || emptyArray;
697            }
698            const fileIncludeReasons = program.getFileIncludeReasons();
699            const referencedFile = program.getSourceFile(fileName);
700            return referencedFile && fileIncludeReasons && getReferencesForNonModule(referencedFile, fileIncludeReasons, program) || emptyArray;
701        }
702
703        function getReferencesForNonModule(referencedFile: SourceFile, refFileMap: MultiMap<Path, FileIncludeReason>, program: Program): readonly SpanEntry[] | undefined {
704            let entries: SpanEntry[] | undefined;
705            const references = refFileMap.get(referencedFile.path) || emptyArray;
706            for (const ref of references) {
707                if (isReferencedFile(ref)) {
708                    const referencingFile = program.getSourceFileByPath(ref.file)!;
709                    const location = getReferencedFileLocation(program.getSourceFileByPath, ref);
710                    if (isReferenceFileLocation(location)) {
711                        entries = append(entries, {
712                            kind: EntryKind.Span,
713                            fileName: referencingFile.fileName,
714                            textSpan: createTextSpanFromRange(location)
715                        });
716                    }
717                }
718            }
719            return entries;
720        }
721
722        function getMergedAliasedSymbolOfNamespaceExportDeclaration(node: Node, symbol: Symbol, checker: TypeChecker) {
723            if (node.parent && isNamespaceExportDeclaration(node.parent)) {
724                const aliasedSymbol = checker.getAliasedSymbol(symbol);
725                const targetSymbol = checker.getMergedSymbol(aliasedSymbol);
726                if (aliasedSymbol !== targetSymbol) {
727                    return targetSymbol;
728                }
729            }
730            return undefined;
731        }
732
733        function getReferencedSymbolsForModuleIfDeclaredBySourceFile(symbol: Symbol, program: Program, sourceFiles: readonly SourceFile[], cancellationToken: CancellationToken, options: Options, sourceFilesSet: ReadonlySet<string>) {
734            const moduleSourceFile = (symbol.flags & SymbolFlags.Module) && symbol.declarations && find(symbol.declarations, isSourceFile);
735            if (!moduleSourceFile) return undefined;
736            const exportEquals = symbol.exports!.get(InternalSymbolName.ExportEquals);
737            // If !!exportEquals, we're about to add references to `import("mod")` anyway, so don't double-count them.
738            const moduleReferences = getReferencedSymbolsForModule(program, symbol, !!exportEquals, sourceFiles, sourceFilesSet);
739            if (!exportEquals || !sourceFilesSet.has(moduleSourceFile.fileName)) return moduleReferences;
740            // Continue to get references to 'export ='.
741            const checker = program.getTypeChecker();
742            symbol = skipAlias(exportEquals, checker);
743            return mergeReferences(program, moduleReferences, getReferencedSymbolsForSymbol(symbol, /*node*/ undefined, sourceFiles, sourceFilesSet, checker, cancellationToken, options));
744        }
745
746        /**
747         * Merges the references by sorting them (by file index in sourceFiles and their location in it) that point to same definition symbol
748         */
749        function mergeReferences(program: Program, ...referencesToMerge: (SymbolAndEntries[] | undefined)[]): SymbolAndEntries[] | undefined {
750            let result: SymbolAndEntries[] | undefined;
751            for (const references of referencesToMerge) {
752                if (!references || !references.length) continue;
753                if (!result) {
754                    result = references;
755                    continue;
756                }
757                for (const entry of references) {
758                    if (!entry.definition || entry.definition.type !== DefinitionKind.Symbol) {
759                        result.push(entry);
760                        continue;
761                    }
762                    const symbol = entry.definition.symbol;
763                    const refIndex = findIndex(result, ref => !!ref.definition &&
764                        ref.definition.type === DefinitionKind.Symbol &&
765                        ref.definition.symbol === symbol);
766                    if (refIndex === -1) {
767                        result.push(entry);
768                        continue;
769                    }
770
771                    const reference = result[refIndex];
772                    result[refIndex] = {
773                        definition: reference.definition,
774                        references: reference.references.concat(entry.references).sort((entry1, entry2) => {
775                            const entry1File = getSourceFileIndexOfEntry(program, entry1);
776                            const entry2File = getSourceFileIndexOfEntry(program, entry2);
777                            if (entry1File !== entry2File) {
778                                return compareValues(entry1File, entry2File);
779                            }
780
781                            const entry1Span = getTextSpanOfEntry(entry1);
782                            const entry2Span = getTextSpanOfEntry(entry2);
783                            return entry1Span.start !== entry2Span.start ?
784                                compareValues(entry1Span.start, entry2Span.start) :
785                                compareValues(entry1Span.length, entry2Span.length);
786                        })
787                    };
788                }
789            }
790            return result;
791        }
792
793        function getSourceFileIndexOfEntry(program: Program, entry: Entry) {
794            const sourceFile = entry.kind === EntryKind.Span ?
795                program.getSourceFile(entry.fileName)! :
796                entry.node.getSourceFile();
797            return program.getSourceFiles().indexOf(sourceFile);
798        }
799
800        function getReferencedSymbolsForModule(program: Program, symbol: Symbol, excludeImportTypeOfExportEquals: boolean, sourceFiles: readonly SourceFile[], sourceFilesSet: ReadonlySet<string>): SymbolAndEntries[] {
801            Debug.assert(!!symbol.valueDeclaration);
802
803            const references = mapDefined<ModuleReference, Entry>(findModuleReferences(program, sourceFiles, symbol), reference => {
804                if (reference.kind === "import") {
805                    const parent = reference.literal.parent;
806                    if (isLiteralTypeNode(parent)) {
807                        const importType = cast(parent.parent, isImportTypeNode);
808                        if (excludeImportTypeOfExportEquals && !importType.qualifier) {
809                            return undefined;
810                        }
811                    }
812                    // import("foo") with no qualifier will reference the `export =` of the module, which may be referenced anyway.
813                    return nodeEntry(reference.literal);
814                }
815                else {
816                    return {
817                        kind: EntryKind.Span,
818                        fileName: reference.referencingFile.fileName,
819                        textSpan: createTextSpanFromRange(reference.ref),
820                    };
821                }
822            });
823
824            if (symbol.declarations) {
825                for (const decl of symbol.declarations) {
826                    switch (decl.kind) {
827                        case SyntaxKind.SourceFile:
828                            // Don't include the source file itself. (This may not be ideal behavior, but awkward to include an entire file as a reference.)
829                            break;
830                        case SyntaxKind.ModuleDeclaration:
831                            if (sourceFilesSet.has(decl.getSourceFile().fileName)) {
832                                references.push(nodeEntry((decl as ModuleDeclaration).name));
833                            }
834                            break;
835                        default:
836                            // This may be merged with something.
837                            Debug.assert(!!(symbol.flags & SymbolFlags.Transient), "Expected a module symbol to be declared by a SourceFile or ModuleDeclaration.");
838                    }
839                }
840            }
841
842            const exported = symbol.exports!.get(InternalSymbolName.ExportEquals);
843            if (exported) {
844                for (const decl of exported.declarations) {
845                    const sourceFile = decl.getSourceFile();
846                    if (sourceFilesSet.has(sourceFile.fileName)) {
847                        // At `module.exports = ...`, reference node is `module`
848                        const node = isBinaryExpression(decl) && isPropertyAccessExpression(decl.left) ? decl.left.expression :
849                            isExportAssignment(decl) ? Debug.checkDefined(findChildOfKind(decl, SyntaxKind.ExportKeyword, sourceFile)) :
850                            getNameOfDeclaration(decl) || decl;
851                        references.push(nodeEntry(node));
852                    }
853                }
854            }
855
856            return references.length ? [{ definition: { type: DefinitionKind.Symbol, symbol }, references }] : emptyArray;
857        }
858
859        /** As in a `readonly prop: any` or `constructor(readonly prop: any)`, not a `readonly any[]`. */
860        function isReadonlyTypeOperator(node: Node): boolean {
861            return node.kind === SyntaxKind.ReadonlyKeyword
862                && isTypeOperatorNode(node.parent)
863                && node.parent.operator === SyntaxKind.ReadonlyKeyword;
864        }
865
866        /** getReferencedSymbols for special node kinds. */
867        function getReferencedSymbolsSpecial(node: Node, sourceFiles: readonly SourceFile[], cancellationToken: CancellationToken): SymbolAndEntries[] | undefined {
868            if (isTypeKeyword(node.kind)) {
869                // A void expression (i.e., `void foo()`) is not special, but the `void` type is.
870                if (node.kind === SyntaxKind.VoidKeyword && isVoidExpression(node.parent)) {
871                    return undefined;
872                }
873
874                // A modifier readonly (like on a property declaration) is not special;
875                // a readonly type keyword (like `readonly string[]`) is.
876                if (node.kind === SyntaxKind.ReadonlyKeyword && !isReadonlyTypeOperator(node)) {
877                    return undefined;
878                }
879                // Likewise, when we *are* looking for a special keyword, make sure we
880                // *don’t* include readonly member modifiers.
881                return getAllReferencesForKeyword(
882                    sourceFiles,
883                    node.kind,
884                    cancellationToken,
885                    node.kind === SyntaxKind.ReadonlyKeyword ? isReadonlyTypeOperator : undefined);
886            }
887
888            // Labels
889            if (isJumpStatementTarget(node)) {
890                const labelDefinition = getTargetLabel(node.parent, node.text);
891                // if we have a label definition, look within its statement for references, if not, then
892                // the label is undefined and we have no results..
893                return labelDefinition && getLabelReferencesInNode(labelDefinition.parent, labelDefinition);
894            }
895            else if (isLabelOfLabeledStatement(node)) {
896                // it is a label definition and not a target, search within the parent labeledStatement
897                return getLabelReferencesInNode(node.parent, node);
898            }
899
900            if (isThis(node)) {
901                return getReferencesForThisKeyword(node, sourceFiles, cancellationToken);
902            }
903
904            if (node.kind === SyntaxKind.SuperKeyword) {
905                return getReferencesForSuperKeyword(node);
906            }
907
908            return undefined;
909        }
910
911        /** Core find-all-references algorithm for a normal symbol. */
912        function getReferencedSymbolsForSymbol(originalSymbol: Symbol, node: Node | undefined, sourceFiles: readonly SourceFile[], sourceFilesSet: ReadonlySet<string>, checker: TypeChecker, cancellationToken: CancellationToken, options: Options): SymbolAndEntries[] {
913            const symbol = node && skipPastExportOrImportSpecifierOrUnion(originalSymbol, node, checker, /*useLocalSymbolForExportSpecifier*/ !isForRenameWithPrefixAndSuffixText(options)) || originalSymbol;
914
915            // Compute the meaning from the location and the symbol it references
916            const searchMeaning = node ? getIntersectingMeaningFromDeclarations(node, symbol) : SemanticMeaning.All;
917            const result: SymbolAndEntries[] = [];
918            const state = new State(sourceFiles, sourceFilesSet, node ? getSpecialSearchKind(node) : SpecialSearchKind.None, checker, cancellationToken, searchMeaning, options, result);
919
920            const exportSpecifier = !isForRenameWithPrefixAndSuffixText(options) ? undefined : find(symbol.declarations, isExportSpecifier);
921            if (exportSpecifier) {
922                // When renaming at an export specifier, rename the export and not the thing being exported.
923                getReferencesAtExportSpecifier(exportSpecifier.name, symbol, exportSpecifier, state.createSearch(node, originalSymbol, /*comingFrom*/ undefined), state, /*addReferencesHere*/ true, /*alwaysGetReferences*/ true);
924            }
925            else if (node && node.kind === SyntaxKind.DefaultKeyword) {
926                addReference(node, symbol, state);
927                searchForImportsOfExport(node, symbol, { exportingModuleSymbol: Debug.checkDefined(symbol.parent, "Expected export symbol to have a parent"), exportKind: ExportKind.Default }, state);
928            }
929            else {
930                const search = state.createSearch(node, symbol, /*comingFrom*/ undefined, { allSearchSymbols: node ? populateSearchSymbolSet(symbol, node, checker, options.use === FindReferencesUse.Rename, !!options.providePrefixAndSuffixTextForRename, !!options.implementations) : [symbol] });
931                getReferencesInContainerOrFiles(symbol, state, search);
932            }
933
934            return result;
935        }
936
937        function getReferencesInContainerOrFiles(symbol: Symbol, state: State, search: Search): void {
938            // Try to get the smallest valid scope that we can limit our search to;
939            // otherwise we'll need to search globally (i.e. include each file).
940            const scope = getSymbolScope(symbol);
941            if (scope) {
942                getReferencesInContainer(scope, scope.getSourceFile(), search, state, /*addReferencesHere*/ !(isSourceFile(scope) && !contains(state.sourceFiles, scope)));
943            }
944            else {
945                // Global search
946                for (const sourceFile of state.sourceFiles) {
947                    state.cancellationToken.throwIfCancellationRequested();
948                    searchForName(sourceFile, search, state);
949                }
950            }
951        }
952
953        function getSpecialSearchKind(node: Node): SpecialSearchKind {
954            switch (node.kind) {
955                case SyntaxKind.Constructor:
956                case SyntaxKind.ConstructorKeyword:
957                    return SpecialSearchKind.Constructor;
958                case SyntaxKind.Identifier:
959                    if (isClassLike(node.parent)) {
960                        Debug.assert(node.parent.name === node);
961                        return SpecialSearchKind.Class;
962                    }
963                    // falls through
964                default:
965                    return SpecialSearchKind.None;
966            }
967        }
968
969        /** Handle a few special cases relating to export/import specifiers. */
970        function skipPastExportOrImportSpecifierOrUnion(symbol: Symbol, node: Node, checker: TypeChecker, useLocalSymbolForExportSpecifier: boolean): Symbol | undefined {
971            const { parent } = node;
972            if (isExportSpecifier(parent) && useLocalSymbolForExportSpecifier) {
973                return getLocalSymbolForExportSpecifier(node as Identifier, symbol, parent, checker);
974            }
975            // If the symbol is declared as part of a declaration like `{ type: "a" } | { type: "b" }`, use the property on the union type to get more references.
976            return firstDefined(symbol.declarations, decl => {
977                if (!decl.parent) {
978                    // Ignore UMD module and global merge
979                    if (symbol.flags & SymbolFlags.Transient) return undefined;
980                    // Assertions for GH#21814. We should be handling SourceFile symbols in `getReferencedSymbolsForModule` instead of getting here.
981                    Debug.fail(`Unexpected symbol at ${Debug.formatSyntaxKind(node.kind)}: ${Debug.formatSymbol(symbol)}`);
982                }
983                return isTypeLiteralNode(decl.parent) && isUnionTypeNode(decl.parent.parent)
984                    ? checker.getPropertyOfType(checker.getTypeFromTypeNode(decl.parent.parent), symbol.name)
985                    : undefined;
986            });
987        }
988
989        /**
990         * Symbol that is currently being searched for.
991         * This will be replaced if we find an alias for the symbol.
992         */
993        interface Search {
994            /** If coming from an export, we will not recursively search for the imported symbol (since that's where we came from). */
995            readonly comingFrom?: ImportExport;
996
997            readonly symbol: Symbol;
998            readonly text: string;
999            readonly escapedText: __String;
1000            /** Only set if `options.implementations` is true. These are the symbols checked to get the implementations of a property access. */
1001            readonly parents: readonly Symbol[] | undefined;
1002            readonly allSearchSymbols: readonly Symbol[];
1003
1004            /**
1005             * Whether a symbol is in the search set.
1006             * Do not compare directly to `symbol` because there may be related symbols to search for. See `populateSearchSymbolSet`.
1007             */
1008            includes(symbol: Symbol): boolean;
1009        }
1010
1011        const enum SpecialSearchKind {
1012            None,
1013            Constructor,
1014            Class,
1015        }
1016
1017        function getNonModuleSymbolOfMergedModuleSymbol(symbol: Symbol) {
1018            if (!(symbol.flags & (SymbolFlags.Module | SymbolFlags.Transient))) return undefined;
1019            const decl = symbol.declarations && find(symbol.declarations, d => !isSourceFile(d) && !isModuleDeclaration(d));
1020            return decl && decl.symbol;
1021        }
1022
1023        /**
1024         * Holds all state needed for the finding references.
1025         * Unlike `Search`, there is only one `State`.
1026         */
1027        class State {
1028            /** Cache for `explicitlyinheritsFrom`. */
1029            readonly inheritsFromCache = new Map<string, boolean>();
1030
1031            /**
1032             * Type nodes can contain multiple references to the same type. For example:
1033             *      let x: Foo & (Foo & Bar) = ...
1034             * Because we are returning the implementation locations and not the identifier locations,
1035             * duplicate entries would be returned here as each of the type references is part of
1036             * the same implementation. For that reason, check before we add a new entry.
1037             */
1038            readonly markSeenContainingTypeReference = nodeSeenTracker();
1039
1040            /**
1041             * It's possible that we will encounter the right side of `export { foo as bar } from "x";` more than once.
1042             * For example:
1043             *     // b.ts
1044             *     export { foo as bar } from "./a";
1045             *     import { bar } from "./b";
1046             *
1047             * Normally at `foo as bar` we directly add `foo` and do not locally search for it (since it doesn't declare a local).
1048             * But another reference to it may appear in the same source file.
1049             * See `tests/cases/fourslash/transitiveExportImports3.ts`.
1050             */
1051            readonly markSeenReExportRHS = nodeSeenTracker();
1052
1053            constructor(
1054                readonly sourceFiles: readonly SourceFile[],
1055                readonly sourceFilesSet: ReadonlySet<string>,
1056                readonly specialSearchKind: SpecialSearchKind,
1057                readonly checker: TypeChecker,
1058                readonly cancellationToken: CancellationToken,
1059                readonly searchMeaning: SemanticMeaning,
1060                readonly options: Options,
1061                private readonly result: Push<SymbolAndEntries>) {
1062            }
1063
1064            includesSourceFile(sourceFile: SourceFile): boolean {
1065                return this.sourceFilesSet.has(sourceFile.fileName);
1066            }
1067
1068            private importTracker: ImportTracker | undefined;
1069            /** Gets every place to look for references of an exported symbols. See `ImportsResult` in `importTracker.ts` for more documentation. */
1070            getImportSearches(exportSymbol: Symbol, exportInfo: ExportInfo): ImportsResult {
1071                if (!this.importTracker) this.importTracker = createImportTracker(this.sourceFiles, this.sourceFilesSet, this.checker, this.cancellationToken);
1072                return this.importTracker(exportSymbol, exportInfo, this.options.use === FindReferencesUse.Rename);
1073            }
1074
1075            /** @param allSearchSymbols set of additional symbols for use by `includes`. */
1076            createSearch(location: Node | undefined, symbol: Symbol, comingFrom: ImportExport | undefined, searchOptions: { text?: string, allSearchSymbols?: Symbol[] } = {}): Search {
1077                // Note: if this is an external module symbol, the name doesn't include quotes.
1078                // Note: getLocalSymbolForExportDefault handles `export default class C {}`, but not `export default C` or `export { C as default }`.
1079                // The other two forms seem to be handled downstream (e.g. in `skipPastExportOrImportSpecifier`), so special-casing the first form
1080                // here appears to be intentional).
1081                const {
1082                    text = stripQuotes(symbolName(getLocalSymbolForExportDefault(symbol) || getNonModuleSymbolOfMergedModuleSymbol(symbol) || symbol)),
1083                    allSearchSymbols = [symbol],
1084                } = searchOptions;
1085                const escapedText = escapeLeadingUnderscores(text);
1086                const parents = this.options.implementations && location ? getParentSymbolsOfPropertyAccess(location, symbol, this.checker) : undefined;
1087                return { symbol, comingFrom, text, escapedText, parents, allSearchSymbols, includes: sym => contains(allSearchSymbols, sym) };
1088            }
1089
1090            private readonly symbolIdToReferences: Entry[][] = [];
1091            /**
1092             * Callback to add references for a particular searched symbol.
1093             * This initializes a reference group, so only call this if you will add at least one reference.
1094             */
1095            referenceAdder(searchSymbol: Symbol): (node: Node, kind?: NodeEntryKind) => void {
1096                const symbolId = getSymbolId(searchSymbol);
1097                let references = this.symbolIdToReferences[symbolId];
1098                if (!references) {
1099                    references = this.symbolIdToReferences[symbolId] = [];
1100                    this.result.push({ definition: { type: DefinitionKind.Symbol, symbol: searchSymbol }, references });
1101                }
1102                return (node, kind) => references.push(nodeEntry(node, kind));
1103            }
1104
1105            /** Add a reference with no associated definition. */
1106            addStringOrCommentReference(fileName: string, textSpan: TextSpan): void {
1107                this.result.push({
1108                    definition: undefined,
1109                    references: [{ kind: EntryKind.Span, fileName, textSpan }]
1110                });
1111            }
1112
1113            // Source file ID → symbol ID → Whether the symbol has been searched for in the source file.
1114            private readonly sourceFileToSeenSymbols: Set<number>[] = [];
1115            /** Returns `true` the first time we search for a symbol in a file and `false` afterwards. */
1116            markSearchedSymbols(sourceFile: SourceFile, symbols: readonly Symbol[]): boolean {
1117                const sourceId = getNodeId(sourceFile);
1118                const seenSymbols = this.sourceFileToSeenSymbols[sourceId] || (this.sourceFileToSeenSymbols[sourceId] = new Set<number>());
1119
1120                let anyNewSymbols = false;
1121                for (const sym of symbols) {
1122                    anyNewSymbols = tryAddToSet(seenSymbols, getSymbolId(sym)) || anyNewSymbols;
1123                }
1124                return anyNewSymbols;
1125            }
1126        }
1127
1128        /** Search for all imports of a given exported symbol using `State.getImportSearches`. */
1129        function searchForImportsOfExport(exportLocation: Node, exportSymbol: Symbol, exportInfo: ExportInfo, state: State): void {
1130            const { importSearches, singleReferences, indirectUsers } = state.getImportSearches(exportSymbol, exportInfo);
1131
1132            // For `import { foo as bar }` just add the reference to `foo`, and don't otherwise search in the file.
1133            if (singleReferences.length) {
1134                const addRef = state.referenceAdder(exportSymbol);
1135                for (const singleRef of singleReferences) {
1136                    if (shouldAddSingleReference(singleRef, state)) addRef(singleRef);
1137                }
1138            }
1139
1140            // For each import, find all references to that import in its source file.
1141            for (const [importLocation, importSymbol] of importSearches) {
1142                getReferencesInSourceFile(importLocation.getSourceFile(), state.createSearch(importLocation, importSymbol, ImportExport.Export), state);
1143            }
1144
1145            if (indirectUsers.length) {
1146                let indirectSearch: Search | undefined;
1147                switch (exportInfo.exportKind) {
1148                    case ExportKind.Named:
1149                        indirectSearch = state.createSearch(exportLocation, exportSymbol, ImportExport.Export);
1150                        break;
1151                    case ExportKind.Default:
1152                        // Search for a property access to '.default'. This can't be renamed.
1153                        indirectSearch = state.options.use === FindReferencesUse.Rename ? undefined : state.createSearch(exportLocation, exportSymbol, ImportExport.Export, { text: "default" });
1154                        break;
1155                    case ExportKind.ExportEquals:
1156                        break;
1157                }
1158                if (indirectSearch) {
1159                    for (const indirectUser of indirectUsers) {
1160                        searchForName(indirectUser, indirectSearch, state);
1161                    }
1162                }
1163            }
1164        }
1165
1166        export function eachExportReference(
1167            sourceFiles: readonly SourceFile[],
1168            checker: TypeChecker,
1169            cancellationToken: CancellationToken | undefined,
1170            exportSymbol: Symbol,
1171            exportingModuleSymbol: Symbol,
1172            exportName: string,
1173            isDefaultExport: boolean,
1174            cb: (ref: Identifier) => void,
1175        ): void {
1176            const importTracker = createImportTracker(sourceFiles, new Set(sourceFiles.map(f => f.fileName)), checker, cancellationToken);
1177            const { importSearches, indirectUsers } = importTracker(exportSymbol, { exportKind: isDefaultExport ? ExportKind.Default : ExportKind.Named, exportingModuleSymbol }, /*isForRename*/ false);
1178            for (const [importLocation] of importSearches) {
1179                cb(importLocation);
1180            }
1181            for (const indirectUser of indirectUsers) {
1182                for (const node of getPossibleSymbolReferenceNodes(indirectUser, isDefaultExport ? "default" : exportName)) {
1183                    // Import specifiers should be handled by importSearches
1184                    if (isIdentifier(node) && !isImportOrExportSpecifier(node.parent) && checker.getSymbolAtLocation(node) === exportSymbol) {
1185                        cb(node);
1186                    }
1187                }
1188            }
1189        }
1190
1191        function shouldAddSingleReference(singleRef: Identifier | StringLiteral, state: State): boolean {
1192            if (!hasMatchingMeaning(singleRef, state)) return false;
1193            if (state.options.use !== FindReferencesUse.Rename) return true;
1194            // Don't rename an import type `import("./module-name")` when renaming `name` in `export = name;`
1195            if (!isIdentifier(singleRef)) return false;
1196            // At `default` in `import { default as x }` or `export { default as x }`, do add a reference, but do not rename.
1197            return !(isImportOrExportSpecifier(singleRef.parent) && singleRef.escapedText === InternalSymbolName.Default);
1198        }
1199
1200        // Go to the symbol we imported from and find references for it.
1201        function searchForImportedSymbol(symbol: Symbol, state: State): void {
1202            if (!symbol.declarations) return;
1203
1204            for (const declaration of symbol.declarations) {
1205                const exportingFile = declaration.getSourceFile();
1206                // Need to search in the file even if it's not in the search-file set, because it might export the symbol.
1207                getReferencesInSourceFile(exportingFile, state.createSearch(declaration, symbol, ImportExport.Import), state, state.includesSourceFile(exportingFile));
1208            }
1209        }
1210
1211        /** Search for all occurences of an identifier in a source file (and filter out the ones that match). */
1212        function searchForName(sourceFile: SourceFile, search: Search, state: State): void {
1213            if (getNameTable(sourceFile).get(search.escapedText) !== undefined) {
1214                getReferencesInSourceFile(sourceFile, search, state);
1215            }
1216        }
1217
1218        function getPropertySymbolOfDestructuringAssignment(location: Node, checker: TypeChecker): Symbol | undefined {
1219            return isArrayLiteralOrObjectLiteralDestructuringPattern(location.parent.parent)
1220                ? checker.getPropertySymbolOfDestructuringAssignment(<Identifier>location)
1221                : undefined;
1222        }
1223
1224        /**
1225         * Determines the smallest scope in which a symbol may have named references.
1226         * Note that not every construct has been accounted for. This function can
1227         * probably be improved.
1228         *
1229         * @returns undefined if the scope cannot be determined, implying that
1230         * a reference to a symbol can occur anywhere.
1231         */
1232        function getSymbolScope(symbol: Symbol): Node | undefined {
1233            // If this is the symbol of a named function expression or named class expression,
1234            // then named references are limited to its own scope.
1235            const { declarations, flags, parent, valueDeclaration } = symbol;
1236            if (valueDeclaration && (valueDeclaration.kind === SyntaxKind.FunctionExpression || valueDeclaration.kind === SyntaxKind.ClassExpression)) {
1237                return valueDeclaration;
1238            }
1239
1240            if (!declarations) {
1241                return undefined;
1242            }
1243
1244            // If this is private property or method, the scope is the containing class
1245            if (flags & (SymbolFlags.Property | SymbolFlags.Method)) {
1246                const privateDeclaration = find(declarations, d => hasEffectiveModifier(d, ModifierFlags.Private) || isPrivateIdentifierPropertyDeclaration(d));
1247                if (privateDeclaration) {
1248                    return getAncestor(privateDeclaration, SyntaxKind.ClassDeclaration);
1249                }
1250                // Else this is a public property and could be accessed from anywhere.
1251                return undefined;
1252            }
1253
1254            // If symbol is of object binding pattern element without property name we would want to
1255            // look for property too and that could be anywhere
1256            if (declarations.some(isObjectBindingElementWithoutPropertyName)) {
1257                return undefined;
1258            }
1259
1260            /*
1261            If the symbol has a parent, it's globally visible unless:
1262            - It's a private property (handled above).
1263            - It's a type parameter.
1264            - The parent is an external module: then we should only search in the module (and recurse on the export later).
1265            - But if the parent has `export as namespace`, the symbol is globally visible through that namespace.
1266            */
1267            const exposedByParent = parent && !(symbol.flags & SymbolFlags.TypeParameter);
1268            if (exposedByParent && !(isExternalModuleSymbol(parent!) && !parent!.globalExports)) {
1269                return undefined;
1270            }
1271
1272            let scope: Node | undefined;
1273            for (const declaration of declarations) {
1274                const container = getContainerNode(declaration);
1275                if (scope && scope !== container) {
1276                    // Different declarations have different containers, bail out
1277                    return undefined;
1278                }
1279
1280                if (!container || container.kind === SyntaxKind.SourceFile && !isExternalOrCommonJsModule(<SourceFile>container)) {
1281                    // This is a global variable and not an external module, any declaration defined
1282                    // within this scope is visible outside the file
1283                    return undefined;
1284                }
1285
1286                scope = container;
1287                if (isFunctionExpression(scope)) {
1288                    let next: Node | undefined;
1289                    while (next = getNextJSDocCommentLocation(scope)) {
1290                        scope = next;
1291                    }
1292                }
1293            }
1294
1295            // If symbol.parent, this means we are in an export of an external module. (Otherwise we would have returned `undefined` above.)
1296            // For an export of a module, we may be in a declaration file, and it may be accessed elsewhere. E.g.:
1297            //     declare module "a" { export type T = number; }
1298            //     declare module "b" { import { T } from "a"; export const x: T; }
1299            // So we must search the whole source file. (Because we will mark the source file as seen, we we won't return to it when searching for imports.)
1300            return exposedByParent ? scope!.getSourceFile() : scope; // TODO: GH#18217
1301        }
1302
1303        /** Used as a quick check for whether a symbol is used at all in a file (besides its definition). */
1304        export function isSymbolReferencedInFile(definition: Identifier, checker: TypeChecker, sourceFile: SourceFile, searchContainer: Node = sourceFile): boolean {
1305            return eachSymbolReferenceInFile(definition, checker, sourceFile, () => true, searchContainer) || false;
1306        }
1307
1308        export function eachSymbolReferenceInFile<T>(definition: Identifier, checker: TypeChecker, sourceFile: SourceFile, cb: (token: Identifier) => T, searchContainer: Node = sourceFile): T | undefined {
1309            const symbol = isParameterPropertyDeclaration(definition.parent, definition.parent.parent)
1310                ? first(checker.getSymbolsOfParameterPropertyDeclaration(definition.parent, definition.text))
1311                : checker.getSymbolAtLocation(definition);
1312            if (!symbol) return undefined;
1313            for (const token of getPossibleSymbolReferenceNodes(sourceFile, symbol.name, searchContainer)) {
1314                if (!isIdentifier(token) || token === definition || token.escapedText !== definition.escapedText) continue;
1315                const referenceSymbol: Symbol = checker.getSymbolAtLocation(token)!; // See GH#19955 for why the type annotation is necessary
1316                if (referenceSymbol === symbol
1317                    || checker.getShorthandAssignmentValueSymbol(token.parent) === symbol
1318                    || isExportSpecifier(token.parent) && getLocalSymbolForExportSpecifier(token, referenceSymbol, token.parent, checker) === symbol) {
1319                    const res = cb(token);
1320                    if (res) return res;
1321                }
1322            }
1323        }
1324
1325        export function someSignatureUsage(
1326            signature: SignatureDeclaration,
1327            sourceFiles: readonly SourceFile[],
1328            checker: TypeChecker,
1329            cb: (name: Identifier, call?: CallExpression) => boolean
1330        ): boolean {
1331            if (!signature.name || !isIdentifier(signature.name)) return false;
1332
1333            const symbol = Debug.checkDefined(checker.getSymbolAtLocation(signature.name));
1334
1335            for (const sourceFile of sourceFiles) {
1336                for (const name of getPossibleSymbolReferenceNodes(sourceFile, symbol.name)) {
1337                    if (!isIdentifier(name) || name === signature.name || name.escapedText !== signature.name.escapedText) continue;
1338                    const called = climbPastPropertyAccess(name);
1339                    const call = isCallExpression(called.parent) && called.parent.expression === called ? called.parent : undefined;
1340                    const referenceSymbol = checker.getSymbolAtLocation(name);
1341                    if (referenceSymbol && checker.getRootSymbols(referenceSymbol).some(s => s === symbol)) {
1342                        if (cb(name, call)) {
1343                            return true;
1344                        }
1345                    }
1346                }
1347            }
1348            return false;
1349        }
1350
1351        function getPossibleSymbolReferenceNodes(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile): readonly Node[] {
1352            return getPossibleSymbolReferencePositions(sourceFile, symbolName, container).map(pos => getTouchingPropertyName(sourceFile, pos));
1353        }
1354
1355        function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile): readonly number[] {
1356            const positions: number[] = [];
1357
1358            /// TODO: Cache symbol existence for files to save text search
1359            // Also, need to make this work for unicode escapes.
1360
1361            // Be resilient in the face of a symbol with no name or zero length name
1362            if (!symbolName || !symbolName.length) {
1363                return positions;
1364            }
1365
1366            const text = sourceFile.text;
1367            const sourceLength = text.length;
1368            const symbolNameLength = symbolName.length;
1369
1370            let position = text.indexOf(symbolName, container.pos);
1371            while (position >= 0) {
1372                // If we are past the end, stop looking
1373                if (position > container.end) break;
1374
1375                // We found a match.  Make sure it's not part of a larger word (i.e. the char
1376                // before and after it have to be a non-identifier char).
1377                const endPosition = position + symbolNameLength;
1378
1379                if ((position === 0 || !isIdentifierPart(text.charCodeAt(position - 1), ScriptTarget.Latest)) &&
1380                    (endPosition === sourceLength || !isIdentifierPart(text.charCodeAt(endPosition), ScriptTarget.Latest))) {
1381                    // Found a real match.  Keep searching.
1382                    positions.push(position);
1383                }
1384                position = text.indexOf(symbolName, position + symbolNameLength + 1);
1385            }
1386
1387            return positions;
1388        }
1389
1390        function getLabelReferencesInNode(container: Node, targetLabel: Identifier): SymbolAndEntries[] {
1391            const sourceFile = container.getSourceFile();
1392            const labelName = targetLabel.text;
1393            const references = mapDefined(getPossibleSymbolReferenceNodes(sourceFile, labelName, container), node =>
1394                // Only pick labels that are either the target label, or have a target that is the target label
1395                node === targetLabel || (isJumpStatementTarget(node) && getTargetLabel(node, labelName) === targetLabel) ? nodeEntry(node) : undefined);
1396            return [{ definition: { type: DefinitionKind.Label, node: targetLabel }, references }];
1397        }
1398
1399        function isValidReferencePosition(node: Node, searchSymbolName: string): boolean {
1400            // Compare the length so we filter out strict superstrings of the symbol we are looking for
1401            switch (node.kind) {
1402                case SyntaxKind.PrivateIdentifier:
1403                case SyntaxKind.Identifier:
1404                    return (node as PrivateIdentifier | Identifier).text.length === searchSymbolName.length;
1405                case SyntaxKind.NoSubstitutionTemplateLiteral:
1406                case SyntaxKind.StringLiteral: {
1407                    const str = node as StringLiteralLike;
1408                    return (isLiteralNameOfPropertyDeclarationOrIndexAccess(str) || isNameOfModuleDeclaration(node) || isExpressionOfExternalModuleImportEqualsDeclaration(node) || (isCallExpression(node.parent) && isBindableObjectDefinePropertyCall(node.parent) && node.parent.arguments[1] === node)) &&
1409                        str.text.length === searchSymbolName.length;
1410                }
1411
1412                case SyntaxKind.NumericLiteral:
1413                    return isLiteralNameOfPropertyDeclarationOrIndexAccess(node as NumericLiteral) && (node as NumericLiteral).text.length === searchSymbolName.length;
1414
1415                case SyntaxKind.DefaultKeyword:
1416                    return "default".length === searchSymbolName.length;
1417
1418                default:
1419                    return false;
1420            }
1421        }
1422
1423        function getAllReferencesForKeyword(sourceFiles: readonly SourceFile[], keywordKind: SyntaxKind, cancellationToken: CancellationToken, filter?: (node: Node) => boolean): SymbolAndEntries[] | undefined {
1424            const references = flatMap(sourceFiles, sourceFile => {
1425                cancellationToken.throwIfCancellationRequested();
1426                return mapDefined(getPossibleSymbolReferenceNodes(sourceFile, tokenToString(keywordKind)!, sourceFile), referenceLocation => {
1427                    if (referenceLocation.kind === keywordKind && (!filter || filter(referenceLocation))) {
1428                        return nodeEntry(referenceLocation);
1429                    }
1430                });
1431            });
1432            return references.length ? [{ definition: { type: DefinitionKind.Keyword, node: references[0].node }, references }] : undefined;
1433        }
1434
1435        function getReferencesInSourceFile(sourceFile: SourceFile, search: Search, state: State, addReferencesHere = true): void {
1436            state.cancellationToken.throwIfCancellationRequested();
1437            return getReferencesInContainer(sourceFile, sourceFile, search, state, addReferencesHere);
1438        }
1439
1440        /**
1441         * Search within node "container" for references for a search value, where the search value is defined as a
1442         * tuple of(searchSymbol, searchText, searchLocation, and searchMeaning).
1443         * searchLocation: a node where the search value
1444         */
1445        function getReferencesInContainer(container: Node, sourceFile: SourceFile, search: Search, state: State, addReferencesHere: boolean): void {
1446            if (!state.markSearchedSymbols(sourceFile, search.allSearchSymbols)) {
1447                return;
1448            }
1449
1450            for (const position of getPossibleSymbolReferencePositions(sourceFile, search.text, container)) {
1451                getReferencesAtLocation(sourceFile, position, search, state, addReferencesHere);
1452            }
1453        }
1454
1455        function hasMatchingMeaning(referenceLocation: Node, state: State): boolean {
1456            return !!(getMeaningFromLocation(referenceLocation) & state.searchMeaning);
1457        }
1458
1459        function getReferencesAtLocation(sourceFile: SourceFile, position: number, search: Search, state: State, addReferencesHere: boolean): void {
1460            const referenceLocation = getTouchingPropertyName(sourceFile, position);
1461
1462            if (!isValidReferencePosition(referenceLocation, search.text)) {
1463                // This wasn't the start of a token.  Check to see if it might be a
1464                // match in a comment or string if that's what the caller is asking
1465                // for.
1466                if (!state.options.implementations && (state.options.findInStrings && isInString(sourceFile, position) || state.options.findInComments && isInNonReferenceComment(sourceFile, position))) {
1467                    // In the case where we're looking inside comments/strings, we don't have
1468                    // an actual definition.  So just use 'undefined' here.  Features like
1469                    // 'Rename' won't care (as they ignore the definitions), and features like
1470                    // 'FindReferences' will just filter out these results.
1471                    state.addStringOrCommentReference(sourceFile.fileName, createTextSpan(position, search.text.length));
1472                }
1473
1474                return;
1475            }
1476
1477            if (!hasMatchingMeaning(referenceLocation, state)) return;
1478
1479            const referenceSymbol = state.checker.getSymbolAtLocation(referenceLocation);
1480            if (!referenceSymbol) {
1481                return;
1482            }
1483
1484            const parent = referenceLocation.parent;
1485            if (isImportSpecifier(parent) && parent.propertyName === referenceLocation) {
1486                // This is added through `singleReferences` in ImportsResult. If we happen to see it again, don't add it again.
1487                return;
1488            }
1489
1490            if (isExportSpecifier(parent)) {
1491                Debug.assert(referenceLocation.kind === SyntaxKind.Identifier);
1492                getReferencesAtExportSpecifier(referenceLocation as Identifier, referenceSymbol, parent, search, state, addReferencesHere);
1493                return;
1494            }
1495
1496            const relatedSymbol = getRelatedSymbol(search, referenceSymbol, referenceLocation, state);
1497            if (!relatedSymbol) {
1498                getReferenceForShorthandProperty(referenceSymbol, search, state);
1499                return;
1500            }
1501
1502            switch (state.specialSearchKind) {
1503                case SpecialSearchKind.None:
1504                    if (addReferencesHere) addReference(referenceLocation, relatedSymbol, state);
1505                    break;
1506                case SpecialSearchKind.Constructor:
1507                    addConstructorReferences(referenceLocation, sourceFile, search, state);
1508                    break;
1509                case SpecialSearchKind.Class:
1510                    addClassStaticThisReferences(referenceLocation, search, state);
1511                    break;
1512                default:
1513                    Debug.assertNever(state.specialSearchKind);
1514            }
1515
1516            getImportOrExportReferences(referenceLocation, referenceSymbol, search, state);
1517        }
1518
1519        function getReferencesAtExportSpecifier(
1520            referenceLocation: Identifier,
1521            referenceSymbol: Symbol,
1522            exportSpecifier: ExportSpecifier,
1523            search: Search,
1524            state: State,
1525            addReferencesHere: boolean,
1526            alwaysGetReferences?: boolean,
1527        ): void {
1528            Debug.assert(!alwaysGetReferences || !!state.options.providePrefixAndSuffixTextForRename, "If alwaysGetReferences is true, then prefix/suffix text must be enabled");
1529
1530            const { parent, propertyName, name } = exportSpecifier;
1531            const exportDeclaration = parent.parent;
1532            const localSymbol = getLocalSymbolForExportSpecifier(referenceLocation, referenceSymbol, exportSpecifier, state.checker);
1533            if (!alwaysGetReferences && !search.includes(localSymbol)) {
1534                return;
1535            }
1536
1537            if (!propertyName) {
1538                // Don't rename at `export { default } from "m";`. (but do continue to search for imports of the re-export)
1539                if (!(state.options.use === FindReferencesUse.Rename && (name.escapedText === InternalSymbolName.Default))) {
1540                    addRef();
1541                }
1542            }
1543            else if (referenceLocation === propertyName) {
1544                // For `export { foo as bar } from "baz"`, "`foo`" will be added from the singleReferences for import searches of the original export.
1545                // For `export { foo as bar };`, where `foo` is a local, so add it now.
1546                if (!exportDeclaration.moduleSpecifier) {
1547                    addRef();
1548                }
1549
1550                if (addReferencesHere && state.options.use !== FindReferencesUse.Rename && state.markSeenReExportRHS(name)) {
1551                    addReference(name, Debug.checkDefined(exportSpecifier.symbol), state);
1552                }
1553            }
1554            else {
1555                if (state.markSeenReExportRHS(referenceLocation)) {
1556                    addRef();
1557                }
1558            }
1559
1560            // For `export { foo as bar }`, rename `foo`, but not `bar`.
1561            if (!isForRenameWithPrefixAndSuffixText(state.options) || alwaysGetReferences) {
1562                const isDefaultExport = referenceLocation.originalKeywordKind === SyntaxKind.DefaultKeyword
1563                    || exportSpecifier.name.originalKeywordKind === SyntaxKind.DefaultKeyword;
1564                const exportKind = isDefaultExport ? ExportKind.Default : ExportKind.Named;
1565                const exportSymbol = Debug.checkDefined(exportSpecifier.symbol);
1566                const exportInfo = getExportInfo(exportSymbol, exportKind, state.checker);
1567                if (exportInfo) {
1568                    searchForImportsOfExport(referenceLocation, exportSymbol, exportInfo, state);
1569                }
1570            }
1571
1572            // At `export { x } from "foo"`, also search for the imported symbol `"foo".x`.
1573            if (search.comingFrom !== ImportExport.Export && exportDeclaration.moduleSpecifier && !propertyName && !isForRenameWithPrefixAndSuffixText(state.options)) {
1574                const imported = state.checker.getExportSpecifierLocalTargetSymbol(exportSpecifier);
1575                if (imported) searchForImportedSymbol(imported, state);
1576            }
1577
1578            function addRef() {
1579                if (addReferencesHere) addReference(referenceLocation, localSymbol, state);
1580            }
1581        }
1582
1583        function getLocalSymbolForExportSpecifier(referenceLocation: Identifier, referenceSymbol: Symbol, exportSpecifier: ExportSpecifier, checker: TypeChecker): Symbol {
1584            return isExportSpecifierAlias(referenceLocation, exportSpecifier) && checker.getExportSpecifierLocalTargetSymbol(exportSpecifier) || referenceSymbol;
1585        }
1586
1587        function isExportSpecifierAlias(referenceLocation: Identifier, exportSpecifier: ExportSpecifier): boolean {
1588            const { parent, propertyName, name } = exportSpecifier;
1589            Debug.assert(propertyName === referenceLocation || name === referenceLocation);
1590            if (propertyName) {
1591                // Given `export { foo as bar } [from "someModule"]`: It's an alias at `foo`, but at `bar` it's a new symbol.
1592                return propertyName === referenceLocation;
1593            }
1594            else {
1595                // `export { foo } from "foo"` is a re-export.
1596                // `export { foo };` is not a re-export, it creates an alias for the local variable `foo`.
1597                return !parent.parent.moduleSpecifier;
1598            }
1599        }
1600
1601        function getImportOrExportReferences(referenceLocation: Node, referenceSymbol: Symbol, search: Search, state: State): void {
1602            const importOrExport = getImportOrExportSymbol(referenceLocation, referenceSymbol, state.checker, search.comingFrom === ImportExport.Export);
1603            if (!importOrExport) return;
1604
1605            const { symbol } = importOrExport;
1606
1607            if (importOrExport.kind === ImportExport.Import) {
1608                if (!(isForRenameWithPrefixAndSuffixText(state.options))) {
1609                    searchForImportedSymbol(symbol, state);
1610                }
1611            }
1612            else {
1613                searchForImportsOfExport(referenceLocation, symbol, importOrExport.exportInfo, state);
1614            }
1615        }
1616
1617        function getReferenceForShorthandProperty({ flags, valueDeclaration }: Symbol, search: Search, state: State): void {
1618            const shorthandValueSymbol = state.checker.getShorthandAssignmentValueSymbol(valueDeclaration)!;
1619            const name = valueDeclaration && getNameOfDeclaration(valueDeclaration);
1620            /*
1621            * Because in short-hand property assignment, an identifier which stored as name of the short-hand property assignment
1622            * has two meanings: property name and property value. Therefore when we do findAllReference at the position where
1623            * an identifier is declared, the language service should return the position of the variable declaration as well as
1624            * the position in short-hand property assignment excluding property accessing. However, if we do findAllReference at the
1625            * position of property accessing, the referenceEntry of such position will be handled in the first case.
1626            */
1627            if (!(flags & SymbolFlags.Transient) && name && search.includes(shorthandValueSymbol)) {
1628                addReference(name, shorthandValueSymbol, state);
1629            }
1630        }
1631
1632        function addReference(referenceLocation: Node, relatedSymbol: Symbol | RelatedSymbol, state: State): void {
1633            const { kind, symbol } = "kind" in relatedSymbol ? relatedSymbol : { kind: undefined, symbol: relatedSymbol }; // eslint-disable-line no-in-operator
1634            const addRef = state.referenceAdder(symbol);
1635            if (state.options.implementations) {
1636                addImplementationReferences(referenceLocation, addRef, state);
1637            }
1638            else {
1639                addRef(referenceLocation, kind);
1640            }
1641        }
1642
1643        /** Adds references when a constructor is used with `new this()` in its own class and `super()` calls in subclasses.  */
1644        function addConstructorReferences(referenceLocation: Node, sourceFile: SourceFile, search: Search, state: State): void {
1645            if (isNewExpressionTarget(referenceLocation)) {
1646                addReference(referenceLocation, search.symbol, state);
1647            }
1648
1649            const pusher = () => state.referenceAdder(search.symbol);
1650
1651            if (isClassLike(referenceLocation.parent)) {
1652                Debug.assert(referenceLocation.kind === SyntaxKind.DefaultKeyword || referenceLocation.parent.name === referenceLocation);
1653                // This is the class declaration containing the constructor.
1654                findOwnConstructorReferences(search.symbol, sourceFile, pusher());
1655            }
1656            else {
1657                // If this class appears in `extends C`, then the extending class' "super" calls are references.
1658                const classExtending = tryGetClassByExtendingIdentifier(referenceLocation);
1659                if (classExtending) {
1660                    findSuperConstructorAccesses(classExtending, pusher());
1661                    findInheritedConstructorReferences(classExtending, state);
1662                }
1663            }
1664        }
1665
1666        function addClassStaticThisReferences(referenceLocation: Node, search: Search, state: State): void {
1667            addReference(referenceLocation, search.symbol, state);
1668            const classLike = referenceLocation.parent;
1669            if (state.options.use === FindReferencesUse.Rename || !isClassLike(classLike)) return;
1670            Debug.assert(classLike.name === referenceLocation);
1671            const addRef = state.referenceAdder(search.symbol);
1672            for (const member of classLike.members) {
1673                if (!(isMethodOrAccessor(member) && hasSyntacticModifier(member, ModifierFlags.Static))) {
1674                    continue;
1675                }
1676                if (member.body) {
1677                    member.body.forEachChild(function cb(node) {
1678                        if (node.kind === SyntaxKind.ThisKeyword) {
1679                            addRef(node);
1680                        }
1681                        else if (!isFunctionLike(node) && !isClassLike(node)) {
1682                            node.forEachChild(cb);
1683                        }
1684                    });
1685                }
1686            }
1687        }
1688
1689        /**
1690         * `classSymbol` is the class where the constructor was defined.
1691         * Reference the constructor and all calls to `new this()`.
1692         */
1693        function findOwnConstructorReferences(classSymbol: Symbol, sourceFile: SourceFile, addNode: (node: Node) => void): void {
1694            const constructorSymbol = getClassConstructorSymbol(classSymbol);
1695            if (constructorSymbol && constructorSymbol.declarations) {
1696                for (const decl of constructorSymbol.declarations) {
1697                    const ctrKeyword = findChildOfKind(decl, SyntaxKind.ConstructorKeyword, sourceFile)!;
1698                    Debug.assert(decl.kind === SyntaxKind.Constructor && !!ctrKeyword);
1699                    addNode(ctrKeyword);
1700                }
1701            }
1702
1703            if (classSymbol.exports) {
1704                classSymbol.exports.forEach(member => {
1705                    const decl = member.valueDeclaration;
1706                    if (decl && decl.kind === SyntaxKind.MethodDeclaration) {
1707                        const body = (<MethodDeclaration>decl).body;
1708                        if (body) {
1709                            forEachDescendantOfKind(body, SyntaxKind.ThisKeyword, thisKeyword => {
1710                                if (isNewExpressionTarget(thisKeyword)) {
1711                                    addNode(thisKeyword);
1712                                }
1713                            });
1714                        }
1715                    }
1716                });
1717            }
1718        }
1719
1720        function getClassConstructorSymbol(classSymbol: Symbol): Symbol | undefined {
1721            return classSymbol.members && classSymbol.members.get(InternalSymbolName.Constructor);
1722        }
1723
1724        /** Find references to `super` in the constructor of an extending class.  */
1725        function findSuperConstructorAccesses(classDeclaration: ClassLikeDeclaration, addNode: (node: Node) => void): void {
1726            const constructor = getClassConstructorSymbol(classDeclaration.symbol);
1727            if (!(constructor && constructor.declarations)) {
1728                return;
1729            }
1730
1731            for (const decl of constructor.declarations) {
1732                Debug.assert(decl.kind === SyntaxKind.Constructor);
1733                const body = (<ConstructorDeclaration>decl).body;
1734                if (body) {
1735                    forEachDescendantOfKind(body, SyntaxKind.SuperKeyword, node => {
1736                        if (isCallExpressionTarget(node)) {
1737                            addNode(node);
1738                        }
1739                    });
1740                }
1741            }
1742        }
1743
1744        function hasOwnConstructor(classDeclaration: ClassLikeDeclaration): boolean {
1745            return !!getClassConstructorSymbol(classDeclaration.symbol);
1746        }
1747
1748        function findInheritedConstructorReferences(classDeclaration: ClassLikeDeclaration, state: State): void {
1749            if (hasOwnConstructor(classDeclaration)) return;
1750            const classSymbol = classDeclaration.symbol;
1751            const search = state.createSearch(/*location*/ undefined, classSymbol, /*comingFrom*/ undefined);
1752            getReferencesInContainerOrFiles(classSymbol, state, search);
1753        }
1754
1755        function addImplementationReferences(refNode: Node, addReference: (node: Node) => void, state: State): void {
1756            // Check if we found a function/propertyAssignment/method with an implementation or initializer
1757            if (isDeclarationName(refNode) && isImplementation(refNode.parent)) {
1758                addReference(refNode);
1759                return;
1760            }
1761
1762            if (refNode.kind !== SyntaxKind.Identifier) {
1763                return;
1764            }
1765
1766            if (refNode.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
1767                // Go ahead and dereference the shorthand assignment by going to its definition
1768                getReferenceEntriesForShorthandPropertyAssignment(refNode, state.checker, addReference);
1769            }
1770
1771            // Check if the node is within an extends or implements clause
1772            const containingClass = getContainingClassIfInHeritageClause(refNode);
1773            if (containingClass) {
1774                addReference(containingClass);
1775                return;
1776            }
1777
1778            // If we got a type reference, try and see if the reference applies to any expressions that can implement an interface
1779            // Find the first node whose parent isn't a type node -- i.e., the highest type node.
1780            const typeNode = findAncestor(refNode, a => !isQualifiedName(a.parent) && !isTypeNode(a.parent) && !isTypeElement(a.parent))!;
1781            const typeHavingNode = typeNode.parent;
1782            if (hasType(typeHavingNode) && typeHavingNode.type === typeNode && state.markSeenContainingTypeReference(typeHavingNode)) {
1783                if (hasInitializer(typeHavingNode)) {
1784                    addIfImplementation(typeHavingNode.initializer!);
1785                }
1786                else if (isFunctionLike(typeHavingNode) && (typeHavingNode as FunctionLikeDeclaration).body) {
1787                    const body = (typeHavingNode as FunctionLikeDeclaration).body!;
1788                    if (body.kind === SyntaxKind.Block) {
1789                        forEachReturnStatement(<Block>body, returnStatement => {
1790                            if (returnStatement.expression) addIfImplementation(returnStatement.expression);
1791                        });
1792                    }
1793                    else {
1794                        addIfImplementation(body);
1795                    }
1796                }
1797                else if (isAssertionExpression(typeHavingNode)) {
1798                    addIfImplementation(typeHavingNode.expression);
1799                }
1800            }
1801
1802            function addIfImplementation(e: Expression): void {
1803                if (isImplementationExpression(e)) addReference(e);
1804            }
1805        }
1806
1807        function getContainingClassIfInHeritageClause(node: Node): ClassLikeDeclaration | InterfaceDeclaration | undefined {
1808            return isIdentifier(node) || isPropertyAccessExpression(node) ? getContainingClassIfInHeritageClause(node.parent)
1809                : isExpressionWithTypeArguments(node) ? tryCast(node.parent.parent, isClassLike) : undefined;
1810        }
1811
1812        /**
1813         * Returns true if this is an expression that can be considered an implementation
1814         */
1815        function isImplementationExpression(node: Expression): boolean {
1816            switch (node.kind) {
1817                case SyntaxKind.ParenthesizedExpression:
1818                    return isImplementationExpression((<ParenthesizedExpression>node).expression);
1819                case SyntaxKind.ArrowFunction:
1820                case SyntaxKind.FunctionExpression:
1821                case SyntaxKind.ObjectLiteralExpression:
1822                case SyntaxKind.ClassExpression:
1823                case SyntaxKind.ArrayLiteralExpression:
1824                    return true;
1825                default:
1826                    return false;
1827            }
1828        }
1829
1830        /**
1831         * Determines if the parent symbol occurs somewhere in the child's ancestry. If the parent symbol
1832         * is an interface, determines if some ancestor of the child symbol extends or inherits from it.
1833         * Also takes in a cache of previous results which makes this slightly more efficient and is
1834         * necessary to avoid potential loops like so:
1835         *     class A extends B { }
1836         *     class B extends A { }
1837         *
1838         * We traverse the AST rather than using the type checker because users are typically only interested
1839         * in explicit implementations of an interface/class when calling "Go to Implementation". Sibling
1840         * implementations of types that share a common ancestor with the type whose implementation we are
1841         * searching for need to be filtered out of the results. The type checker doesn't let us make the
1842         * distinction between structurally compatible implementations and explicit implementations, so we
1843         * must use the AST.
1844         *
1845         * @param symbol         A class or interface Symbol
1846         * @param parent        Another class or interface Symbol
1847         * @param cachedResults A map of symbol id pairs (i.e. "child,parent") to booleans indicating previous results
1848         */
1849        function explicitlyInheritsFrom(symbol: Symbol, parent: Symbol, cachedResults: ESMap<string, boolean>, checker: TypeChecker): boolean {
1850            if (symbol === parent) {
1851                return true;
1852            }
1853
1854            const key = getSymbolId(symbol) + "," + getSymbolId(parent);
1855            const cached = cachedResults.get(key);
1856            if (cached !== undefined) {
1857                return cached;
1858            }
1859
1860            // Set the key so that we don't infinitely recurse
1861            cachedResults.set(key, false);
1862
1863            const inherits = !!symbol.declarations && symbol.declarations.some(declaration =>
1864                getAllSuperTypeNodes(declaration).some(typeReference => {
1865                    const type = checker.getTypeAtLocation(typeReference);
1866                    return !!type && !!type.symbol && explicitlyInheritsFrom(type.symbol, parent, cachedResults, checker);
1867                }));
1868            cachedResults.set(key, inherits);
1869            return inherits;
1870        }
1871
1872        function getReferencesForSuperKeyword(superKeyword: Node): SymbolAndEntries[] | undefined {
1873            let searchSpaceNode = getSuperContainer(superKeyword, /*stopOnFunctions*/ false);
1874            if (!searchSpaceNode) {
1875                return undefined;
1876            }
1877            // Whether 'super' occurs in a static context within a class.
1878            let staticFlag = ModifierFlags.Static;
1879
1880            switch (searchSpaceNode.kind) {
1881                case SyntaxKind.PropertyDeclaration:
1882                case SyntaxKind.PropertySignature:
1883                case SyntaxKind.MethodDeclaration:
1884                case SyntaxKind.MethodSignature:
1885                case SyntaxKind.Constructor:
1886                case SyntaxKind.GetAccessor:
1887                case SyntaxKind.SetAccessor:
1888                    staticFlag &= getSyntacticModifierFlags(searchSpaceNode);
1889                    searchSpaceNode = searchSpaceNode.parent; // re-assign to be the owning class
1890                    break;
1891                default:
1892                    return undefined;
1893            }
1894
1895            const sourceFile = searchSpaceNode.getSourceFile();
1896            const references = mapDefined(getPossibleSymbolReferenceNodes(sourceFile, "super", searchSpaceNode), node => {
1897                if (node.kind !== SyntaxKind.SuperKeyword) {
1898                    return;
1899                }
1900
1901                const container = getSuperContainer(node, /*stopOnFunctions*/ false);
1902
1903                // If we have a 'super' container, we must have an enclosing class.
1904                // Now make sure the owning class is the same as the search-space
1905                // and has the same static qualifier as the original 'super's owner.
1906                return container && (ModifierFlags.Static & getSyntacticModifierFlags(container)) === staticFlag && container.parent.symbol === searchSpaceNode.symbol ? nodeEntry(node) : undefined;
1907            });
1908
1909            return [{ definition: { type: DefinitionKind.Symbol, symbol: searchSpaceNode.symbol }, references }];
1910        }
1911
1912        function isParameterName(node: Node) {
1913            return node.kind === SyntaxKind.Identifier && node.parent.kind === SyntaxKind.Parameter && (<ParameterDeclaration>node.parent).name === node;
1914        }
1915
1916        function getReferencesForThisKeyword(thisOrSuperKeyword: Node, sourceFiles: readonly SourceFile[], cancellationToken: CancellationToken): SymbolAndEntries[] | undefined {
1917            let searchSpaceNode = getThisContainer(thisOrSuperKeyword, /* includeArrowFunctions */ false);
1918
1919            // Whether 'this' occurs in a static context within a class.
1920            let staticFlag = ModifierFlags.Static;
1921
1922            switch (searchSpaceNode.kind) {
1923                case SyntaxKind.MethodDeclaration:
1924                case SyntaxKind.MethodSignature:
1925                    if (isObjectLiteralMethod(searchSpaceNode)) {
1926                        break;
1927                    }
1928                    // falls through
1929                case SyntaxKind.PropertyDeclaration:
1930                case SyntaxKind.PropertySignature:
1931                case SyntaxKind.Constructor:
1932                case SyntaxKind.GetAccessor:
1933                case SyntaxKind.SetAccessor:
1934                    staticFlag &= getSyntacticModifierFlags(searchSpaceNode);
1935                    searchSpaceNode = searchSpaceNode.parent; // re-assign to be the owning class
1936                    break;
1937                case SyntaxKind.SourceFile:
1938                    if (isExternalModule(<SourceFile>searchSpaceNode) || isParameterName(thisOrSuperKeyword)) {
1939                        return undefined;
1940                    }
1941                    // falls through
1942                case SyntaxKind.FunctionDeclaration:
1943                case SyntaxKind.FunctionExpression:
1944                    break;
1945                // Computed properties in classes are not handled here because references to this are illegal,
1946                // so there is no point finding references to them.
1947                default:
1948                    return undefined;
1949            }
1950
1951            const references = flatMap(searchSpaceNode.kind === SyntaxKind.SourceFile ? sourceFiles : [searchSpaceNode.getSourceFile()], sourceFile => {
1952                cancellationToken.throwIfCancellationRequested();
1953                return getPossibleSymbolReferenceNodes(sourceFile, "this", isSourceFile(searchSpaceNode) ? sourceFile : searchSpaceNode).filter(node => {
1954                    if (!isThis(node)) {
1955                        return false;
1956                    }
1957                    const container = getThisContainer(node, /* includeArrowFunctions */ false);
1958                    switch (searchSpaceNode.kind) {
1959                        case SyntaxKind.FunctionExpression:
1960                        case SyntaxKind.FunctionDeclaration:
1961                            return searchSpaceNode.symbol === container.symbol;
1962                        case SyntaxKind.MethodDeclaration:
1963                        case SyntaxKind.MethodSignature:
1964                            return isObjectLiteralMethod(searchSpaceNode) && searchSpaceNode.symbol === container.symbol;
1965                        case SyntaxKind.ClassExpression:
1966                        case SyntaxKind.ClassDeclaration:
1967                        case SyntaxKind.StructDeclaration:
1968                            // Make sure the container belongs to the same class
1969                            // and has the appropriate static modifier from the original container.
1970                            return container.parent && searchSpaceNode.symbol === container.parent.symbol && (getSyntacticModifierFlags(container) & ModifierFlags.Static) === staticFlag;
1971                        case SyntaxKind.SourceFile:
1972                            return container.kind === SyntaxKind.SourceFile && !isExternalModule(<SourceFile>container) && !isParameterName(node);
1973                    }
1974                });
1975            }).map(n => nodeEntry(n));
1976
1977            const thisParameter = firstDefined(references, r => isParameter(r.node.parent) ? r.node : undefined);
1978            return [{
1979                definition: { type: DefinitionKind.This, node: thisParameter || thisOrSuperKeyword },
1980                references
1981            }];
1982        }
1983
1984        function getReferencesForStringLiteral(node: StringLiteralLike, sourceFiles: readonly SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken): SymbolAndEntries[] {
1985            const type = getContextualTypeOrAncestorTypeNodeType(node, checker);
1986            const references = flatMap(sourceFiles, sourceFile => {
1987                cancellationToken.throwIfCancellationRequested();
1988                return mapDefined(getPossibleSymbolReferenceNodes(sourceFile, node.text), ref => {
1989                    if (isStringLiteralLike(ref) && ref.text === node.text) {
1990                        if (type) {
1991                            const refType = getContextualTypeOrAncestorTypeNodeType(ref, checker);
1992                            if (type !== checker.getStringType() && type === refType) {
1993                                return nodeEntry(ref, EntryKind.StringLiteral);
1994                            }
1995                        }
1996                        else {
1997                            return nodeEntry(ref, EntryKind.StringLiteral);
1998                        }
1999                    }
2000                });
2001            });
2002
2003            return [{
2004                definition: { type: DefinitionKind.String, node },
2005                references
2006            }];
2007        }
2008
2009        // For certain symbol kinds, we need to include other symbols in the search set.
2010        // This is not needed when searching for re-exports.
2011        function populateSearchSymbolSet(symbol: Symbol, location: Node, checker: TypeChecker, isForRename: boolean, providePrefixAndSuffixText: boolean, implementations: boolean): Symbol[] {
2012            const result: Symbol[] = [];
2013            forEachRelatedSymbol<void>(symbol, location, checker, isForRename, !(isForRename && providePrefixAndSuffixText),
2014                (sym, root, base) => {
2015                    // static method/property and instance method/property might have the same name. Only include static or only include instance.
2016                    if (base) {
2017                        if (isStatic(symbol) !== isStatic(base)) {
2018                            base = undefined;
2019                        }
2020                    }
2021                    result.push(base || root || sym);
2022                },
2023                // when try to find implementation, implementations is true, and not allowed to find base class
2024                /*allowBaseTypes*/() => !implementations);
2025            return result;
2026        }
2027
2028        /**
2029         * @param allowBaseTypes return true means it would try to find in base class or interface.
2030         */
2031        function forEachRelatedSymbol<T>(
2032            symbol: Symbol, location: Node, checker: TypeChecker, isForRenamePopulateSearchSymbolSet: boolean, onlyIncludeBindingElementAtReferenceLocation: boolean,
2033            /**
2034             * @param baseSymbol This symbol means one property/mehtod from base class or interface when it is not null or undefined,
2035             */
2036            cbSymbol: (symbol: Symbol, rootSymbol?: Symbol, baseSymbol?: Symbol, kind?: NodeEntryKind) => T | undefined,
2037            allowBaseTypes: (rootSymbol: Symbol) => boolean,
2038        ): T | undefined {
2039            const containingObjectLiteralElement = getContainingObjectLiteralElement(location);
2040            if (containingObjectLiteralElement) {
2041                /* Because in short-hand property assignment, location has two meaning : property name and as value of the property
2042                * When we do findAllReference at the position of the short-hand property assignment, we would want to have references to position of
2043                * property name and variable declaration of the identifier.
2044                * Like in below example, when querying for all references for an identifier 'name', of the property assignment, the language service
2045                * should show both 'name' in 'obj' and 'name' in variable declaration
2046                *      const name = "Foo";
2047                *      const obj = { name };
2048                * In order to do that, we will populate the search set with the value symbol of the identifier as a value of the property assignment
2049                * so that when matching with potential reference symbol, both symbols from property declaration and variable declaration
2050                * will be included correctly.
2051                */
2052                const shorthandValueSymbol = checker.getShorthandAssignmentValueSymbol(location.parent); // gets the local symbol
2053                if (shorthandValueSymbol && isForRenamePopulateSearchSymbolSet) {
2054                    // When renaming 'x' in `const o = { x }`, just rename the local variable, not the property.
2055                    return cbSymbol(shorthandValueSymbol, /*rootSymbol*/ undefined, /*baseSymbol*/ undefined, EntryKind.SearchedLocalFoundProperty);
2056                }
2057
2058                // If the location is in a context sensitive location (i.e. in an object literal) try
2059                // to get a contextual type for it, and add the property symbol from the contextual
2060                // type to the search set
2061                const contextualType = checker.getContextualType(containingObjectLiteralElement.parent);
2062                const res = contextualType && firstDefined(
2063                    getPropertySymbolsFromContextualType(containingObjectLiteralElement, checker, contextualType, /*unionSymbolOk*/ true),
2064                    sym => fromRoot(sym, EntryKind.SearchedPropertyFoundLocal));
2065                if (res) return res;
2066
2067                // If the location is name of property symbol from object literal destructuring pattern
2068                // Search the property symbol
2069                //      for ( { property: p2 } of elems) { }
2070                const propertySymbol = getPropertySymbolOfDestructuringAssignment(location, checker);
2071                const res1 = propertySymbol && cbSymbol(propertySymbol, /*rootSymbol*/ undefined, /*baseSymbol*/ undefined, EntryKind.SearchedPropertyFoundLocal);
2072                if (res1) return res1;
2073
2074                const res2 = shorthandValueSymbol && cbSymbol(shorthandValueSymbol, /*rootSymbol*/ undefined, /*baseSymbol*/ undefined, EntryKind.SearchedLocalFoundProperty);
2075                if (res2) return res2;
2076            }
2077
2078            const aliasedSymbol = getMergedAliasedSymbolOfNamespaceExportDeclaration(location, symbol, checker);
2079            if (aliasedSymbol) {
2080                // In case of UMD module and global merging, search for global as well
2081                const res = cbSymbol(aliasedSymbol, /*rootSymbol*/ undefined, /*baseSymbol*/ undefined, EntryKind.Node);
2082                if (res) return res;
2083            }
2084
2085            const res = fromRoot(symbol);
2086            if (res) return res;
2087
2088            if (symbol.valueDeclaration && isParameterPropertyDeclaration(symbol.valueDeclaration, symbol.valueDeclaration.parent)) {
2089                // For a parameter property, now try on the other symbol (property if this was a parameter, parameter if this was a property).
2090                const paramProps = checker.getSymbolsOfParameterPropertyDeclaration(cast(symbol.valueDeclaration, isParameter), symbol.name);
2091                Debug.assert(paramProps.length === 2 && !!(paramProps[0].flags & SymbolFlags.FunctionScopedVariable) && !!(paramProps[1].flags & SymbolFlags.Property)); // is [parameter, property]
2092                return fromRoot(symbol.flags & SymbolFlags.FunctionScopedVariable ? paramProps[1] : paramProps[0]);
2093            }
2094
2095            const exportSpecifier = getDeclarationOfKind<ExportSpecifier>(symbol, SyntaxKind.ExportSpecifier);
2096            if (!isForRenamePopulateSearchSymbolSet || exportSpecifier && !exportSpecifier.propertyName) {
2097                const localSymbol = exportSpecifier && checker.getExportSpecifierLocalTargetSymbol(exportSpecifier);
2098                if (localSymbol) {
2099                    const res = cbSymbol(localSymbol, /*rootSymbol*/ undefined, /*baseSymbol*/ undefined, EntryKind.Node);
2100                    if (res) return res;
2101                }
2102            }
2103
2104            // symbolAtLocation for a binding element is the local symbol. See if the search symbol is the property.
2105            // Don't do this when populating search set for a rename when prefix and suffix text will be provided -- just rename the local.
2106            if (!isForRenamePopulateSearchSymbolSet) {
2107                let bindingElementPropertySymbol: Symbol | undefined;
2108                if (onlyIncludeBindingElementAtReferenceLocation) {
2109                    bindingElementPropertySymbol = isObjectBindingElementWithoutPropertyName(location.parent) ? getPropertySymbolFromBindingElement(checker, location.parent) : undefined;
2110                }
2111                else {
2112                    bindingElementPropertySymbol = getPropertySymbolOfObjectBindingPatternWithoutPropertyName(symbol, checker);
2113                }
2114                return bindingElementPropertySymbol && fromRoot(bindingElementPropertySymbol, EntryKind.SearchedPropertyFoundLocal);
2115            }
2116
2117            Debug.assert(isForRenamePopulateSearchSymbolSet);
2118            // due to the above assert and the arguments at the uses of this function,
2119            // (onlyIncludeBindingElementAtReferenceLocation <=> !providePrefixAndSuffixTextForRename) holds
2120            const includeOriginalSymbolOfBindingElement = onlyIncludeBindingElementAtReferenceLocation;
2121
2122            if (includeOriginalSymbolOfBindingElement) {
2123                const bindingElementPropertySymbol = getPropertySymbolOfObjectBindingPatternWithoutPropertyName(symbol, checker);
2124                return bindingElementPropertySymbol && fromRoot(bindingElementPropertySymbol, EntryKind.SearchedPropertyFoundLocal);
2125            }
2126
2127            function fromRoot(sym: Symbol, kind?: NodeEntryKind): T | undefined {
2128                // If this is a union property:
2129                //   - In populateSearchSymbolsSet we will add all the symbols from all its source symbols in all unioned types.
2130                //   - In findRelatedSymbol, we will just use the union symbol if any source symbol is included in the search.
2131                // If the symbol is an instantiation from a another symbol (e.g. widened symbol):
2132                //   - In populateSearchSymbolsSet, add the root the list
2133                //   - In findRelatedSymbol, return the source symbol if that is in the search. (Do not return the instantiation symbol.)
2134                return firstDefined(checker.getRootSymbols(sym), rootSymbol =>
2135                    cbSymbol(sym, rootSymbol, /*baseSymbol*/ undefined, kind)
2136                    // Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions
2137                    || (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface) && allowBaseTypes(rootSymbol)
2138                        ? getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.name, checker, base => cbSymbol(sym, rootSymbol, base, kind))
2139                        : undefined));
2140            }
2141
2142            function getPropertySymbolOfObjectBindingPatternWithoutPropertyName(symbol: Symbol, checker: TypeChecker): Symbol | undefined {
2143                const bindingElement = getDeclarationOfKind<BindingElement>(symbol, SyntaxKind.BindingElement);
2144                if (bindingElement && isObjectBindingElementWithoutPropertyName(bindingElement)) {
2145                    return getPropertySymbolFromBindingElement(checker, bindingElement);
2146                }
2147            }
2148        }
2149
2150        /**
2151         * Find symbol of the given property-name and add the symbol to the given result array
2152         * @param symbol a symbol to start searching for the given propertyName
2153         * @param propertyName a name of property to search for
2154         * @param result an array of symbol of found property symbols
2155         * @param previousIterationSymbolsCache a cache of symbol from previous iterations of calling this function to prevent infinite revisiting of the same symbol.
2156         *                                The value of previousIterationSymbol is undefined when the function is first called.
2157         */
2158        function getPropertySymbolsFromBaseTypes<T>(symbol: Symbol, propertyName: string, checker: TypeChecker, cb: (symbol: Symbol) => T | undefined): T | undefined {
2159            const seen = new Map<string, true>();
2160            return recur(symbol);
2161
2162            function recur(symbol: Symbol): T | undefined {
2163                // Use `addToSeen` to ensure we don't infinitely recurse in this situation:
2164                //      interface C extends C {
2165                //          /*findRef*/propName: string;
2166                //      }
2167                if (!(symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) || !addToSeen(seen, getSymbolId(symbol))) return;
2168
2169                return firstDefined(symbol.declarations, declaration => firstDefined(getAllSuperTypeNodes(declaration), typeReference => {
2170                    const type = checker.getTypeAtLocation(typeReference);
2171                    const propertySymbol = type && type.symbol && checker.getPropertyOfType(type, propertyName);
2172                    // Visit the typeReference as well to see if it directly or indirectly uses that property
2173                    return type && propertySymbol && (firstDefined(checker.getRootSymbols(propertySymbol), cb) || recur(type.symbol));
2174                }));
2175            }
2176        }
2177
2178        interface RelatedSymbol {
2179            readonly symbol: Symbol;
2180            readonly kind: NodeEntryKind | undefined;
2181        }
2182
2183        function isStatic(symbol: Symbol): boolean {
2184            if (!symbol.valueDeclaration) { return false; }
2185            const modifierFlags = getEffectiveModifierFlags(symbol.valueDeclaration);
2186            return !!(modifierFlags & ModifierFlags.Static);
2187        }
2188
2189        function getRelatedSymbol(search: Search, referenceSymbol: Symbol, referenceLocation: Node, state: State): RelatedSymbol | undefined {
2190            const { checker } = state;
2191            return forEachRelatedSymbol(referenceSymbol, referenceLocation, checker, /*isForRenamePopulateSearchSymbolSet*/ false,
2192                /*onlyIncludeBindingElementAtReferenceLocation*/ state.options.use !== FindReferencesUse.Rename || !!state.options.providePrefixAndSuffixTextForRename,
2193                (sym, rootSymbol, baseSymbol, kind): RelatedSymbol | undefined => {
2194                    // check whether the symbol used to search itself is just the searched one.
2195                    if (baseSymbol) {
2196                        // static method/property and instance method/property might have the same name. Only check static or only check instance.
2197                        if (isStatic(referenceSymbol) !== isStatic(baseSymbol)) {
2198                            baseSymbol = undefined;
2199                        }
2200                    }
2201                    return search.includes(baseSymbol || rootSymbol || sym)
2202                        // For a base type, use the symbol for the derived type. For a synthetic (e.g. union) property, use the union symbol.
2203                        ? { symbol: rootSymbol && !(getCheckFlags(sym) & CheckFlags.Synthetic) ? rootSymbol : sym, kind }
2204                        : undefined;
2205                },
2206                /*allowBaseTypes*/ rootSymbol =>
2207                    !(search.parents && !search.parents.some(parent => explicitlyInheritsFrom(rootSymbol.parent!, parent, state.inheritsFromCache, checker)))
2208            );
2209        }
2210
2211        /**
2212         * Given an initial searchMeaning, extracted from a location, widen the search scope based on the declarations
2213         * of the corresponding symbol. e.g. if we are searching for "Foo" in value position, but "Foo" references a class
2214         * then we need to widen the search to include type positions as well.
2215         * On the contrary, if we are searching for "Bar" in type position and we trace bar to an interface, and an uninstantiated
2216         * module, we want to keep the search limited to only types, as the two declarations (interface and uninstantiated module)
2217         * do not intersect in any of the three spaces.
2218         */
2219        export function getIntersectingMeaningFromDeclarations(node: Node, symbol: Symbol): SemanticMeaning {
2220            let meaning = getMeaningFromLocation(node);
2221            const { declarations } = symbol;
2222            if (declarations) {
2223                let lastIterationMeaning: SemanticMeaning;
2224                do {
2225                    // The result is order-sensitive, for instance if initialMeaning === Namespace, and declarations = [class, instantiated module]
2226                    // we need to consider both as they initialMeaning intersects with the module in the namespace space, and the module
2227                    // intersects with the class in the value space.
2228                    // To achieve that we will keep iterating until the result stabilizes.
2229
2230                    // Remember the last meaning
2231                    lastIterationMeaning = meaning;
2232
2233                    for (const declaration of declarations) {
2234                        const declarationMeaning = getMeaningFromDeclaration(declaration);
2235
2236                        if (declarationMeaning & meaning) {
2237                            meaning |= declarationMeaning;
2238                        }
2239                    }
2240                }
2241                while (meaning !== lastIterationMeaning);
2242            }
2243            return meaning;
2244        }
2245
2246        function isImplementation(node: Node): boolean {
2247            return !!(node.flags & NodeFlags.Ambient) ? !(isInterfaceDeclaration(node) || isTypeAliasDeclaration(node)) :
2248                (isVariableLike(node) ? hasInitializer(node) :
2249                isFunctionLikeDeclaration(node) ? !!node.body :
2250                isClassLike(node) || isModuleOrEnumDeclaration(node));
2251        }
2252
2253        export function getReferenceEntriesForShorthandPropertyAssignment(node: Node, checker: TypeChecker, addReference: (node: Node) => void): void {
2254            const refSymbol = checker.getSymbolAtLocation(node)!;
2255            const shorthandSymbol = checker.getShorthandAssignmentValueSymbol(refSymbol.valueDeclaration);
2256
2257            if (shorthandSymbol) {
2258                for (const declaration of shorthandSymbol.getDeclarations()!) {
2259                    if (getMeaningFromDeclaration(declaration) & SemanticMeaning.Value) {
2260                        addReference(declaration);
2261                    }
2262                }
2263            }
2264        }
2265
2266        function forEachDescendantOfKind(node: Node, kind: SyntaxKind, action: (node: Node) => void): void {
2267            forEachChild(node, child => {
2268                if (child.kind === kind) {
2269                    action(child);
2270                }
2271                forEachDescendantOfKind(child, kind, action);
2272            });
2273        }
2274
2275        /** Get `C` given `N` if `N` is in the position `class C extends N` or `class C extends foo.N` where `N` is an identifier. */
2276        function tryGetClassByExtendingIdentifier(node: Node): ClassLikeDeclaration | undefined {
2277            return tryGetClassExtendingExpressionWithTypeArguments(climbPastPropertyAccess(node).parent);
2278        }
2279
2280        /**
2281         * If we are just looking for implementations and this is a property access expression, we need to get the
2282         * symbol of the local type of the symbol the property is being accessed on. This is because our search
2283         * symbol may have a different parent symbol if the local type's symbol does not declare the property
2284         * being accessed (i.e. it is declared in some parent class or interface)
2285         */
2286        function getParentSymbolsOfPropertyAccess(location: Node, symbol: Symbol, checker: TypeChecker): readonly Symbol[] | undefined {
2287            const propertyAccessExpression = isRightSideOfPropertyAccess(location) ? <PropertyAccessExpression>location.parent : undefined;
2288            const lhsType = propertyAccessExpression && checker.getTypeAtLocation(propertyAccessExpression.expression);
2289            const res = mapDefined(lhsType && (lhsType.isUnionOrIntersection() ? lhsType.types : lhsType.symbol === symbol.parent ? undefined : [lhsType]), t =>
2290                t.symbol && t.symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface) ? t.symbol : undefined);
2291            return res.length === 0 ? undefined : res;
2292        }
2293
2294        function isForRenameWithPrefixAndSuffixText(options: Options) {
2295            return options.use === FindReferencesUse.Rename && options.providePrefixAndSuffixTextForRename;
2296        }
2297    }
2298}
2299