Return potentially affected files from applyPendingFileChanges()

Change-Id: I7a9410a5ba8a2cdbc2fd44fb5e25c311569696ab
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/237180
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/dart/analysis/analysis_context.dart b/pkg/analyzer/lib/dart/analysis/analysis_context.dart
index b30e1fc..68fb611 100644
--- a/pkg/analyzer/lib/dart/analysis/analysis_context.dart
+++ b/pkg/analyzer/lib/dart/analysis/analysis_context.dart
@@ -38,5 +38,12 @@
 
   /// Return a [Future] that completes after pending file changes are applied,
   /// so that [currentSession] can be used to compute results.
-  Future<void> applyPendingFileChanges();
+  ///
+  /// The value is the set of all files that are potentially affected by
+  /// the pending changes. This set can be both wider than the set of analyzed
+  /// files (because it may include files imported from other packages, and
+  /// which are on the import path from a changed file to an analyze file),
+  /// and narrower than the set of analyzed files (because only files that
+  /// were previously accessed are considered to be known and affected).
+  Future<List<String>> applyPendingFileChanges();
 }
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index bf8129f..562528c 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -145,8 +145,12 @@
   /// The file changes that should be applied before processing requests.
   final List<_FileChange> _pendingFileChanges = [];
 
+  /// When [_applyFileChangesSynchronously] is `true`, affected files are
+  /// accumulated here.
+  Set<String> _accumulatedAffected = {};
+
   /// The completers to complete after [_pendingFileChanges] are applied.
-  final _pendingFileChangesCompleters = <Completer<void>>[];
+  final _pendingFileChangesCompleters = <Completer<List<String>>>[];
 
   /// The mapping from the files for which analysis was requested using
   /// [getResult] to the [Completer]s to report the result.
