Skip to content

Commit f6b206a

Browse files
author
Andy
authored
When testing references, also test documentHighlights respects filesToSearch (#23306)
* When testing references, also test documentHighlights respects filesToSearch * Fix handling for redirects and move assertion inside getDocumentHighlights * Add another assert
1 parent fef2866 commit f6b206a

File tree

6 files changed

+59
-37
lines changed

6 files changed

+59
-37
lines changed

src/harness/fourslash.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1081,8 +1081,20 @@ namespace FourSlash {
10811081
}
10821082
}
10831083

1084+
private verifyDocumentHighlightsRespectFilesList(files: ReadonlyArray<string>): void {
1085+
const startFile = this.activeFile.fileName;
1086+
for (const fileName of files) {
1087+
const searchFileNames = startFile === fileName ? [startFile] : [startFile, fileName];
1088+
const highlights = this.getDocumentHighlightsAtCurrentPosition(searchFileNames);
1089+
if (!highlights.every(dh => ts.contains(searchFileNames, dh.fileName))) {
1090+
this.raiseError(`When asking for document highlights only in files ${searchFileNames}, got document highlights in ${unique(highlights, dh => dh.fileName)}`);
1091+
}
1092+
}
1093+
}
1094+
10841095
public verifyReferencesOf(range: Range, references: Range[]) {
10851096
this.goToRangeStart(range);
1097+
this.verifyDocumentHighlightsRespectFilesList(unique(references, e => e.fileName));
10861098
this.verifyReferencesAre(references);
10871099
}
10881100

@@ -1094,7 +1106,7 @@ namespace FourSlash {
10941106
}
10951107
}
10961108

1097-
public verifyReferenceGroups(starts: string | string[] | Range | Range[], parts: FourSlashInterface.ReferenceGroup[]): void {
1109+
public verifyReferenceGroups(starts: string | string[] | Range | Range[], parts: FourSlashInterface.ReferenceGroup[] | undefined): void {
10981110
interface ReferenceGroupJson {
10991111
definition: string | { text: string, range: ts.TextSpan };
11001112
references: ts.ReferenceEntry[];
@@ -1128,6 +1140,10 @@ namespace FourSlash {
11281140
};
11291141
});
11301142
this.assertObjectsEqual(fullActual, fullExpected);
1143+
1144+
if (parts) {
1145+
this.verifyDocumentHighlightsRespectFilesList(unique(ts.flatMap(parts, p => p.ranges), r => r.fileName));
1146+
}
11311147
}
11321148
}
11331149

src/services/documentHighlights.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,20 @@ namespace ts.DocumentHighlights {
2222
}
2323

2424
function getSemanticDocumentHighlights(position: number, node: Node, program: Program, cancellationToken: CancellationToken, sourceFilesToSearch: ReadonlyArray<SourceFile>): DocumentHighlights[] | undefined {
25-
const referenceEntries = FindAllReferences.getReferenceEntriesForNode(position, node, program, sourceFilesToSearch, cancellationToken);
25+
const sourceFilesSet = arrayToSet(sourceFilesToSearch, f => f.fileName);
26+
const referenceEntries = FindAllReferences.getReferenceEntriesForNode(position, node, program, sourceFilesToSearch, cancellationToken, /*options*/ undefined, sourceFilesSet);
2627
if (!referenceEntries) return undefined;
2728
const map = arrayToMultiMap(referenceEntries.map(FindAllReferences.toHighlightSpan), e => e.fileName, e => e.span);
28-
return arrayFrom(map.entries(), ([fileName, highlightSpans]) => ({ fileName, highlightSpans }));
29+
return arrayFrom(map.entries(), ([fileName, highlightSpans]) => {
30+
if (!sourceFilesSet.has(fileName)) {
31+
Debug.assert(program.redirectTargetsSet.has(fileName));
32+
const redirectTarget = program.getSourceFile(fileName);
33+
const redirect = find(sourceFilesToSearch, f => f.redirectInfo && f.redirectInfo.redirectTarget === redirectTarget)!;
34+
fileName = redirect.fileName;
35+
Debug.assert(sourceFilesSet.has(fileName));
36+
}
37+
return { fileName, highlightSpans };
38+
});
2939
}
3040

