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