@@ -453,7 +457,7 @@
     if (file_paths.isDart(resourceProvider.pathContext, path)) {
       _priorityResults.clear();
       if (_applyFileChangesSynchronously) {
-        _removePotentiallyAffectedLibraries(path);
+        _removePotentiallyAffectedLibraries(_accumulatedAffected, path);
         _fileTracker.addFile(path);
       } else {
         _pendingFileChanges.add(
@@ -466,13 +470,22 @@
 
   /// Return a [Future] that completes after pending file changes are applied,
   /// so that [currentSession] can be used to compute results.
-  Future<void> applyPendingFileChanges() {
+  ///
+  /// The value is the set of all files that are potentially affected by
+  /// the pending changes. This set can be both wider than the set of analyzed
+  /// files (because it may include files imported from other packages, and
+  /// which are on the import path from a changed file to an analyze file),
+  /// and narrower than the set of analyzed files (because only files that
+  /// were previously accessed are considered to be known and affected).
+  Future<List<String>> applyPendingFileChanges() {
     if (_pendingFileChanges.isNotEmpty) {
-      var completer = Completer<void>();
+      var completer = Completer<List<String>>();
       _pendingFileChangesCompleters.add(completer);
       return completer.future;
     } else {
-      return Future.value();
+      var accumulatedAffected = _accumulatedAffected.toList();
+      _accumulatedAffected = {};
+      return Future.value(accumulatedAffected);
     }
   }
 
@@ -500,7 +513,7 @@
     if (file_paths.isDart(resourceProvider.pathContext, path)) {
       _priorityResults.clear();
       if (_applyFileChangesSynchronously) {
-        _removePotentiallyAffectedLibraries(path);
+        _removePotentiallyAffectedLibraries(_accumulatedAffected, path);
         _fileTracker.changeFile(path);
       } else {
         _pendingFileChanges.add(
@@ -1386,7 +1399,7 @@
       _lastProducedSignatures.remove(path);
       _priorityResults.clear();
       if (_applyFileChangesSynchronously) {
-        _removePotentiallyAffectedLibraries(path);
+        _removePotentiallyAffectedLibraries(_accumulatedAffected, path);
         _fileTracker.removeFile(path);
       } else {
         _pendingFileChanges.add(
@@ -1464,9 +1477,10 @@
   }
 
   void _applyPendingFileChanges() {
+    var accumulatedAffected = <String>{};
     for (var fileChange in _pendingFileChanges) {
       var path = fileChange.path;
-      _removePotentiallyAffectedLibraries(path);
+      _removePotentiallyAffectedLibraries(accumulatedAffected, path);
       switch (fileChange.kind) {
         case _FileChangeKind.add:
           _fileTracker.addFile(path);
@@ -1485,7 +1499,9 @@
       var completers = _pendingFileChangesCompleters.toList();
       _pendingFileChangesCompleters.clear();
       for (var completer in completers) {
-        completer.complete();
+        completer.complete(
+          accumulatedAffected.toList(),
+        );
       }
     }
   }
@@ -1896,12 +1912,16 @@
         'missing', errorsResult, AnalysisDriverUnitIndexBuilder());
   }
 
-  void _removePotentiallyAffectedLibraries(String path) {
+  void _removePotentiallyAffectedLibraries(
+    Set<String> accumulatedAffected,
+    String path,
+  ) {
     var affected = <FileState>{};
     _fsState.collectAffected(path, affected);
 
     for (var file in affected) {
       file.invalidateLibraryCycle();
+      accumulatedAffected.add(file.path);
     }
 
     _libraryContext?.elementFactory.removeLibraries(
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver_based_analysis_context.dart b/pkg/analyzer/lib/src/dart/analysis/driver_based_analysis_context.dart
index b00038a..79b54c0 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver_based_analysis_context.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver_based_analysis_context.dart
@@ -55,7 +55,7 @@
   }
 
   @override
-  Future<void> applyPendingFileChanges() {
+  Future<List<String>> applyPendingFileChanges() {
     return driver.applyPendingFileChanges();
   }
 }
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index 5c33990..1bfe413 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -790,10 +790,10 @@
     var b = newFile2('/test/lib/b.dart', '''
 import 'a.dart';
 ''');
-    newFile2('/test/lib/c.dart', '''
+    var c = newFile2('/test/lib/c.dart', '''
 import 'b.dart';
 ''');
-    newFile2('/test/lib/d.dart', '''
+    var d = newFile2('/test/lib/d.dart', '''
 import 'c.dart';
 ''');
     newFile2('/test/lib/e.dart', '');
@@ -832,7 +832,8 @@
     // Change `b.dart`, also removes `c.dart` and `d.dart` that import it.
     // But `a.dart` and `d.dart` is not affected.
     driver.changeFile(b.path);
-    await driver.applyPendingFileChanges();
+    var affectedPathList = await driver.applyPendingFileChanges();
+    expect(affectedPathList, unorderedEquals([b.path, c.path, d.path]));
 
     // We have a new session.
     var session2 = driver.currentSession;
@@ -863,10 +864,10 @@
     var a = newFile2('/test/lib/a.dart', '''
 part of 'b.dart';
 ''');
-    newFile2('/test/lib/b.dart', '''
+    var b = newFile2('/test/lib/b.dart', '''
 part 'a.dart';
 ''');
-    newFile2('/test/lib/c.dart', '''
+    var c = newFile2('/test/lib/c.dart', '''
 import 'b.dart';
 ''');
     newFile2('/test/lib/d.dart', '');
@@ -900,7 +901,13 @@
     // Removes `c.dart` that imports `b.dart`.
     // But `d.dart` is not affected.
     driver.changeFile(a.path);
-    await driver.applyPendingFileChanges();
+    var affectedPathList = await driver.applyPendingFileChanges();
+    expect(affectedPathList, unorderedEquals([a.path, b.path, c.path]));
+
+    // We have a new session.
+    var session2 = driver.currentSession;
+    expect(session2, isNot(session1));
+
     driver.assertLoadedLibraryUriSet(
       excluded: [
         'package:test/b.dart',
@@ -911,10 +918,6 @@
       ],
     );
 
-    // We have a new session.
-    var session2 = driver.currentSession;
-    expect(session2, isNot(session1));
-
     // `d.dart` moved to the new session.
     // Invalidated libraries stuck with the old session.
     expect(b_element.session, session1);