@@ -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