Skip to content

Commit bfc0093

Browse files
author
Andy
authored
Fix bug: Get mapped location of definition for findAllReferencesFull (#27113)
1 parent c9f1902 commit bfc0093

File tree

2 files changed

+91
-17
lines changed

2 files changed

+91
-17
lines changed

src/server/session.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -336,16 +336,23 @@ namespace ts.server {
336336
function combineProjectOutputForReferences(projects: Projects, defaultProject: Project, initialLocation: sourcemaps.SourceMappableLocation, projectService: ProjectService): ReadonlyArray<ReferencedSymbol> {
337337
const outputs: ReferencedSymbol[] = [];
338338

339-
combineProjectOutputWorker<sourcemaps.SourceMappableLocation>(projects, defaultProject, initialLocation, projectService, ({ project, location }, tryAddToTodo) => {
339+
combineProjectOutputWorker<sourcemaps.SourceMappableLocation>(projects, defaultProject, initialLocation, projectService, ({ project, location }, getMappedLocation) => {
340340
for (const outputReferencedSymbol of project.getLanguageService().findReferences(location.fileName, location.position) || emptyArray) {
341-
let symbolToAddTo = find(outputs, o => documentSpansEqual(o.definition, outputReferencedSymbol.definition));
341+
const mappedDefinitionFile = getMappedLocation(project, documentSpanLocation(outputReferencedSymbol.definition));
342+
const definition: ReferencedSymbolDefinitionInfo = mappedDefinitionFile === undefined ? outputReferencedSymbol.definition : {
343+
...outputReferencedSymbol.definition,
344+
textSpan: createTextSpan(mappedDefinitionFile.position, outputReferencedSymbol.definition.textSpan.length),
345+
fileName: mappedDefinitionFile.fileName,
346+
};
347+
let symbolToAddTo = find(outputs, o => documentSpansEqual(o.definition, definition));
342348
if (!symbolToAddTo) {
343-
symbolToAddTo = { definition: outputReferencedSymbol.definition, references: [] };
349+
symbolToAddTo = { definition, references: [] };
344350
outputs.push(symbolToAddTo);
345351
}
346352

347353
for (const ref of outputReferencedSymbol.references) {
348-
if (!contains(symbolToAddTo.references, ref, documentSpansEqual) && !tryAddToTodo(project, documentSpanLocation(ref))) {
354+
// If it's in a mapped file, that is added to the todo list by `getMappedLocation`.
355+
if (!contains(symbolToAddTo.references, ref, documentSpansEqual) && !getMappedLocation(project, documentSpanLocation(ref))) {
349356
symbolToAddTo.references.push(ref);
350357
}
351358
}
@@ -373,12 +380,17 @@ namespace ts.server {
373380
}
374381
}
375382

383+
type CombineProjectOutputCallback<TLocation extends sourcemaps.SourceMappableLocation | undefined> = (
384+
where: ProjectAndLocation<TLocation>,
385+
getMappedLocation: (project: Project, location: sourcemaps.SourceMappableLocation) => sourcemaps.SourceMappableLocation | undefined,
386+
) => void;
387+
376388
function combineProjectOutputWorker<TLocation extends sourcemaps.SourceMappableLocation | undefined>(
377389
projects: Projects,
378390
defaultProject: Project,
379391
initialLocation: TLocation,
380392
projectService: ProjectService,
381-
cb: (where: ProjectAndLocation<TLocation>, getMappedLocation: (project: Project, location: sourcemaps.SourceMappableLocation) => boolean) => void,
393+
cb: CombineProjectOutputCallback<TLocation>,
382394
getDefinition: (() => sourcemaps.SourceMappableLocation | undefined) | undefined,
383395
): void {
384396
let toDo: ProjectAndLocation<TLocation>[] | undefined;
@@ -417,13 +429,13 @@ namespace ts.server {
417429
projectService: ProjectService,
418430
toDo: ProjectAndLocation<TLocation>[] | undefined,
419431
seenProjects: Map<true>,
420-
cb: (where: ProjectAndLocation<TLocation>, getMappedLocation: (project: Project, location: sourcemaps.SourceMappableLocation) => boolean) => void,
432+
cb: CombineProjectOutputCallback<TLocation>,
421433
): ProjectAndLocation<TLocation>[] | undefined {
422434
if (projectAndLocation.project.getCancellationToken().isCancellationRequested()) return undefined; // Skip rest of toDo if cancelled
423435
cb(projectAndLocation, (project, location) => {
424436
seenProjects.set(projectAndLocation.project.projectName, true);
425437
const originalLocation = projectService.getOriginalLocationEnsuringConfiguredProject(project, location);
426-
if (!originalLocation) return false;
438+
if (!originalLocation) return undefined;
427439

428440
const originalScriptInfo = projectService.getScriptInfo(originalLocation.fileName)!;
429441
toDo = toDo || [];
@@ -437,7 +449,7 @@ namespace ts.server {
437449
for (const symlinkedProject of symlinkedProjects) addToTodo({ project: symlinkedProject, location: originalLocation as TLocation }, toDo!, seenProjects);
438450
});
439451
}
440-
return true;
452+
return originalLocation;
441453
});
442454
return toDo;
443455
}

src/testRunner/unittests/tsserverProjectSystem.ts

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3491,7 +3491,7 @@ namespace ts.projectSystem {
34913491
host.checkTimeoutQueueLength(2); // Update configured project and projects for open file
34923492
checkProjectActualFiles(services.configuredProjects.get(config.path)!, filesWithFileA.map(f => f.path));
34933493

3494-
// host.fileExists = originalFileExists;
3494+
// host.fileExists = originalFileExists;
34953495
openFile(fileSubA);
34963496
// This should create inferred project since fileSubA not on the disk
34973497
checkProjectActualFiles(services.configuredProjects.get(config.path)!, mapDefined(filesWithFileA, f => f === fileA ? undefined : f.path));
@@ -3672,7 +3672,7 @@ namespace ts.projectSystem {
36723672
path: `${projectRoot}/app1/tsconfig.json`,
36733673
content: JSON.stringify({
36743674
files: ["app.ts", "../core/core.ts"],
3675-
compilerOptions: { outFile : "build/output.js" },
3675+
compilerOptions: { outFile: "build/output.js" },
36763676
compileOnSave: true
36773677
})
36783678
};
@@ -6778,9 +6778,9 @@ namespace ts.projectSystem {
67786778
fileName: "/a.1.ts",
67796779
textChanges: [
67806780
{
6781-
start: { line: 0, offset: 0 },
6782-
end: { line: 0, offset: 0 },
6783-
newText: "export const a = 0;",
6781+
start: { line: 0, offset: 0 },
6782+
end: { line: 0, offset: 0 },
6783+
newText: "export const a = 0;",
67846784
},
67856785
],
67866786
}
@@ -8672,7 +8672,7 @@ new C();`
86728672
}));
86738673
checkWatchedDirectories(host, [], /*recursive*/ false);
86748674
checkWatchedDirectories(host, arrayFrom(expectedRecursiveDirectories.keys()), /*recursive*/ true);
8675-
}
8675+
}
86768676

86778677
describe("from files in same folder", () => {
86788678
function getFiles(fileContent: string) {
@@ -9750,7 +9750,7 @@ declare class TestLib {
97509750
function verifyATsConfigOriginalProject(session: TestSession) {
97519751
checkNumberOfProjects(session.getProjectService(), { inferredProjects: 1, configuredProjects: 1 });
97529752
verifyInferredProjectUnchanged(session);
9753-
verifyATsConfigProject(session);
9753+
verifyATsConfigProject(session);
97549754
// Close user file should close all the projects
97559755
closeFilesForSession([userTs], session);
97569756
verifyOnlyOrphanInferredProject(session);
@@ -9900,11 +9900,12 @@ declare class TestLib {
99009900
verifyATsConfigWhenOpened(session);
99019901
});
99029902

9903+
interface ReferencesFullRequest extends protocol.FileLocationRequest { readonly command: protocol.CommandTypes.ReferencesFull; }
9904+
interface ReferencesFullResponse extends protocol.Response { readonly body: ReadonlyArray<ReferencedSymbol>; }
9905+
99039906
it("findAllReferencesFull", () => {
99049907
const session = makeSampleProjects();
99059908

9906-
interface ReferencesFullRequest extends protocol.FileLocationRequest { command: protocol.CommandTypes.ReferencesFull; }
9907-
interface ReferencesFullResponse extends protocol.Response { body: ReadonlyArray<ReferencedSymbol>; }
99089909
const responseFull = executeSessionRequest<ReferencesFullRequest, ReferencesFullResponse>(session, protocol.CommandTypes.ReferencesFull, protocolFileLocationFromSubstring(userTs, "fnA()"));
99099910

99109911
function fnAVoid(kind: SymbolDisplayPartKind): SymbolDisplayPart[] {
@@ -9961,6 +9962,67 @@ declare class TestLib {
99619962
verifyATsConfigOriginalProject(session);
99629963
});
99639964

9965+
it("findAllReferencesFull definition is in mapped file", () => {
9966+
const aTs: File = { path: "/a/a.ts", content: `function f() {}` };
9967+
const aTsconfig: File = {
9968+
path: "/a/tsconfig.json",
9969+
content: JSON.stringify({ compilerOptions: { declaration: true, declarationMap: true, outFile: "../bin/a.js" } }),
9970+
};
9971+
const bTs: File = { path: "/b/b.ts", content: `f();` };
9972+
const bTsconfig: File = { path: "/b/tsconfig.json", content: JSON.stringify({ references: [{ path: "../a" }] }) };
9973+
const aDts: File = { path: "/bin/a.d.ts", content: `declare function f(): void;\n//# sourceMappingURL=a.d.ts.map` };
9974+
const aDtsMap: File = {
9975+
path: "/bin/a.d.ts.map",
9976+
content: JSON.stringify({ version: 3, file: "a.d.ts", sourceRoot: "", sources: ["../a/a.ts"], names: [], mappings: "AAAA,iBAAS,CAAC,SAAK" }),
9977+
};
9978+
9979+
const session = createSession(createServerHost([aTs, aTsconfig, bTs, bTsconfig, aDts, aDtsMap]));
9980+
checkDeclarationFiles(aTs, session, [aDtsMap, aDts]);
9981+
openFilesForSession([bTs], session);
9982+
checkNumberOfProjects(session.getProjectService(), { configuredProjects: 1 });
9983+
9984+
const responseFull = executeSessionRequest<ReferencesFullRequest, ReferencesFullResponse>(session, protocol.CommandTypes.ReferencesFull, protocolFileLocationFromSubstring(bTs, "f()"));
9985+
9986+
assert.deepEqual<ReadonlyArray<ReferencedSymbol>>(responseFull, [
9987+
{
9988+
definition: {
9989+
containerKind: ScriptElementKind.unknown,
9990+
containerName: "",
9991+
displayParts: [
9992+
keywordPart(SyntaxKind.FunctionKeyword),
9993+
spacePart(),
9994+
displayPart("f", SymbolDisplayPartKind.functionName),
9995+
punctuationPart(SyntaxKind.OpenParenToken),
9996+
punctuationPart(SyntaxKind.CloseParenToken),
9997+
punctuationPart(SyntaxKind.ColonToken),
9998+
spacePart(),
9999+
keywordPart(SyntaxKind.VoidKeyword),
10000+
],
10001+
fileName: aTs.path,
10002+
kind: ScriptElementKind.functionElement,
10003+
name: "function f(): void",
10004+
textSpan: { start: 9, length: 1 },
10005+
},
10006+
references: [
10007+
{
10008+
fileName: bTs.path,
10009+
isDefinition: false,
10010+
isInString: undefined,
10011+
isWriteAccess: false,
10012+
textSpan: { start: 0, length: 1 },
10013+
},
10014+
{
10015+
fileName: aTs.path,
10016+
isDefinition: true,
10017+
isInString: undefined,
10018+
isWriteAccess: true,
10019+
textSpan: { start: 9, length: 1 },
10020+
},
10021+
],
10022+
}
10023+
]);
10024+
});
10025+
996410026
it("findAllReferences -- target does not exist", () => {
996510027
const session = makeSampleProjects();
996610028

0 commit comments

Comments
 (0)