3141
function getSyntacticDocumentHighlights(node: Node, sourceFile: SourceFile): DocumentHighlights[] {

src/services/findAllReferences.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ namespace ts.FindAllReferences {
4343

4444
export function findReferencedSymbols(program: Program, cancellationToken: CancellationToken, sourceFiles: ReadonlyArray<SourceFile>, sourceFile: SourceFile, position: number): ReferencedSymbol[] | undefined {
4545
const node = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
46-
const referencedSymbols = Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, /*options*/ {});
46+
const referencedSymbols = Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken);
4747
const checker = program.getTypeChecker();
4848
return !referencedSymbols || !referencedSymbols.length ? undefined : mapDefined<SymbolAndEntries, ReferencedSymbol>(referencedSymbols, ({ definition, references }) =>
4949
// Only include referenced symbols that have a valid definition.
@@ -88,8 +88,8 @@ namespace ts.FindAllReferences {
8888
return map(flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options)), toReferenceEntry);
8989
}
9090

91-
export function getReferenceEntriesForNode(position: number, node: Node, program: Program, sourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken, options: Options = {}): Entry[] | undefined {
92-
return flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options));
91+
export function getReferenceEntriesForNode(position: number, node: Node, program: Program, sourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken, options: Options = {}, sourceFilesSet: ReadonlyMap<true> = arrayToSet(sourceFiles, f => f.fileName)): Entry[] | undefined {
92+
return flattenEntries(Core.getReferencedSymbolsForNode(position, node, program, sourceFiles, cancellationToken, options, sourceFilesSet));
9393
}
9494

