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