namespace ts . CallHierarchy { export type NamedExpression = ClassExpression & { name : Identifier ; } | FunctionExpression & { name : Identifier ; } ; /** Indictates whether a node is named function or class expression. */ function isNamedExpression ( node ) { return ( isFunctionExpression ( node ) || isClassExpression ( node ) ) && isNamedDeclaration ( node ) ; } export type ConstNamedExpression = ClassExpression & { name : undefined ; parent : VariableDeclaration & { name : Identifier ; } ; } | FunctionExpression & { name : undefined ; parent : VariableDeclaration & { name : Identifier ; } ; } | ArrowFunction & { name : undefined ; parent : VariableDeclaration & { name : Identifier ; } ; } ; /** Indicates whether a node is a function, arrow, or class expression assigned to a constant variable. */ function isConstNamedExpression ( node ) { return ( isFunctionExpression ( node ) || isArrowFunction ( node ) || isClassExpression ( node ) ) && isVariableDeclaration ( node . parent ) && node === node . parent . initializer && isIdentifier ( node . parent . name ) && ! ! ( getCombinedNodeFlags ( node . parent ) & NodeFlags . Const ) ; } export type CallHierarchyDeclaration = SourceFile | ModuleDeclaration & { name : Identifier ; } | FunctionDeclaration | ClassDeclaration | ClassStaticBlockDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | NamedExpression | ConstNamedExpression ; /**
     * Indicates whether a node could possibly be a call hierarchy declaration.
     *
     * See `resolveCallHierarchyDeclaration` for the specific rules.
     */ function isPossibleCallHierarchyDeclaration ( node ) { return isSourceFile ( node ) || isModuleDeclaration ( node ) || isFunctionDeclaration ( node ) || isFunctionExpression ( node ) || isClassDeclaration ( node ) || isClassExpression ( node ) || isClassStaticBlockDeclaration ( node ) || isMethodDeclaration ( node ) || isMethodSignature ( node ) || isGetAccessorDeclaration ( node ) || isSetAccessorDeclaration ( node ) ; } /**
     * Indicates whether a node is a valid a call hierarchy declaration.
     *
     * See `resolveCallHierarchyDeclaration` for the specific rules.
     */ function isValidCallHierarchyDeclaration ( node ) { return isSourceFile ( node ) || isModuleDeclaration ( node ) && isIdentifier ( node . name ) || isFunctionDeclaration ( node ) || isClassDeclaration ( node ) || isClassStaticBlockDeclaration ( node ) || isMethodDeclaration ( node ) || isMethodSignature ( node ) || isGetAccessorDeclaration ( node ) || isSetAccessorDeclaration ( node ) || isNamedExpression ( node ) || isConstNamedExpression ( node ) ; } /** Gets the node that can be used as a reference to a call hierarchy declaration. */ function getCallHierarchyDeclarationReferenceNode ( node ) { if ( isSourceFile ( node ) ) return node ; if ( isNamedDeclaration ( node ) ) return node . name ; if ( isConstNamedExpression ( node ) ) return node . parent . name ; return Debug . checkDefined ( node . modifiers && find ( node . modifiers , isDefaultModifier ) ) ; } function isDefaultModifier ( node ) { return node . kind === SyntaxKind . DefaultKeyword ; } /** Gets the symbol for a call hierarchy declaration. */ function getSymbolOfCallHierarchyDeclaration ( typeChecker , node ) { const location = getCallHierarchyDeclarationReferenceNode ( node ) ; return location && typeChecker . getSymbolAtLocation ( location ) ; } /** Gets the text and range for the name of a call hierarchy declaration. */ function getCallHierarchyItemName ( program , node ) { if ( isSourceFile ( node ) ) { return { text : node . fileName , pos : 0 , end : 0 } ; } if ( ( isFunctionDeclaration ( node ) || isClassDeclaration ( node ) ) && ! isNamedDeclaration ( node ) ) { const defaultModifier = node . modifiers && find ( node . modifiers , isDefaultModifier ) ; if ( defaultModifier ) { return { text : "default" , pos : defaultModifier . getStart ( ) , end : defaultModifier . getEnd ( ) } ; } } if ( isClassStaticBlockDeclaration ( node ) ) { const sourceFile = node . getSourceFile ( ) ; const pos = skipTrivia ( sourceFile . text , moveRangePastModifiers ( node ) . pos ) ; const end = pos + 6 ; const typeChecker = program . getTypeChecker ( ) ; const symbol = typeChecker . getSymbolAtLocation ( node . parent ) ; const prefix = symbol ? `${ typeChecker . symbolToString ( symbol , node . parent ) } ` : "" ; return { text : `${ prefix }static {}` , pos , end } ; } const declName = isConstNamedExpression ( node ) ? node . parent . name : Debug . checkDefined ( getNameOfDeclaration ( node ) , "Expected call hierarchy item to have a name" ) ; let text = isIdentifier ( declName ) ? idText ( declName ) : isStringOrNumericLiteralLike ( declName ) ? declName . text : isComputedPropertyName ( declName ) ? isStringOrNumericLiteralLike ( declName . expression ) ? declName . expression . text : undefined : undefined ; if ( text === undefined ) { const typeChecker = program . getTypeChecker ( ) ; const symbol = typeChecker . getSymbolAtLocation ( declName ) ; if ( symbol ) { text = typeChecker . symbolToString ( symbol , node ) ; } } if ( text === undefined ) { const printer = createPrinter ( { removeComments : true , omitTrailingSemicolon : true } ) ; text = usingSingleLineStringWriter ( writer => printer . writeNode ( EmitHint . Unspecified , node , node . getSourceFile ( ) , writer ) ) ; } return { text , pos : declName . getStart ( ) , end : declName . getEnd ( ) } ; } function getCallHierarchItemContainerName ( node ) { if ( isConstNamedExpression ( node ) ) { if ( isModuleBlock ( node . parent . parent . parent . parent ) && isIdentifier ( node . parent . parent . parent . parent . parent . name ) ) { return node . parent . parent . parent . parent . parent . name . getText ( ) ; } return ; } switch ( node . kind ) { case SyntaxKind . GetAccessor : case SyntaxKind . SetAccessor : case SyntaxKind . MethodDeclaration : if ( node . parent . kind === SyntaxKind . ObjectLiteralExpression ) { return getAssignedName ( node . parent ) ?. getText ( ) ; } return getNameOfDeclaration ( node . parent ) ?. getText ( ) ; case SyntaxKind . FunctionDeclaration : case SyntaxKind . ClassDeclaration : case SyntaxKind . ModuleDeclaration : if ( isModuleBlock ( node . parent ) && isIdentifier ( node . parent . parent . name ) ) { return node . parent . parent . name . getText ( ) ; } } } /** Finds the implementation of a function-like declaration, if one exists. */ function findImplementation ( typeChecker , node ) ; function findImplementation ( typeChecker , node ) ; function findImplementation ( typeChecker , node ) { if ( node . body ) { return node ; } if ( isConstructorDeclaration ( node ) ) { return getFirstConstructorWithBody ( node . parent ) ; } if ( isFunctionDeclaration ( node ) || isMethodDeclaration ( node ) ) { const symbol = getSymbolOfCallHierarchyDeclaration ( typeChecker , node ) ; if ( symbol && symbol . valueDeclaration && isFunctionLikeDeclaration ( symbol . valueDeclaration ) && symbol . valueDeclaration . body ) { return symbol . valueDeclaration ; } return undefined ; } return node ; } function findAllInitialDeclarations ( typeChecker , node ) { const symbol = getSymbolOfCallHierarchyDeclaration ( typeChecker , node ) ; let declarations ; if ( symbol && symbol . declarations ) { const indices = indicesOf ( symbol . declarations ) ; const keys = map ( symbol . declarations , decl => ( { file : decl . getSourceFile ( ) . fileName , pos : decl . pos } ) ) ; indices . sort ( ( a , b ) => compareStringsCaseSensitive ( keys [ a ] . file , keys [ b ] . file ) || keys [ a ] . pos - keys [ b ] . pos ) ; const sortedDeclarations = map ( indices , i => symbol . declarations ! [ i ] ) ; let lastDecl ; for ( const decl of sortedDeclarations ) { if ( isValidCallHierarchyDeclaration ( decl ) ) { if ( ! lastDecl || lastDecl . parent !== decl . parent || lastDecl . end !== decl . pos ) { declarations = append ( declarations , decl ) ; } lastDecl = decl ; } } } return declarations ; } /** Find the implementation or the first declaration for a call hierarchy declaration. */ function findImplementationOrAllInitialDeclarations ( typeChecker , node ) { if ( isClassStaticBlockDeclaration ( node ) ) { return node ; } if ( isFunctionLikeDeclaration ( node ) ) { return findImplementation ( typeChecker , node ) ?? findAllInitialDeclarations ( typeChecker , node ) ?? node ; } return findAllInitialDeclarations ( typeChecker , node ) ?? node ; } /** Resolves the call hierarchy declaration for a node. */ export function resolveCallHierarchyDeclaration ( program , location ) { const typeChecker = program . getTypeChecker ( ) ; let followingSymbol = false ; while ( true ) { if ( isValidCallHierarchyDeclaration ( location ) ) { return findImplementationOrAllInitialDeclarations ( typeChecker , location ) ; } if ( isPossibleCallHierarchyDeclaration ( location ) ) { const ancestor = findAncestor ( location , isValidCallHierarchyDeclaration ) ; return ancestor && findImplementationOrAllInitialDeclarations ( typeChecker , ancestor ) ; } if ( isDeclarationName ( location ) ) { if ( isValidCallHierarchyDeclaration ( location . parent ) ) { return findImplementationOrAllInitialDeclarations ( typeChecker , location . parent ) ; } if ( isPossibleCallHierarchyDeclaration ( location . parent ) ) { const ancestor = findAncestor ( location . parent , isValidCallHierarchyDeclaration ) ; return ancestor && findImplementationOrAllInitialDeclarations ( typeChecker , ancestor ) ; } if ( isVariableDeclaration ( location . parent ) && location . parent . initializer && isConstNamedExpression ( location . parent . initializer ) ) { return location . parent . initializer ; } return undefined ; } if ( isConstructorDeclaration ( location ) ) { if ( isValidCallHierarchyDeclaration ( location . parent ) ) { return location . parent ; } return undefined ; } if ( location . kind === SyntaxKind . StaticKeyword && isClassStaticBlockDeclaration ( location . parent ) ) { location = location . parent ; continue ; } if ( isVariableDeclaration ( location ) && location . initializer && isConstNamedExpression ( location . initializer ) ) { return location . initializer ; } if ( ! followingSymbol ) { let symbol = typeChecker . getSymbolAtLocation ( location ) ; if ( symbol ) { if ( symbol . flags & SymbolFlags . Alias ) { symbol = typeChecker . getAliasedSymbol ( symbol ) ; } if ( symbol . valueDeclaration ) { followingSymbol = true ; location = symbol . valueDeclaration ; continue ; } } } return undefined ; } } /** Creates a `CallHierarchyItem` for a call hierarchy declaration. */ export function createCallHierarchyItem ( program , node ) { const sourceFile = node . getSourceFile ( ) ; const name = getCallHierarchyItemName ( program , node ) ; const containerName = getCallHierarchItemContainerName ( node ) ; const kind = getNodeKind ( node ) ; const kindModifiers = getNodeModifiers ( node ) ; const span = createTextSpanFromBounds ( skipTrivia ( sourceFile . text , node . getFullStart ( ) , false , true ) , node . getEnd ( ) ) ; const selectionSpan = createTextSpanFromBounds ( name . pos , name . end ) ; return { file : sourceFile . fileName , kind , kindModifiers , name : name . text , containerName , span , selectionSpan } ; } function isDefined < T > ( x ) { return x !== undefined ; } interface CallSite { declaration : CallHierarchyDeclaration ; range : TextRange ; } function convertEntryToCallSite ( entry ) { if ( entry . kind === FindAllReferences . EntryKind . Node ) { const { node } = entry ; if ( isCallOrNewExpressionTarget ( node , true , true ) || isTaggedTemplateTag ( node , true , true ) || isDecoratorTarget ( node , true , true ) || isJsxOpeningLikeElementTagName ( node , true , true ) || isRightSideOfPropertyAccess ( node ) || isArgumentExpressionOfElementAccess ( node ) ) { const sourceFile = node . getSourceFile ( ) ; const ancestor = findAncestor ( node , isValidCallHierarchyDeclaration ) || sourceFile ; return { declaration : ancestor , range : createTextRangeFromNode ( node , sourceFile ) } ; } } } function getCallSiteGroupKey ( entry ) { return getNodeId ( entry . declaration ) ; } function createCallHierarchyIncomingCall ( from , fromSpans ) { return { from , fromSpans } ; } function convertCallSiteGroupToIncomingCall ( program , entries ) { return createCallHierarchyIncomingCall ( createCallHierarchyItem ( program , entries [ 0 ] . declaration ) , map ( entries , entry => createTextSpanFromRange ( entry . range ) ) ) ; } /** Gets the call sites that call into the provided call hierarchy declaration. */ export function getIncomingCalls ( program , declaration , cancellationToken ) { if ( isSourceFile ( declaration ) || isModuleDeclaration ( declaration ) || isClassStaticBlockDeclaration ( declaration ) ) { return [ ] ; } const location = getCallHierarchyDeclarationReferenceNode ( declaration ) ; const calls = filter ( FindAllReferences . findReferenceOrRenameEntries ( program , cancellationToken , program . getSourceFiles ( ) , location , 0 , { use : FindAllReferences . FindReferencesUse . References } , convertEntryToCallSite ) , isDefined ) ; return calls ? group ( calls , getCallSiteGroupKey , entries => convertCallSiteGroupToIncomingCall ( program , entries ) ) : [ ] ; } function createCallSiteCollector ( program , callSites ) { function recordCallSite ( node ) { const target = isTaggedTemplateExpression ( node ) ? node . tag : isJsxOpeningLikeElement ( node ) ? node . tagName : isAccessExpression ( node ) ? node : isClassStaticBlockDeclaration ( node ) ? node : node . expression ; const declaration = resolveCallHierarchyDeclaration ( program , target ) ; if ( declaration ) { const range = createTextRangeFromNode ( target , node . getSourceFile ( ) ) ; if ( isArray ( declaration ) ) { for ( const decl of declaration ) { callSites . push ( { declaration : decl , range } ) ; } } else { callSites . push ( { declaration , range } ) ; } } } function collect ( node ) { if ( ! node ) return ; if ( node . flags & NodeFlags . Ambient ) { return ; } if ( isValidCallHierarchyDeclaration ( node ) ) { if ( isClassLike ( node ) ) { for ( const member of node . members ) { if ( member . name && isComputedPropertyName ( member . name ) ) { collect ( member . name . expression ) ; } } } return ; } switch ( node . kind ) { case SyntaxKind . Identifier : case SyntaxKind . ImportEqualsDeclaration : case SyntaxKind . ImportDeclaration : case SyntaxKind . ExportDeclaration : case SyntaxKind . InterfaceDeclaration : case SyntaxKind . TypeAliasDeclaration : return ; case SyntaxKind . ClassStaticBlockDeclaration : recordCallSite ( node as ClassStaticBlockDeclaration ) ; return ; case SyntaxKind . TypeAssertionExpression : case SyntaxKind . AsExpression : collect ( ( node as TypeAssertion | AsExpression ) . expression ) ; return ; case SyntaxKind . VariableDeclaration : case SyntaxKind . Parameter : collect ( ( node as VariableDeclaration | ParameterDeclaration ) . name ) ; collect ( ( node as VariableDeclaration | ParameterDeclaration ) . initializer ) ; return ; case SyntaxKind . CallExpression : recordCallSite ( node as CallExpression ) ; collect ( ( node as CallExpression ) . expression ) ; forEach ( ( node as CallExpression ) . arguments , collect ) ; return ; case SyntaxKind . NewExpression : recordCallSite ( node as NewExpression ) ; collect ( ( node as NewExpression ) . expression ) ; forEach ( ( node as NewExpression ) . arguments , collect ) ; return ; case SyntaxKind . TaggedTemplateExpression : recordCallSite ( node as TaggedTemplateExpression ) ; collect ( ( node as TaggedTemplateExpression ) . tag ) ; collect ( ( node as TaggedTemplateExpression ) . template ) ; return ; case SyntaxKind . JsxOpeningElement : case SyntaxKind . JsxSelfClosingElement : recordCallSite ( node as JsxOpeningLikeElement ) ; collect ( ( node as JsxOpeningLikeElement ) . tagName ) ; collect ( ( node as JsxOpeningLikeElement ) . attributes ) ; return ; case SyntaxKind . Decorator : recordCallSite ( node as Decorator ) ; collect ( ( node as Decorator ) . expression ) ; return ; case SyntaxKind . PropertyAccessExpression : case SyntaxKind . ElementAccessExpression : recordCallSite ( node as AccessExpression ) ; forEachChild ( node , collect ) ; break ; } if ( isPartOfTypeNode ( node ) ) { return ; } forEachChild ( node , collect ) ; } return collect ; } function collectCallSitesOfSourceFile ( node , collect ) { forEach ( node . statements , collect ) ; } function collectCallSitesOfModuleDeclaration ( node , collect ) { if ( ! hasSyntacticModifier ( node , ModifierFlags . Ambient ) && node . body && isModuleBlock ( node . body ) ) { forEach ( node . body . statements , collect ) ; } } function collectCallSitesOfFunctionLikeDeclaration ( typeChecker , node , collect ) { const implementation = findImplementation ( typeChecker , node ) ; if ( implementation ) { forEach ( implementation . parameters , collect ) ; collect ( implementation . body ) ; } } function collectCallSitesOfClassStaticBlockDeclaration ( node , collect ) { collect ( node . body ) ; } function collectCallSitesOfClassLikeDeclaration ( node , collect ) { forEach ( node . decorators , collect ) ; const heritage = getClassExtendsHeritageElement ( node ) ; if ( heritage ) { collect ( heritage . expression ) ; } for ( const member of node . members ) { forEach ( member . decorators , collect ) ; if ( isPropertyDeclaration ( member ) ) { collect ( member . initializer ) ; } else if ( isConstructorDeclaration ( member ) && member . body ) { forEach ( member . parameters , collect ) ; collect ( member . body ) ; } else if ( isClassStaticBlockDeclaration ( member ) ) { collect ( member ) ; } } } function collectCallSites ( program , node ) { const callSites = [ ] ; const collect = createCallSiteCollector ( program , callSites ) ; switch ( node . kind ) { case SyntaxKind . SourceFile : collectCallSitesOfSourceFile ( node , collect ) ; break ; case SyntaxKind . ModuleDeclaration : collectCallSitesOfModuleDeclaration ( node , collect ) ; break ; case SyntaxKind . FunctionDeclaration : case SyntaxKind . FunctionExpression : case SyntaxKind . ArrowFunction : case SyntaxKind . MethodDeclaration : case SyntaxKind . GetAccessor : case SyntaxKind . SetAccessor : collectCallSitesOfFunctionLikeDeclaration ( program . getTypeChecker ( ) , node , collect ) ; break ; case SyntaxKind . ClassDeclaration : case SyntaxKind . ClassExpression : collectCallSitesOfClassLikeDeclaration ( node , collect ) ; break ; case SyntaxKind . ClassStaticBlockDeclaration : collectCallSitesOfClassStaticBlockDeclaration ( node , collect ) ; break ; default : Debug . assertNever ( node ) ; } return callSites ; } function createCallHierarchyOutgoingCall ( to , fromSpans ) { return { to , fromSpans } ; } function convertCallSiteGroupToOutgoingCall ( program , entries ) { return createCallHierarchyOutgoingCall ( createCallHierarchyItem ( program , entries [ 0 ] . declaration ) , map ( entries , entry => createTextSpanFromRange ( entry . range ) ) ) ; } /** Gets the call sites that call out of the provided call hierarchy declaration. */ export function getOutgoingCalls ( program , declaration ) { if ( declaration . flags & NodeFlags . Ambient || isMethodSignature ( declaration ) ) { return [ ] ; } return group ( collectCallSites ( program , declaration ) , getCallSiteGroupKey , entries => convertCallSiteGroupToOutgoingCall ( program , entries ) ) ; } }