Find references of a module by filename (#41805)

* Naive implementation enough to build and write a test

* Add simple test

* Add project references test

* Add deduplication test, accept baselines

* Add test for referencing a script (doesn’t do anything)

* Update API baselines

* Use refFileMap for non-module references

* Fix find-all-refs on module specifier

* Remove unused util

* Don’t store text range on ts.RefFile

* Ensure string literal could itself be a file reference

* Remove unused utilities

* Improve baseline format

* Preserve old behavior of falling back to string literal references

* Update baselines from master

* Fix old RefFileMap code after merge

* Add test for additional response info

* Undo test change
This commit is contained in:
Andrew Branch
2020-12-11 12:37:02 -08:00
committed by GitHub
parent 1c1cd9b08d
commit 9dfbf07d8a
36 changed files with 810 additions and 45 deletions
+44 -1
View File
@@ -622,7 +622,21 @@ namespace ts.FindAllReferences {
// Could not find a symbol e.g. unknown identifier
if (!symbol) {
// String literal might be a property (and thus have a symbol), so do this here rather than in getReferencedSymbolsSpecial.
return !options.implementations && isStringLiteralLike(node) ? getReferencesForStringLiteral(node, sourceFiles, checker, cancellationToken) : undefined;
if (!options.implementations && isStringLiteralLike(node)) {
if (isRequireCall(node.parent, /*requireStringLiteralLikeArgument*/ true) || isExternalModuleReference(node.parent) || isImportDeclaration(node.parent) || isImportCall(node.parent)) {
const fileIncludeReasons = program.getFileIncludeReasons();
const referencedFileName = node.getSourceFile().resolvedModules?.get(node.text)?.resolvedFileName;
const referencedFile = referencedFileName ? program.getSourceFile(referencedFileName) : undefined;
if (referencedFile) {
return [{ definition: { type: DefinitionKind.String, node }, references: getReferencesForNonModule(referencedFile, fileIncludeReasons, program) || emptyArray }];
}
// Fall through to string literal references. This is not very likely to return
// anything useful, but I guess it's better than nothing, and there's an existing
// test that expects this to happen (fourslash/cases/untypedModuleImport.ts).
}
return getReferencesForStringLiteral(node, sourceFiles, checker, cancellationToken);
}
return undefined;
}
if (symbol.escapedName === InternalSymbolName.ExportEquals) {
@@ -642,6 +656,35 @@ namespace ts.FindAllReferences {
return mergeReferences(program, moduleReferences, references, moduleReferencesOfExportTarget);
}
export function getReferencesForFileName(fileName: string, program: Program, sourceFiles: readonly SourceFile[], sourceFilesSet: ReadonlySet<string> = new Set(sourceFiles.map(f => f.fileName))): readonly Entry[] {
const moduleSymbol = program.getSourceFile(fileName)?.symbol;
if (moduleSymbol) {
return getReferencedSymbolsForModule(program, moduleSymbol, /*excludeImportTypeOfExportEquals*/ false, sourceFiles, sourceFilesSet)[0]?.references || emptyArray;
}
const fileIncludeReasons = program.getFileIncludeReasons();
const referencedFile = program.getSourceFile(fileName);
return referencedFile && fileIncludeReasons && getReferencesForNonModule(referencedFile, fileIncludeReasons, program) || emptyArray;
}
function getReferencesForNonModule(referencedFile: SourceFile, refFileMap: MultiMap<Path, FileIncludeReason>, program: Program): readonly SpanEntry[] | undefined {
let entries: SpanEntry[] | undefined;
const references = refFileMap.get(referencedFile.path) || emptyArray;
for (const ref of references) {
if (isReferencedFile(ref)) {
const referencingFile = program.getSourceFileByPath(ref.file)!;
const location = getReferencedFileLocation(program.getSourceFileByPath, ref);
if (isReferenceFileLocation(location)) {
entries = append(entries, {
kind: EntryKind.Span,
fileName: referencingFile.fileName,
textSpan: createTextSpanFromRange(location)
});
}
}
}
return entries;
}
function getMergedAliasedSymbolOfNamespaceExportDeclaration(node: Node, symbol: Symbol, checker: TypeChecker) {
if (node.parent && isNamespaceExportDeclaration(node.parent)) {
const aliasedSymbol = checker.getAliasedSymbol(symbol);
+6
View File
@@ -1728,6 +1728,11 @@ namespace ts {
return FindAllReferences.findReferencedSymbols(program, cancellationToken, program.getSourceFiles(), getValidSourceFile(fileName), position);
}
function getFileReferences(fileName: string): ReferenceEntry[] {
synchronizeHostData();
return FindAllReferences.Core.getReferencesForFileName(fileName, program, program.getSourceFiles()).map(FindAllReferences.toReferenceEntry);
}
function getNavigateToItems(searchValue: string, maxResultCount?: number, fileName?: string, excludeDtsFiles = false): NavigateToItem[] {
synchronizeHostData();
const sourceFiles = fileName ? [getValidSourceFile(fileName)] : program.getSourceFiles();
@@ -2526,6 +2531,7 @@ namespace ts {
getTypeDefinitionAtPosition,
getReferencesAtPosition,
findReferences,
getFileReferences,
getOccurrencesAtPosition,
getDocumentHighlights,
getNameOrDottedNameSpan,
+13
View File
@@ -208,6 +208,12 @@ namespace ts {
*/
findReferences(fileName: string, position: number): string;
/**
* Returns a JSON-encoded value of the type:
* { fileName: string; textSpan: { start: number; length: number}; isWriteAccess: boolean, isDefinition?: boolean }[]
*/
getFileReferences(fileName: string): string;
/**
* @deprecated
* Returns a JSON-encoded value of the type:
@@ -916,6 +922,13 @@ namespace ts {
);
}
public getFileReferences(fileName: string) {
return this.forwardJSONCall(
`getFileReferences('${fileName})`,
() => this.languageService.getFileReferences(fileName)
);
}
public getOccurrencesAtPosition(fileName: string, position: number): string {
return this.forwardJSONCall(
`getOccurrencesAtPosition('${fileName}', ${position})`,
+1
View File
@@ -468,6 +468,7 @@ namespace ts {
getReferencesAtPosition(fileName: string, position: number): ReferenceEntry[] | undefined;
findReferences(fileName: string, position: number): ReferencedSymbol[] | undefined;
getDocumentHighlights(fileName: string, position: number, filesToSearch: string[]): DocumentHighlights[] | undefined;
getFileReferences(fileName: string): ReferenceEntry[];
/** @deprecated */
getOccurrencesAtPosition(fileName: string, position: number): readonly ReferenceEntry[] | undefined;