Issue 44131. Handle .dart_tool/package_config.json file change, re-configure with new Packages.

Bug: https://github.com/dart-lang/sdk/issues/44131
Change-Id: I4c843da4c6cecea5e334ba6b73cffa42be6d80ea
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/171360
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index 5c09be3..c37c64c 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -887,9 +887,9 @@
   }
 
   void _checkForPackagespecUpdate(String path, ContextInfo info) {
-    // Check to see if this is the .packages file for this context and if so,
-    // update the context's source factory.
-    if (pathContext.basename(path) == PACKAGE_SPEC_NAME) {
+    // Check to see if this is `.dart_tool/package_config.json` or `.packages`
+    // file for this context and if so, update the context's source factory.
+    if (_isPackageConfigJsonFilePath(path) || _isDotPackagesFilePath(path)) {
       var driver = info.analysisDriver;
       if (driver == null) {
         // I suspect that this happens as a result of a race condition: server
@@ -1249,6 +1249,9 @@
     if (info.hasDependency(path)) {
       _recomputeFolderDisposition(info);
     }
+
+    _checkForPackagespecUpdate(path, info);
+
     // maybe excluded globally
     if (_isExcluded(path) ||
         _isContainedInDotFolder(info.folder.path, path) ||
@@ -1283,7 +1286,7 @@
                 return;
               }
             }
-            if (_isPackagespec(path)) {
+            if (_isDotPackagesFilePath(path)) {
               // Check for a sibling pubspec.yaml file.
               if (!resourceProvider
                   .getFile(pathContext.join(directoryPath, PUBSPEC_NAME))
@@ -1323,7 +1326,7 @@
                 return;
               }
             }
-            if (_isPackagespec(path)) {
+            if (_isDotPackagesFilePath(path)) {
               // Check for a sibling pubspec.yaml file.
               if (!resourceProvider
                   .getFile(pathContext.join(directoryPath, PUBSPEC_NAME))
@@ -1352,7 +1355,6 @@
           }
         }
     }
-    _checkForPackagespecUpdate(path, info);
     _checkForAnalysisOptionsUpdate(path, info);
     _checkForDataFileUpdate(path, info);
     _checkForPubspecUpdate(path, info);
@@ -1388,6 +1390,10 @@
   /// to specify data-driven fixes.
   bool _isDataFile(String path) => pathContext.basename(path) == dataFileName;
 
+  bool _isDotPackagesFilePath(String path) {
+    return pathContext.basename(path) == PACKAGE_SPEC_NAME;
+  }
+
   /// Returns `true` if the given [path] is excluded by [excludedPaths].
   bool _isExcluded(String path) => _isExcludedBy(excludedPaths, path);
 
@@ -1415,8 +1421,12 @@
 
   bool _isManifest(String path) => pathContext.basename(path) == MANIFEST_NAME;
 
-  bool _isPackagespec(String path) =>
-      pathContext.basename(path) == PACKAGE_SPEC_NAME;
+  bool _isPackageConfigJsonFilePath(String path) {
+    var components = pathContext.split(path);
+    return components.length > 2 &&
+        components[components.length - 1] == 'package_config.json' &&
+        components[components.length - 2] == '.dart_tool';
+  }
 
   bool _isPubspec(String path) => pathContext.basename(path) == PUBSPEC_NAME;
 
@@ -1477,8 +1487,13 @@
     var builder = callbacks.createContextBuilder(info.folder);
     var options = builder.getAnalysisOptions(contextRoot,
         contextRoot: driver.contextRoot);
+    var packages = builder.createPackageMap(contextRoot);
     var factory = builder.createSourceFactory(contextRoot);
-    driver.configure(analysisOptions: options, sourceFactory: factory);
+    driver.configure(
+      analysisOptions: options,
+      packages: packages,
+      sourceFactory: factory,
+    );
     callbacks.analysisOptionsUpdated(driver);
   }
 
diff --git a/pkg/analysis_server/test/context_manager_test.dart b/pkg/analysis_server/test/context_manager_test.dart
index 28ba464..a6b4251 100644
--- a/pkg/analysis_server/test/context_manager_test.dart
+++ b/pkg/analysis_server/test/context_manager_test.dart
@@ -23,6 +23,7 @@
 import 'package:analyzer/src/generated/source_io.dart';
 import 'package:analyzer/src/services/lint.dart';
 import 'package:analyzer/src/test_utilities/mock_sdk.dart';
+import 'package:analyzer/src/test_utilities/package_config_file_builder.dart';
 import 'package:analyzer/src/test_utilities/resource_provider_mixin.dart';
 import 'package:analyzer/src/util/glob.dart';
 import 'package:linter/src/rules.dart';
@@ -1490,6 +1491,34 @@
     });
   }
 
+  Future<void> test_watch_modifyPackageConfigJson() {
+    var packageConfigPath = '$projPath/.dart_tool/package_config.json';
+    var filePath = convertPath('$projPath/bin/main.dart');
+
+    resourceProvider.newFile(packageConfigPath, '');
+    resourceProvider.newFile(filePath, 'library main;');
+
+    manager.setRoots(<String>[projPath], <String>[]);
+
+    var filePaths = callbacks.currentFilePaths;
+    expect(filePaths, hasLength(1));
+    expect(filePaths, contains(filePath));
+    expect(_currentPackageMap, isEmpty);
+
+    // update .dart_tool/package_config.json
+    callbacks.now++;
+    resourceProvider.modifyFile(
+      packageConfigPath,
+      (PackageConfigFileBuilder()..add(name: 'my', rootPath: '../'))
+          .toContent(toUriStr: toUriStr),
+    );
+
+    return pumpEventQueue().then((_) {
+      // verify new package info
+      expect(_currentPackageMap.keys, unorderedEquals(['my']));
+    });
+  }
+
   Future<void> test_watch_modifyPackagespec() {
     var packagesPath = convertPath('$projPath/.packages');
     var filePath = convertPath('$projPath/bin/main.dart');