• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import {
2    AccessExpression, append, ArrowFunction, AsExpression, CallExpression, CallHierarchyIncomingCall, CallHierarchyItem,
3    CallHierarchyOutgoingCall, CancellationToken, canHaveModifiers, ClassDeclaration, ClassExpression,
4    ClassLikeDeclaration, ClassStaticBlockDeclaration, compareStringsCaseSensitive, createPrinterWithRemoveCommentsOmitTrailingSemicolon,
5    createTextRangeFromNode, createTextSpanFromBounds, createTextSpanFromRange, Debug, Decorator,
6    ElementAccessExpression, EmitHint, filter, find, FindAllReferences, findAncestor, forEach, forEachChild,
7    FunctionDeclaration, FunctionExpression, FunctionLikeDeclaration, GetAccessorDeclaration, getAssignedName,
8    getClassExtendsHeritageElement, getCombinedNodeFlags, getFirstConstructorWithBody, getNameOfDeclaration, getNodeId,
9    getNodeKind, getNodeModifiers, group, hasSyntacticModifier, Identifier, idText, indicesOf, isAccessExpression,
10    isArgumentExpressionOfElementAccess, isArray, isArrowFunction, isCallOrNewExpressionTarget, isClassDeclaration,
11    isClassExpression, isClassLike, isClassStaticBlockDeclaration, isComputedPropertyName, isConstructorDeclaration,
12    isDeclarationName, isDecoratorTarget, isFunctionDeclaration, isFunctionExpression, isFunctionLikeDeclaration,
13    isGetAccessorDeclaration, isIdentifier, isJsxOpeningLikeElement, isJsxOpeningLikeElementTagName,
14    isMethodDeclaration, isMethodSignature, isModuleBlock, isModuleDeclaration, isNamedDeclaration, isPartOfTypeNode,
15    isPropertyDeclaration, isRightSideOfPropertyAccess, isSetAccessorDeclaration, isSourceFile,
16    isStringOrNumericLiteralLike, isTaggedTemplateExpression, isTaggedTemplateTag, isVariableDeclaration,
17    JsxOpeningLikeElement, map, MethodDeclaration, ModifierFlags, ModuleDeclaration, moveRangePastModifiers,
18    NewExpression, Node, NodeFlags, ParameterDeclaration, Program, PropertyAccessExpression, SatisfiesExpression,
19    SetAccessorDeclaration, skipTrivia, SourceFile, StructDeclaration, SymbolFlags, SyntaxKind,
20    TaggedTemplateExpression, TextRange, TextSpan, TypeAssertion, TypeChecker, usingSingleLineStringWriter,
21    VariableDeclaration,
22} from "./_namespaces/ts";
23
24/** @internal */
25export type NamedExpression =
26    | ClassExpression & { name: Identifier }
27    | FunctionExpression & { name: Identifier }
28    ;
29
30/** Indictates whether a node is named function or class expression. */
31function isNamedExpression(node: Node): node is NamedExpression {
32    return (isFunctionExpression(node) || isClassExpression(node)) && isNamedDeclaration(node);
33}
34
35/** @internal */
36export type ConstNamedExpression =
37    | ClassExpression & { name: undefined, parent: VariableDeclaration & { name: Identifier } }
38    | FunctionExpression & { name: undefined, parent: VariableDeclaration & { name: Identifier } }
39    | ArrowFunction & { name: undefined, parent: VariableDeclaration & { name: Identifier } }
40    ;
41
42/** Indicates whether a node is a function, arrow, or class expression assigned to a constant variable. */
43function isConstNamedExpression(node: Node): node is ConstNamedExpression {
44    return (isFunctionExpression(node) || isArrowFunction(node) || isClassExpression(node))
45        && isVariableDeclaration(node.parent)
46        && node === node.parent.initializer
47        && isIdentifier(node.parent.name)
48        && !!(getCombinedNodeFlags(node.parent) & NodeFlags.Const);
49}
50
51/** @internal */
52export type CallHierarchyDeclaration =
53    | SourceFile
54    | ModuleDeclaration & { name: Identifier }
55    | FunctionDeclaration
56    | ClassDeclaration
57    | ClassStaticBlockDeclaration
58    | StructDeclaration
59    | MethodDeclaration
60    | GetAccessorDeclaration
61    | SetAccessorDeclaration
62    | NamedExpression
63    | ConstNamedExpression
64    ;
65
66/**
67 * Indicates whether a node could possibly be a call hierarchy declaration.
68 *
69 * See `resolveCallHierarchyDeclaration` for the specific rules.
70 */
71function isPossibleCallHierarchyDeclaration(node: Node) {
72    return isSourceFile(node)
73        || isModuleDeclaration(node)
74        || isFunctionDeclaration(node)
75        || isFunctionExpression(node)
76        || isClassDeclaration(node)
77        || isClassExpression(node)
78        || isClassStaticBlockDeclaration(node)
79        || isMethodDeclaration(node)
80        || isMethodSignature(node)
81        || isGetAccessorDeclaration(node)
82        || isSetAccessorDeclaration(node);
83}
84
85/**
86 * Indicates whether a node is a valid a call hierarchy declaration.
87 *
88 * See `resolveCallHierarchyDeclaration` for the specific rules.
89 */
90function isValidCallHierarchyDeclaration(node: Node): node is CallHierarchyDeclaration {
91    return isSourceFile(node)
92        || isModuleDeclaration(node) && isIdentifier(node.name)
93        || isFunctionDeclaration(node)
94        || isClassDeclaration(node)
95        || isClassStaticBlockDeclaration(node)
96        || isMethodDeclaration(node)
97        || isMethodSignature(node)
98        || isGetAccessorDeclaration(node)
99        || isSetAccessorDeclaration(node)
100        || isNamedExpression(node)
101        || isConstNamedExpression(node);
102}
103
104/** Gets the node that can be used as a reference to a call hierarchy declaration. */
105function getCallHierarchyDeclarationReferenceNode(node: Exclude<CallHierarchyDeclaration, ClassStaticBlockDeclaration>) {
106    if (isSourceFile(node)) return node;
107    if (isNamedDeclaration(node)) return node.name;
108    if (isConstNamedExpression(node)) return node.parent.name;
109    return Debug.checkDefined(node.modifiers && find(node.modifiers, isDefaultModifier));
110}
111
112function isDefaultModifier(node: Node) {
113    return node.kind === SyntaxKind.DefaultKeyword;
114}
115
116/** Gets the symbol for a call hierarchy declaration. */
117function getSymbolOfCallHierarchyDeclaration(typeChecker: TypeChecker, node: Exclude<CallHierarchyDeclaration, ClassStaticBlockDeclaration>) {
118    const location = getCallHierarchyDeclarationReferenceNode(node);
119    return location && typeChecker.getSymbolAtLocation(location);
120}
121
122/** Gets the text and range for the name of a call hierarchy declaration. */
123function getCallHierarchyItemName(program: Program, node: CallHierarchyDeclaration): { text: string, pos: number, end: number } {
124    if (isSourceFile(node)) {
125        return { text: node.fileName, pos: 0, end: 0 };
126    }
127
128    if ((isFunctionDeclaration(node) || isClassDeclaration(node)) && !isNamedDeclaration(node)) {
129        const defaultModifier = node.modifiers && find(node.modifiers, isDefaultModifier);
130        if (defaultModifier) {
131            return { text: "default", pos: defaultModifier.getStart(), end: defaultModifier.getEnd() };
132        }
133    }
134
135    if (isClassStaticBlockDeclaration(node)) {
136        const sourceFile = node.getSourceFile();
137        const pos = skipTrivia(sourceFile.text, moveRangePastModifiers(node).pos);
138        const end = pos + 6; /* "static".length */
139        const typeChecker = program.getTypeChecker();
140        const symbol = typeChecker.getSymbolAtLocation(node.parent);
141        const prefix = symbol ? `${typeChecker.symbolToString(symbol, node.parent)} ` : "";
142        return { text: `${prefix}static {}`, pos, end };
143    }
144
145    const declName = isConstNamedExpression(node) ? node.parent.name :
146        Debug.checkDefined(getNameOfDeclaration(node), "Expected call hierarchy item to have a name");
147
148    let text =
149        isIdentifier(declName) ? idText(declName) :
150        isStringOrNumericLiteralLike(declName) ? declName.text :
151        isComputedPropertyName(declName) ?
152            isStringOrNumericLiteralLike(declName.expression) ? declName.expression.text :
153            undefined :
154        undefined;
155    if (text === undefined) {
156        const typeChecker = program.getTypeChecker();
157        const symbol = typeChecker.getSymbolAtLocation(declName);
158        if (symbol) {
159            text = typeChecker.symbolToString(symbol, node);
160        }
161    }
162    if (text === undefined) {
163        // get the text from printing the node on a single line without comments...
164        const printer = createPrinterWithRemoveCommentsOmitTrailingSemicolon();
165        text = usingSingleLineStringWriter(writer => printer.writeNode(EmitHint.Unspecified, node, node.getSourceFile(), writer));
166    }
167    return { text, pos: declName.getStart(), end: declName.getEnd() };
168}
169
170function getCallHierarchItemContainerName(node: CallHierarchyDeclaration): string | undefined {
171    if (isConstNamedExpression(node)) {
172        if (isModuleBlock(node.parent.parent.parent.parent) && isIdentifier(node.parent.parent.parent.parent.parent.name)) {
173            return node.parent.parent.parent.parent.parent.name.getText();
174        }
175        return;
176    }
177
178    switch (node.kind) {
179        case SyntaxKind.GetAccessor:
180        case SyntaxKind.SetAccessor:
181        case SyntaxKind.MethodDeclaration:
182            if (node.parent.kind === SyntaxKind.ObjectLiteralExpression) {
183                return getAssignedName(node.parent)?.getText();
184            }
185            return getNameOfDeclaration(node.parent)?.getText();
186        case SyntaxKind.FunctionDeclaration:
187        case SyntaxKind.ClassDeclaration:
188        case SyntaxKind.StructDeclaration:
189        case SyntaxKind.ModuleDeclaration:
190            if (isModuleBlock(node.parent) && isIdentifier(node.parent.parent.name)) {
191                return node.parent.parent.name.getText();
192            }
193    }
194}
195
196/** Finds the implementation of a function-like declaration, if one exists. */
197function findImplementation(typeChecker: TypeChecker, node: Extract<CallHierarchyDeclaration, FunctionLikeDeclaration>): Extract<CallHierarchyDeclaration, FunctionLikeDeclaration> | undefined;
198function findImplementation(typeChecker: TypeChecker, node: FunctionLikeDeclaration): FunctionLikeDeclaration | undefined;
199function findImplementation(typeChecker: TypeChecker, node: FunctionLikeDeclaration): FunctionLikeDeclaration | undefined {
200    if (node.body) {
201        return node;
202    }
203    if (isConstructorDeclaration(node)) {
204        return getFirstConstructorWithBody(node.parent);
205    }
206    if (isFunctionDeclaration(node) || isMethodDeclaration(node)) {
207        const symbol = getSymbolOfCallHierarchyDeclaration(typeChecker, node);
208        if (symbol && symbol.valueDeclaration && isFunctionLikeDeclaration(symbol.valueDeclaration) && symbol.valueDeclaration.body) {
209            return symbol.valueDeclaration;
210        }
211        return undefined;
212    }
213    return node;
214}
215
216function findAllInitialDeclarations(typeChecker: TypeChecker, node: Exclude<CallHierarchyDeclaration, ClassStaticBlockDeclaration>) {
217    const symbol = getSymbolOfCallHierarchyDeclaration(typeChecker, node);
218    let declarations: CallHierarchyDeclaration[] | undefined;
219    if (symbol && symbol.declarations) {
220        const indices = indicesOf(symbol.declarations);
221        const keys = map(symbol.declarations, decl => ({ file: decl.getSourceFile().fileName, pos: decl.pos }));
222        indices.sort((a, b) => compareStringsCaseSensitive(keys[a].file, keys[b].file) || keys[a].pos - keys[b].pos);
223        const sortedDeclarations = map(indices, i => symbol.declarations![i]);
224        let lastDecl: CallHierarchyDeclaration | undefined;
225        for (const decl of sortedDeclarations) {
226            if (isValidCallHierarchyDeclaration(decl)) {
227                if (!lastDecl || lastDecl.parent !== decl.parent || lastDecl.end !== decl.pos) {
228                    declarations = append(declarations, decl);
229                }
230                lastDecl = decl;
231            }
232        }
233    }
234    return declarations;
235}
236
237/** Find the implementation or the first declaration for a call hierarchy declaration. */
238function findImplementationOrAllInitialDeclarations(typeChecker: TypeChecker, node: CallHierarchyDeclaration): CallHierarchyDeclaration | CallHierarchyDeclaration[] {
239    if (isClassStaticBlockDeclaration(node)) {
240        return node;
241    }
242    if (isFunctionLikeDeclaration(node)) {
243        return findImplementation(typeChecker, node) ??
244            findAllInitialDeclarations(typeChecker, node) ??
245            node;
246    }
247    return findAllInitialDeclarations(typeChecker, node) ?? node;
248}
249
250/**
251 * Resolves the call hierarchy declaration for a node.
252 *
253 * @internal
254 */
255export function resolveCallHierarchyDeclaration(program: Program, location: Node): CallHierarchyDeclaration | CallHierarchyDeclaration[] | undefined {
256    // A call hierarchy item must refer to either a SourceFile, Module Declaration, Class Static Block, or something intrinsically callable that has a name:
257    // - Class Declarations
258    // - Class Expressions (with a name)
259    // - Function Declarations
260    // - Function Expressions (with a name or assigned to a const variable)
261    // - Arrow Functions (assigned to a const variable)
262    // - Constructors
263    // - Class `static {}` initializer blocks
264    // - Methods
265    // - Accessors
266    //
267    // If a call is contained in a non-named callable Node (function expression, arrow function, etc.), then
268    // its containing `CallHierarchyItem` is a containing function or SourceFile that matches the above list.
269
270    const typeChecker = program.getTypeChecker();
271    let followingSymbol = false;
272    while (true) {
273        if (isValidCallHierarchyDeclaration(location)) {
274            return findImplementationOrAllInitialDeclarations(typeChecker, location);
275        }
276        if (isPossibleCallHierarchyDeclaration(location)) {
277            const ancestor = findAncestor(location, isValidCallHierarchyDeclaration);
278            return ancestor && findImplementationOrAllInitialDeclarations(typeChecker, ancestor);
279        }
280        if (isDeclarationName(location)) {
281            if (isValidCallHierarchyDeclaration(location.parent)) {
282                return findImplementationOrAllInitialDeclarations(typeChecker, location.parent);
283            }
284            if (isPossibleCallHierarchyDeclaration(location.parent)) {
285                const ancestor = findAncestor(location.parent, isValidCallHierarchyDeclaration);
286                return ancestor && findImplementationOrAllInitialDeclarations(typeChecker, ancestor);
287            }
288            if (isVariableDeclaration(location.parent) && location.parent.initializer && isConstNamedExpression(location.parent.initializer)) {
289                return location.parent.initializer;
290            }
291            return undefined;
292        }
293        if (isConstructorDeclaration(location)) {
294            if (isValidCallHierarchyDeclaration(location.parent)) {
295                return location.parent;
296            }
297            return undefined;
298        }
299        if (location.kind === SyntaxKind.StaticKeyword && isClassStaticBlockDeclaration(location.parent)) {
300            location = location.parent;
301            continue;
302        }
303        // #39453
304        if (isVariableDeclaration(location) && location.initializer && isConstNamedExpression(location.initializer)) {
305            return location.initializer;
306        }
307        if (!followingSymbol) {
308            let symbol = typeChecker.getSymbolAtLocation(location);
309            if (symbol) {
310                if (symbol.flags & SymbolFlags.Alias) {
311                    symbol = typeChecker.getAliasedSymbol(symbol);
312                }
313                if (symbol.valueDeclaration) {
314                    followingSymbol = true;
315                    location = symbol.valueDeclaration;
316                    continue;
317                }
318            }
319        }
320        return undefined;
321    }
322}
323
324/**
325 * Creates a `CallHierarchyItem` for a call hierarchy declaration.
326 *
327 * @internal
328 */
329export function createCallHierarchyItem(program: Program, node: CallHierarchyDeclaration): CallHierarchyItem {
330    const sourceFile = node.getSourceFile();
331    const name = getCallHierarchyItemName(program, node);
332    const containerName = getCallHierarchItemContainerName(node);
333    const kind = getNodeKind(node);
334    const kindModifiers = getNodeModifiers(node);
335    const span = createTextSpanFromBounds(skipTrivia(sourceFile.text, node.getFullStart(), /*stopAfterLineBreak*/ false, /*stopAtComments*/ true), node.getEnd());
336    const selectionSpan = createTextSpanFromBounds(name.pos, name.end);
337    return { file: sourceFile.fileName, kind, kindModifiers, name: name.text, containerName, span, selectionSpan };
338}
339
340function isDefined<T>(x: T): x is NonNullable<T> {
341    return x !== undefined;
342}
343
344interface CallSite {
345    declaration: CallHierarchyDeclaration;
346    range: TextRange;
347}
348
349function convertEntryToCallSite(entry: FindAllReferences.Entry): CallSite | undefined {
350    if (entry.kind === FindAllReferences.EntryKind.Node) {
351        const { node } = entry;
352        if (isCallOrNewExpressionTarget(node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true)
353            || isTaggedTemplateTag(node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true)
354            || isDecoratorTarget(node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true)
355            || isJsxOpeningLikeElementTagName(node, /*includeElementAccess*/ true, /*skipPastOuterExpressions*/ true)
356            || isRightSideOfPropertyAccess(node)
357            || isArgumentExpressionOfElementAccess(node)) {
358            const sourceFile = node.getSourceFile();
359            const ancestor = findAncestor(node, isValidCallHierarchyDeclaration) || sourceFile;
360            return { declaration: ancestor, range: createTextRangeFromNode(node, sourceFile) };
361        }
362    }
363}
364
365function getCallSiteGroupKey(entry: CallSite) {
366    return getNodeId(entry.declaration);
367}
368
369function createCallHierarchyIncomingCall(from: CallHierarchyItem, fromSpans: TextSpan[]): CallHierarchyIncomingCall {
370    return { from, fromSpans };
371}
372
373function convertCallSiteGroupToIncomingCall(program: Program, entries: readonly CallSite[]) {
374    return createCallHierarchyIncomingCall(createCallHierarchyItem(program, entries[0].declaration), map(entries, entry => createTextSpanFromRange(entry.range)));
375}
376
377/**
378 * Gets the call sites that call into the provided call hierarchy declaration.
379 *
380 * @internal
381 */
382export function getIncomingCalls(program: Program, declaration: CallHierarchyDeclaration, cancellationToken: CancellationToken): CallHierarchyIncomingCall[] {
383    // Source files and modules have no incoming calls.
384    if (isSourceFile(declaration) || isModuleDeclaration(declaration) || isClassStaticBlockDeclaration(declaration)) {
385        return [];
386    }
387    const location = getCallHierarchyDeclarationReferenceNode(declaration);
388    const calls = filter(FindAllReferences.findReferenceOrRenameEntries(program, cancellationToken, program.getSourceFiles(), location, /*position*/ 0, { use: FindAllReferences.FindReferencesUse.References }, convertEntryToCallSite), isDefined);
389    return calls ? group(calls, getCallSiteGroupKey, entries => convertCallSiteGroupToIncomingCall(program, entries)) : [];
390}
391
392function createCallSiteCollector(program: Program, callSites: CallSite[]): (node: Node | undefined) => void {
393    function recordCallSite(node: CallExpression | NewExpression | TaggedTemplateExpression | PropertyAccessExpression | ElementAccessExpression | Decorator | JsxOpeningLikeElement | ClassStaticBlockDeclaration) {
394        const target =
395            isTaggedTemplateExpression(node) ? node.tag :
396            isJsxOpeningLikeElement(node) ? node.tagName :
397            isAccessExpression(node) ? node :
398            isClassStaticBlockDeclaration(node) ? node :
399            node.expression;
400        const declaration = resolveCallHierarchyDeclaration(program, target);
401        if (declaration) {
402            const range = createTextRangeFromNode(target, node.getSourceFile());
403            if (isArray(declaration)) {
404                for (const decl of declaration) {
405                    callSites.push({ declaration: decl, range });
406                }
407            }
408            else {
409                callSites.push({ declaration, range });
410            }
411        }
412    }
413
414    function collect(node: Node | undefined) {
415        if (!node) return;
416        if (node.flags & NodeFlags.Ambient) {
417            // do not descend into ambient nodes.
418            return;
419        }
420
421        if (isValidCallHierarchyDeclaration(node)) {
422            // do not descend into other call site declarations, other than class member names
423            if (isClassLike(node)) {
424                for (const member of node.members) {
425                    if (member.name && isComputedPropertyName(member.name)) {
426                        collect(member.name.expression);
427                    }
428                }
429            }
430            return;
431        }
432
433        switch (node.kind) {
434            case SyntaxKind.Identifier:
435            case SyntaxKind.ImportEqualsDeclaration:
436            case SyntaxKind.ImportDeclaration:
437            case SyntaxKind.ExportDeclaration:
438            case SyntaxKind.InterfaceDeclaration:
439            case SyntaxKind.TypeAliasDeclaration:
440                // do not descend into nodes that cannot contain callable nodes
441                return;
442            case SyntaxKind.ClassStaticBlockDeclaration:
443                recordCallSite(node as ClassStaticBlockDeclaration);
444                return;
445            case SyntaxKind.TypeAssertionExpression:
446            case SyntaxKind.AsExpression:
447                // do not descend into the type side of an assertion
448                collect((node as TypeAssertion | AsExpression).expression);
449                return;
450            case SyntaxKind.VariableDeclaration:
451            case SyntaxKind.Parameter:
452                // do not descend into the type of a variable or parameter declaration
453                collect((node as VariableDeclaration | ParameterDeclaration).name);
454                collect((node as VariableDeclaration | ParameterDeclaration).initializer);
455                return;
456            case SyntaxKind.CallExpression:
457                // do not descend into the type arguments of a call expression
458                recordCallSite(node as CallExpression);
459                collect((node as CallExpression).expression);
460                forEach((node as CallExpression).arguments, collect);
461                return;
462            case SyntaxKind.NewExpression:
463                // do not descend into the type arguments of a new expression
464                recordCallSite(node as NewExpression);
465                collect((node as NewExpression).expression);
466                forEach((node as NewExpression).arguments, collect);
467                return;
468            case SyntaxKind.TaggedTemplateExpression:
469                // do not descend into the type arguments of a tagged template expression
470                recordCallSite(node as TaggedTemplateExpression);
471                collect((node as TaggedTemplateExpression).tag);
472                collect((node as TaggedTemplateExpression).template);
473                return;
474            case SyntaxKind.JsxOpeningElement:
475            case SyntaxKind.JsxSelfClosingElement:
476                // do not descend into the type arguments of a JsxOpeningLikeElement
477                recordCallSite(node as JsxOpeningLikeElement);
478                collect((node as JsxOpeningLikeElement).tagName);
479                collect((node as JsxOpeningLikeElement).attributes);
480                return;
481            case SyntaxKind.Decorator:
482                recordCallSite(node as Decorator);
483                collect((node as Decorator).expression);
484                return;
485            case SyntaxKind.PropertyAccessExpression:
486            case SyntaxKind.ElementAccessExpression:
487                recordCallSite(node as AccessExpression);
488                forEachChild(node, collect);
489                break;
490            case SyntaxKind.SatisfiesExpression:
491                // do not descend into the type side of an assertion
492                collect((node as SatisfiesExpression).expression);
493                return;
494        }
495
496        if (isPartOfTypeNode(node)) {
497            // do not descend into types
498            return;
499        }
500
501        forEachChild(node, collect);
502    }
503    return collect;
504}
505
506function collectCallSitesOfSourceFile(node: SourceFile, collect: (node: Node | undefined) => void) {
507    forEach(node.statements, collect);
508}
509
510function collectCallSitesOfModuleDeclaration(node: ModuleDeclaration, collect: (node: Node | undefined) => void) {
511    if (!hasSyntacticModifier(node, ModifierFlags.Ambient) && node.body && isModuleBlock(node.body)) {
512        forEach(node.body.statements, collect);
513    }
514}
515
516function collectCallSitesOfFunctionLikeDeclaration(typeChecker: TypeChecker, node: FunctionLikeDeclaration, collect: (node: Node | undefined) => void) {
517    const implementation = findImplementation(typeChecker, node);
518    if (implementation) {
519        forEach(implementation.parameters, collect);
520        collect(implementation.body);
521    }
522}
523
524function collectCallSitesOfClassStaticBlockDeclaration(node: ClassStaticBlockDeclaration, collect: (node: Node | undefined) => void) {
525    collect(node.body);
526}
527
528function collectCallSitesOfClassLikeDeclaration(node: ClassLikeDeclaration, collect: (node: Node | undefined) => void) {
529    forEach(node.modifiers, collect);
530    const heritage = getClassExtendsHeritageElement(node);
531    if (heritage) {
532        collect(heritage.expression);
533    }
534    for (const member of node.members) {
535        if (canHaveModifiers(member)) {
536            forEach(member.modifiers, collect);
537        }
538        if (isPropertyDeclaration(member)) {
539            collect(member.initializer);
540        }
541        else if (isConstructorDeclaration(member) && member.body) {
542            forEach(member.parameters, collect);
543            collect(member.body);
544        }
545        else if (isClassStaticBlockDeclaration(member)) {
546            collect(member);
547        }
548    }
549}
550
551function collectCallSites(program: Program, node: CallHierarchyDeclaration) {
552    const callSites: CallSite[] = [];
553    const collect = createCallSiteCollector(program, callSites);
554    switch (node.kind) {
555        case SyntaxKind.SourceFile:
556            collectCallSitesOfSourceFile(node, collect);
557            break;
558        case SyntaxKind.ModuleDeclaration:
559            collectCallSitesOfModuleDeclaration(node, collect);
560            break;
561        case SyntaxKind.FunctionDeclaration:
562        case SyntaxKind.FunctionExpression:
563        case SyntaxKind.ArrowFunction:
564        case SyntaxKind.MethodDeclaration:
565        case SyntaxKind.GetAccessor:
566        case SyntaxKind.SetAccessor:
567            collectCallSitesOfFunctionLikeDeclaration(program.getTypeChecker(), node, collect);
568            break;
569        case SyntaxKind.ClassDeclaration:
570        case SyntaxKind.ClassExpression:
571        case SyntaxKind.StructDeclaration:
572            collectCallSitesOfClassLikeDeclaration(node, collect);
573            break;
574        case SyntaxKind.ClassStaticBlockDeclaration:
575            collectCallSitesOfClassStaticBlockDeclaration(node, collect);
576            break;
577        default:
578            Debug.assertNever(node);
579    }
580    return callSites;
581}
582
583function createCallHierarchyOutgoingCall(to: CallHierarchyItem, fromSpans: TextSpan[]): CallHierarchyOutgoingCall {
584    return { to, fromSpans };
585}
586
587function convertCallSiteGroupToOutgoingCall(program: Program, entries: readonly CallSite[]) {
588    return createCallHierarchyOutgoingCall(createCallHierarchyItem(program, entries[0].declaration), map(entries, entry => createTextSpanFromRange(entry.range)));
589}
590
591/**
592 * Gets the call sites that call out of the provided call hierarchy declaration.
593 *
594 * @internal
595 */
596export function getOutgoingCalls(program: Program, declaration: CallHierarchyDeclaration): CallHierarchyOutgoingCall[] {
597    if (declaration.flags & NodeFlags.Ambient || isMethodSignature(declaration)) {
598        return [];
599    }
600    return group(collectCallSites(program, declaration), getCallSiteGroupKey, entries => convertCallSiteGroupToOutgoingCall(program, entries));
601}
602