Skip to content

Commit 76f7ee9

Browse files
authored
Merge pull request microsoft#25593 from Microsoft/errorInFileWithDeepImport
Report errors correctly in watch mode by invalidating errors from files referencing modules that export from changed file
2 parents 2f525fa + 06fead5 commit 76f7ee9

File tree

7 files changed

+255
-24
lines changed

7 files changed

+255
-24
lines changed

src/compiler/builder.ts

+79-6
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,18 @@ namespace ts {
3030
* These will be commited whenever the iteration through affected files of current changed file is complete
3131
*/
3232
currentAffectedFilesSignatures: Map<string> | undefined;
33+
/**
34+
* Newly computed visible to outside referencedSet
35+
*/
36+
currentAffectedFilesExportedModulesMap: BuilderState.ComputingExportedModulesMap | undefined;
3337
/**
3438
* Already seen affected files
3539
*/
3640
seenAffectedFiles: Map<true> | undefined;
41+
/**
42+
* True if the semantic diagnostics were copied from the old state
43+
*/
44+
semanticDiagnosticsFromOldState?: Map<true>;
3745
/**
3846
* program corresponding to this state
3947
*/
@@ -96,6 +104,10 @@ namespace ts {
96104
const diagnostics = oldState!.semanticDiagnosticsPerFile!.get(sourceFilePath);
97105
if (diagnostics) {
98106
state.semanticDiagnosticsPerFile!.set(sourceFilePath, diagnostics);
107+
if (!state.semanticDiagnosticsFromOldState) {
108+
state.semanticDiagnosticsFromOldState = createMap<true>();
109+
}
110+
state.semanticDiagnosticsFromOldState.set(sourceFilePath, true);
99111
}
100112
}
101113
});
@@ -120,17 +132,17 @@ namespace ts {
120132
while (true) {
121133
const { affectedFiles } = state;
122134
if (affectedFiles) {
123-
const { seenAffectedFiles, semanticDiagnosticsPerFile } = state;
135+
const seenAffectedFiles = state.seenAffectedFiles!;
124136
let affectedFilesIndex = state.affectedFilesIndex!; // TODO: GH#18217
125137
while (affectedFilesIndex < affectedFiles.length) {
126138
const affectedFile = affectedFiles[affectedFilesIndex];
127-
if (!seenAffectedFiles!.has(affectedFile.path)) {
139+
if (!seenAffectedFiles.has(affectedFile.path)) {
128140
// Set the next affected file as seen and remove the cached semantic diagnostics
129141
state.affectedFilesIndex = affectedFilesIndex;
130-
semanticDiagnosticsPerFile!.delete(affectedFile.path);
142+
cleanSemanticDiagnosticsOfAffectedFile(state, affectedFile);
131143
return affectedFile;
132144
}
133-
seenAffectedFiles!.set(affectedFile.path, true);
145+
seenAffectedFiles.set(affectedFile.path, true);
134146
affectedFilesIndex++;
135147
}
136148

@@ -140,6 +152,7 @@ namespace ts {
140152
// Commit the changes in file signature
141153
BuilderState.updateSignaturesFromCache(state, state.currentAffectedFilesSignatures!);
142154
state.currentAffectedFilesSignatures!.clear();
155+
BuilderState.updateExportedFilesMapFromCache(state, state.currentAffectedFilesExportedModulesMap);
143156
state.affectedFiles = undefined;
144157
}
145158

@@ -160,14 +173,74 @@ namespace ts {
160173

161174
// Get next batch of affected files
162175
state.currentAffectedFilesSignatures = state.currentAffectedFilesSignatures || createMap();
163-
state.affectedFiles = BuilderState.getFilesAffectedBy(state, state.program, nextKey.value as Path, cancellationToken, computeHash, state.currentAffectedFilesSignatures);
176+
if (state.exportedModulesMap) {
177+
state.currentAffectedFilesExportedModulesMap = state.currentAffectedFilesExportedModulesMap || createMap<BuilderState.ReferencedSet | false>();
178+
}
179+
state.affectedFiles = BuilderState.getFilesAffectedBy(state, state.program, nextKey.value as Path, cancellationToken, computeHash, state.currentAffectedFilesSignatures, state.currentAffectedFilesExportedModulesMap);
164180
state.currentChangedFilePath = nextKey.value as Path;
165-
state.semanticDiagnosticsPerFile!.delete(nextKey.value as Path);
166181
state.affectedFilesIndex = 0;
167182
state.seenAffectedFiles = state.seenAffectedFiles || createMap<true>();
168183
}
169184
}
170185

186+
/**
187+
* Remove the semantic diagnostics cached from old state for affected File and the files that are referencing modules that export entities from affected file
188+
*/
189+
function cleanSemanticDiagnosticsOfAffectedFile(state: BuilderProgramState, affectedFile: SourceFile) {
190+
if (removeSemanticDiagnosticsOf(state, affectedFile.path)) {
191+
// If there are no more diagnostics from old cache, done
192+
return;
193+
}
194+
195+
// If there was change in signature for the changed file,
196+
// then delete the semantic diagnostics for files that are affected by using exports of this module
197+
198+
if (!state.exportedModulesMap || state.affectedFiles!.length === 1 || !state.changedFilesSet.has(affectedFile.path)) {
199+
return;
200+
}
201+
202+
Debug.assert(!!state.currentAffectedFilesExportedModulesMap);
203+
// Go through exported modules from cache first
204+
// If exported modules has path, all files referencing file exported from are affected
205+
if (forEachEntry(state.currentAffectedFilesExportedModulesMap!, (exportedModules, exportedFromPath) =>
206+
exportedModules &&
207+
exportedModules.has(affectedFile.path) &&
208+
removeSemanticDiagnosticsOfFilesReferencingPath(state, exportedFromPath as Path)
209+
)) {
210+
return;
211+
}
212+
213+
// If exported from path is not from cache and exported modules has path, all files referencing file exported from are affected
214+
forEachEntry(state.exportedModulesMap, (exportedModules, exportedFromPath) =>
215+
!state.currentAffectedFilesExportedModulesMap!.has(exportedFromPath) && // If we already iterated this through cache, ignore it
216+
exportedModules.has(affectedFile.path) &&
217+
removeSemanticDiagnosticsOfFilesReferencingPath(state, exportedFromPath as Path)
218+
);
219+
}
220+
221+
/**
222+
* removes the semantic diagnostics of files referencing referencedPath and
223+
* returns true if there are no more semantic diagnostics from old state
224+
*/
225+
function removeSemanticDiagnosticsOfFilesReferencingPath(state: BuilderProgramState, referencedPath: Path) {
226+
return forEachEntry(state.referencedMap!, (referencesInFile, filePath) =>
227+
referencesInFile.has(referencedPath) && removeSemanticDiagnosticsOf(state, filePath as Path)
228+
);
229+
}
230+
231+
/**
232+
* Removes semantic diagnostics for path and
233+
* returns true if there are no more semantic diagnostics from the old state
234+
*/
235+
function removeSemanticDiagnosticsOf(state: BuilderProgramState, path: Path) {
236+
if (!state.semanticDiagnosticsFromOldState) {
237+
return false;
238+
}
239+
state.semanticDiagnosticsFromOldState.delete(path);
240+
state.semanticDiagnosticsPerFile!.delete(path);
241+
return !state.semanticDiagnosticsFromOldState.size;
242+
}
243+
171244
/**
172245
* This is called after completing operation on the next affected file.
173246
* The operations here are postponed to ensure that cancellation during the iteration is handled correctly

0 commit comments

Comments
 (0)