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