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