Skip to content

Commit 23e7330

Browse files
committed
When removing old diagnostics for files referencing modules that export affected file with signature change, delete the diagnostics of the module as well as anything that exports that module
Fixes #28328
1 parent eba83f4 commit 23e7330

File tree

2 files changed

+92
-1
lines changed

2 files changed

+92
-1
lines changed

src/compiler/builder.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,35 @@ namespace ts {
225225
*/
226226
function removeSemanticDiagnosticsOfFilesReferencingPath(state: BuilderProgramState, referencedPath: Path) {
227227
return forEachEntry(state.referencedMap!, (referencesInFile, filePath) =>
228-
referencesInFile.has(referencedPath) && removeSemanticDiagnosticsOf(state, filePath as Path)
228+
referencesInFile.has(referencedPath) && removeSemanticDiagnosticsOfFileAndExportsOfFile(state, filePath as Path)
229+
);
230+
}
231+
232+
/**
233+
* Removes semantic diagnostics of file and anything that exports this file
234+
*/
235+
function removeSemanticDiagnosticsOfFileAndExportsOfFile(state: BuilderProgramState, filePath: Path): boolean {
236+
if (removeSemanticDiagnosticsOf(state, filePath)) {
237+
// If there are no more diagnostics from old cache, done
238+
return true;
239+
}
240+
241+
Debug.assert(!!state.currentAffectedFilesExportedModulesMap);
242+
// Go through exported modules from cache first
243+
// If exported modules has path, all files referencing file exported from are affected
244+
if (forEachEntry(state.currentAffectedFilesExportedModulesMap!, (exportedModules, exportedFromPath) =>
245+
exportedModules &&
246+
exportedModules.has(filePath) &&
247+
removeSemanticDiagnosticsOfFileAndExportsOfFile(state, exportedFromPath as Path)
248+
)) {
249+
return true;
250+
}
251+
252+
// If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected
253+
return !!forEachEntry(state.exportedModulesMap!, (exportedModules, exportedFromPath) =>
254+
!state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it
255+
exportedModules.has(filePath) &&
256+
removeSemanticDiagnosticsOfFileAndExportsOfFile(state, exportedFromPath as Path)
229257
);
230258
}
231259

src/testRunner/unittests/tscWatchMode.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1472,6 +1472,69 @@ foo().hello`
14721472
checkProgramActualFiles(watch(), [aFile.path, libFile.path]);
14731473
checkOutputErrorsIncremental(host, emptyArray);
14741474
});
1475+
1476+
it("updates errors when file transitively exported file changes", () => {
1477+
const projectLocation = "/user/username/projects/myproject";
1478+
const config: File = {
1479+
path: `${projectLocation}/tsconfig.json`,
1480+
content: JSON.stringify({
1481+
files: ["app.ts"],
1482+
compilerOptions: { baseUrl: "." }
1483+
})
1484+
};
1485+
const app: File = {
1486+
path: `${projectLocation}/app.ts`,
1487+
content: `import { Data } from "lib2/public";
1488+
export class App {
1489+
public constructor() {
1490+
new Data().test();
1491+
}
1492+
}`
1493+
};
1494+
const lib2Public: File = {
1495+
path: `${projectLocation}/lib2/public.ts`,
1496+
content: `export * from "./data";`
1497+
};
1498+
const lib2Data: File = {
1499+
path: `${projectLocation}/lib2/data.ts`,
1500+
content: `import { ITest } from "lib1/public";
1501+
export class Data {
1502+
public test() {
1503+
const result: ITest = {
1504+
title: "title"
1505+
}
1506+
return result;
1507+
}
1508+
}`
1509+
};
1510+
const lib1Public: File = {
1511+
path: `${projectLocation}/lib1/public.ts`,
1512+
content: `export * from "./tools/public";`
1513+
};
1514+
const lib1ToolsPublic: File = {
1515+
path: `${projectLocation}/lib1/tools/public.ts`,
1516+
content: `export * from "./tools.interface";`
1517+
};
1518+
const lib1ToolsInterface: File = {
1519+
path: `${projectLocation}/lib1/tools/tools.interface.ts`,
1520+
content: `export interface ITest {
1521+
title: string;
1522+
}`
1523+
};
1524+
const filesWithoutConfig = [libFile, app, lib2Public, lib2Data, lib1Public, lib1ToolsPublic, lib1ToolsInterface];
1525+
const files = [config, ...filesWithoutConfig];
1526+
const host = createWatchedSystem(files, { currentDirectory: projectLocation });
1527+
const watch = createWatchOfConfigFile(config.path, host);
1528+
checkProgramActualFiles(watch(), filesWithoutConfig.map(f => f.path));
1529+
checkOutputErrorsInitial(host, emptyArray);
1530+
1531+
host.writeFile(lib1ToolsInterface.path, lib1ToolsInterface.content.replace("title", "title2"));
1532+
host.checkTimeoutQueueLengthAndRun(1);
1533+
checkProgramActualFiles(watch(), filesWithoutConfig.map(f => f.path));
1534+
checkOutputErrorsIncremental(host, [
1535+
"lib2/data.ts(5,13): error TS2322: Type '{ title: string; }' is not assignable to type 'ITest'.\n Object literal may only specify known properties, but 'title' does not exist in type 'ITest'. Did you mean to write 'title2'?\n"
1536+
]);
1537+
});
14751538
});
14761539

14771540
describe("tsc-watch emit with outFile or out setting", () => {

0 commit comments

Comments
 (0)