9595
function flattenEntries(referenceSymbols: SymbolAndEntries[]): Entry[] {
@@ -231,10 +231,10 @@ namespace ts.FindAllReferences {
231231
/* @internal */
232232
namespace ts.FindAllReferences.Core {
233233
/** Core find-all-references algorithm. Handles special cases before delegating to `getReferencedSymbolsForSymbol`. */
234-
export function getReferencedSymbolsForNode(position: number, node: Node, program: Program, sourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken, options: Options = {}): SymbolAndEntries[] | undefined {
234+
export function getReferencedSymbolsForNode(position: number, node: Node, program: Program, sourceFiles: ReadonlyArray<SourceFile>, cancellationToken: CancellationToken, options: Options = {}, sourceFilesSet: ReadonlyMap<true> = arrayToSet(sourceFiles, f => f.fileName)): SymbolAndEntries[] | undefined {
235235
if (isSourceFile(node)) {
236236
const reference = GoToDefinition.getReferenceAtPosition(node, position, program);
237-
return reference && getReferencedSymbolsForModule(program, program.getTypeChecker().getMergedSymbol(reference.file.symbol), sourceFiles);
237+
return reference && getReferencedSymbolsForModule(program, program.getTypeChecker().getMergedSymbol(reference.file.symbol), sourceFiles, sourceFilesSet);
238238
}
239239

240240
if (!options.implementations) {
@@ -254,10 +254,10 @@ namespace ts.FindAllReferences.Core {
254254
}
255255

256256
if (symbol.flags & SymbolFlags.Module && isModuleReferenceLocation(node)) {
257-
return getReferencedSymbolsForModule(program, symbol, sourceFiles);
257+
return getReferencedSymbolsForModule(program, symbol, sourceFiles, sourceFilesSet);
258258
}
259259

260-
return getReferencedSymbolsForSymbol(symbol, node, sourceFiles, checker, cancellationToken, options);
260+
return getReferencedSymbolsForSymbol(symbol, node, sourceFiles, sourceFilesSet, checker, cancellationToken, options);
261261
}
262262

263263
function isModuleReferenceLocation(node: Node): boolean {
@@ -277,7 +277,7 @@ namespace ts.FindAllReferences.Core {
277277
}
278278
}
279279

280-
function getReferencedSymbolsForModule(program: Program, symbol: Symbol, sourceFiles: ReadonlyArray<SourceFile>): SymbolAndEntries[] {
280+
function getReferencedSymbolsForModule(program: Program, symbol: Symbol, sourceFiles: ReadonlyArray<SourceFile>, sourceFilesSet: ReadonlyMap<true>): SymbolAndEntries[] {
281281
Debug.assert(!!symbol.valueDeclaration);
282282

283283
const references = findModuleReferences(program, sourceFiles, symbol).map<Entry>(reference => {
@@ -299,7 +299,9 @@ namespace ts.FindAllReferences.Core {
299299
// Don't include the source file itself. (This may not be ideal behavior, but awkward to include an entire file as a reference.)
300300
break;
301301
case SyntaxKind.ModuleDeclaration:
302-
references.push({ type: "node", node: (decl as ModuleDeclaration).name });
302+
if (sourceFilesSet.has(decl.getSourceFile().fileName)) {
303+
references.push({ type: "node", node: (decl as ModuleDeclaration).name });
304+
}
303305
break;
304306
default:
305307
Debug.fail("Expected a module symbol to be declared by a SourceFile or ModuleDeclaration.");
@@ -339,14 +341,14 @@ namespace ts.FindAllReferences.Core {
339341
}
340342

341343
/** Core find-all-references algorithm for a normal symbol. */
342-
function getReferencedSymbolsForSymbol(symbol: Symbol, node: Node, sourceFiles: ReadonlyArray<SourceFile>, checker: TypeChecker, cancellationToken: CancellationToken, options: Options): SymbolAndEntries[] {
344+
function getReferencedSymbolsForSymbol(symbol: Symbol, node: Node, sourceFiles: ReadonlyArray<SourceFile>, sourceFilesSet: ReadonlyMap<true>, checker: TypeChecker, cancellationToken: CancellationToken, options: Options): SymbolAndEntries[] {
343345
symbol = skipPastExportOrImportSpecifierOrUnion(symbol, node, checker) || symbol;
344346

345347
// Compute the meaning from the location and the symbol it references
346348
const searchMeaning = getIntersectingMeaningFromDeclarations(node, symbol);
347349

348350
const result: SymbolAndEntries[] = [];
349-
const state = new State(sourceFiles, getSpecialSearchKind(node), checker, cancellationToken, searchMeaning, options, result);
351+
const state = new State(sourceFiles, sourceFilesSet, getSpecialSearchKind(node), checker, cancellationToken, searchMeaning, options, result);
350352

351353
if (node.kind === SyntaxKind.DefaultKeyword) {
352354
addReference(node, symbol, state);
@@ -469,28 +471,26 @@ namespace ts.FindAllReferences.Core {
469471
*/
470472
readonly markSeenReExportRHS = nodeSeenTracker();
471473

472-
private readonly includedSourceFiles: Map<true>;
473-
474474
constructor(
475475
readonly sourceFiles: ReadonlyArray<SourceFile>,
476+
readonly sourceFilesSet: ReadonlyMap<true>,
476477
/** True if we're searching for constructor references. */
477478
readonly specialSearchKind: SpecialSearchKind,
478479
readonly checker: TypeChecker,
479480
readonly cancellationToken: CancellationToken,
480481
readonly searchMeaning: SemanticMeaning,
481482
readonly options: Options,
482483
private readonly result: Push<SymbolAndEntries>) {
483-
this.includedSourceFiles = arrayToSet(sourceFiles, s => s.fileName);
484484
}
485485

486486
includesSourceFile(sourceFile: SourceFile): boolean {
487-
return this.includedSourceFiles.has(sourceFile.fileName);
487+
return this.sourceFilesSet.has(sourceFile.fileName);
488488
}
489489

490490
private importTracker: ImportTracker | undefined;
491491
/** Gets every place to look for references of an exported symbols. See `ImportsResult` in `importTracker.ts` for more documentation. */
492492
getImportSearches(exportSymbol: Symbol, exportInfo: ExportInfo): ImportsResult {
493-
if (!this.importTracker) this.importTracker = createImportTracker(this.sourceFiles, this.checker, this.cancellationToken);
493+
if (!this.importTracker) this.importTracker = createImportTracker(this.sourceFiles, this.sourceFilesSet, this.checker, this.cancellationToken);
494494
return this.importTracker(exportSymbol, exportInfo, this.options.isForRename);
495495
}
496496

src/services/importTracker.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ namespace ts.FindAllReferences {
1212
export type ImportTracker = (exportSymbol: Symbol, exportInfo: ExportInfo, isForRename: boolean) => ImportsResult;
1313

1414
/** Creates the imports map and returns an ImportTracker that uses it. Call this lazily to avoid calling `getDirectImportsMap` unnecessarily. */
15-
export function createImportTracker(sourceFiles: ReadonlyArray<SourceFile>, checker: TypeChecker, cancellationToken: CancellationToken): ImportTracker {
15+
export function createImportTracker(sourceFiles: ReadonlyArray<SourceFile>, sourceFilesSet: ReadonlyMap<true>, checker: TypeChecker, cancellationToken: CancellationToken): ImportTracker {
1616
const allDirectImports = getDirectImportsMap(sourceFiles, checker, cancellationToken);
1717
return (exportSymbol, exportInfo, isForRename) => {
18-
const { directImports, indirectUsers } = getImportersForExport(sourceFiles, allDirectImports, exportInfo, checker, cancellationToken);
18+
const { directImports, indirectUsers } = getImportersForExport(sourceFiles, sourceFilesSet, allDirectImports, exportInfo, checker, cancellationToken);
1919
return { indirectUsers, ...getSearchesFromDirectImports(directImports, exportSymbol, exportInfo.exportKind, checker, isForRename) };
2020
};
2121
}
@@ -39,6 +39,7 @@ namespace ts.FindAllReferences {
3939
/** Returns import statements that directly reference the exporting module, and a list of files that may access the module through a namespace. */
4040
function getImportersForExport(
4141
sourceFiles: ReadonlyArray<SourceFile>,
42+
sourceFilesSet: ReadonlyMap<true>,
4243
allDirectImports: Map<ImporterOrCallExpression[]>,
4344
{ exportingModuleSymbol, exportKind }: ExportInfo,
4445
checker: TypeChecker,
@@ -62,7 +63,7 @@ namespace ts.FindAllReferences {
6263

6364
// Module augmentations may use this module's exports without importing it.
6465
for (const decl of exportingModuleSymbol.declarations) {
65-
if (isExternalModuleAugmentation(decl)) {
66+
if (isExternalModuleAugmentation(decl) && sourceFilesSet.has(decl.getSourceFile().fileName)) {
6667
addIndirectUser(decl);
6768
}
6869
}

src/services/services.ts

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1697,20 +1697,17 @@ namespace ts {
16971697

16981698
/// References and Occurrences
16991699
function getOccurrencesAtPosition(fileName: string, position: number): ReferenceEntry[] {
1700-
const canonicalFileName = getCanonicalFileName(normalizeSlashes(fileName));
1701-
return flatMap(getDocumentHighlights(fileName, position, [fileName]), entry => entry.highlightSpans.map<ReferenceEntry>(highlightSpan => {
1702-
Debug.assert(getCanonicalFileName(normalizeSlashes(entry.fileName)) === canonicalFileName); // Get occurrences only supports reporting occurrences for the file queried.
1703-
return {
1704-
fileName: entry.fileName,
1705-
textSpan: highlightSpan.textSpan,
1706-
isWriteAccess: highlightSpan.kind === HighlightSpanKind.writtenReference,
1707-
isDefinition: false,
1708-
isInString: highlightSpan.isInString,
1709-
};
1710-
}));
1700+
return flatMap(getDocumentHighlights(fileName, position, [fileName]), entry => entry.highlightSpans.map<ReferenceEntry>(highlightSpan => ({
1701+
fileName: entry.fileName,
1702+
textSpan: highlightSpan.textSpan,
1703+
isWriteAccess: highlightSpan.kind === HighlightSpanKind.writtenReference,
1704+
isDefinition: false,
1705+
isInString: highlightSpan.isInString,
1706+
})));
17111707
}
17121708

17131709
function getDocumentHighlights(fileName: string, position: number, filesToSearch: ReadonlyArray<string>): DocumentHighlights[] {
1710+
Debug.assert(contains(filesToSearch, fileName));
17141711
synchronizeHostData();
17151712
const sourceFilesToSearch = map(filesToSearch, f => Debug.assertDefined(program.getSourceFile(f)));
17161713
const sourceFile = getValidSourceFile(fileName);

tests/cases/fourslash/duplicatePackageServices.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
////}
2424

2525
// @Filename: /node_modules/b/node_modules/x/package.json
26-
////{ "name": "x", "version": "1.2./*bVersionPatch*/3" }
26+
////{ "name": "x", "version": "1.2.3" }
2727

2828
// @Filename: /src/a.ts
2929
////import { a } from "a";
@@ -40,7 +40,5 @@ const aImport = { definition: "(alias) class X\nimport X", ranges: [r0, r1] };
4040
const def = { definition: "class X", ranges: [r2] };
4141
const bImport = { definition: "(alias) class X\nimport X", ranges: [r3, r4] };
4242
verify.referenceGroups([r0, r1], [aImport, def, bImport]);
43-
verify.referenceGroups([r2], [def, aImport, bImport]);
43+
verify.referenceGroups([r2, r5], [def, aImport, bImport]);
4444
verify.referenceGroups([r3, r4], [bImport, def, aImport]);
45-
46-
verify.referenceGroups(r5, [def, aImport, bImport]);

0 commit comments

Comments
 (0)