Pick the most specific workspace from multiple workspace signals

Closes https://github.com/dart-lang/sdk/pull/49237

GitOrigin-RevId: a1db3bebcd4622bc31303781f5c9ba64167a0e6a
Change-Id: Ief3ef8a5c421ff20e817bf27dac96c1fc37da595
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/248020
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/dart/analysis/context_locator.dart b/pkg/analyzer/lib/src/dart/analysis/context_locator.dart
index ade3b53..af049b8 100644
--- a/pkg/analyzer/lib/src/dart/analysis/context_locator.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/context_locator.dart
@@ -378,10 +378,12 @@
     Workspace? workspace;
     workspace = BazelWorkspace.find(resourceProvider, rootPath,
         lookForBuildFileSubstitutes: false);
-    workspace ??= GnWorkspace.find(resourceProvider, rootPath);
-    workspace ??=
-        PackageBuildWorkspace.find(resourceProvider, packages, rootPath);
-    workspace ??= PubWorkspace.find(resourceProvider, packages, rootPath);
+    workspace = _mostSpecificWorkspace(
+        workspace, GnWorkspace.find(resourceProvider, rootPath));
+    workspace = _mostSpecificWorkspace(workspace,
+        PackageBuildWorkspace.find(resourceProvider, packages, rootPath));
+    workspace = _mostSpecificWorkspace(
+        workspace, PubWorkspace.find(resourceProvider, packages, rootPath));
     workspace ??= BasicWorkspace.find(resourceProvider, packages, rootPath);
     return workspace;
   }
@@ -555,6 +557,20 @@
 
     return true;
   }
+
+  /// Pick a workspace with the most specific root. If the root of [first] is
+  /// non-null and is within the root of [second], return [second]. If any of
+  /// [first] and [second] is null, return the other one. If the roots aren't
+  /// within each other, return [first].
+  static Workspace? _mostSpecificWorkspace(
+      Workspace? first, Workspace? second) {
+    if (first == null) return second;
+    if (second == null) return first;
+    if (isWithin(first.root, second.root)) {
+      return second;
+    }
+    return first;
+  }
 }
 
 /// The packages [file] found for the [parent].
diff --git a/pkg/analyzer/test/src/dart/analysis/context_locator_test.dart b/pkg/analyzer/test/src/dart/analysis/context_locator_test.dart
index 9f757d0..70b8c34 100644
--- a/pkg/analyzer/test/src/dart/analysis/context_locator_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/context_locator_test.dart
@@ -290,6 +290,35 @@
     expect(outerRoot.packagesFile, outerPackagesFile);
   }
 
+  void
+      test_locateRoots_multiple_dirAndNestedDir_outerIsBazel_innerConfigurationFiles() {
+    var outerRootFolder = newFolder('/outer');
+    newFile('$outerRootFolder/WORKSPACE', '');
+    newBazelBuildFile('$outerRootFolder', '');
+    var innerRootFolder = newFolder('/outer/examples/inner');
+    var innerOptionsFile = newAnalysisOptionsYamlFile('$innerRootFolder', '');
+    var innerPackagesFile = newPackageConfigJsonFile('$innerRootFolder', '');
+    newPubspecYamlFile('$innerRootFolder', '');
+
+    var roots = contextLocator.locateRoots(
+      includedPaths: [outerRootFolder.path, innerRootFolder.path],
+    );
+    expect(roots, hasLength(2));
+
+    var outerRoot = findRoot(roots, outerRootFolder);
+    expect(outerRoot.includedPaths, unorderedEquals([outerRootFolder.path]));
+    expect(outerRoot.excludedPaths, unorderedEquals([innerRootFolder.path]));
+    expect(outerRoot.optionsFile, isNull);
+    expect(outerRoot.packagesFile, isNull);
+
+    var innerRoot = findRoot(roots, innerRootFolder);
+    expect(innerRoot.workspace.root, equals(innerRootFolder.path));
+    expect(innerRoot.includedPaths, unorderedEquals([innerRootFolder.path]));
+    expect(innerRoot.excludedPaths, isEmpty);
+    expect(innerRoot.optionsFile, innerOptionsFile);
+    expect(innerRoot.packagesFile, innerPackagesFile);
+  }
+
   void test_locateRoots_multiple_dirAndNestedFile_excludedByOptions() {
     var rootPath = convertPath('/home/test');
     var rootFolder = newFolder(rootPath);