Skip to content

Commit 597aaca

Browse files
authored
Merge pull request microsoft#28346 from Microsoft/transitiveExports
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
2 parents 576fdf9 + 23e7330 commit 597aaca

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)