analyzer: Make all ContextRootImpl fields final

This includes the `excludedGlobs`, `optionsFile`, and `packagesFile`
fields. In each case of setting the field's value after instantiation,
the code happens _right_ after instantiation, so they can just be
set in the constructor and be final.

Change-Id: If39e8ebd8a52f4bfc81a016bff2f40d00db16a3f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/455862
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
diff --git a/pkg/analysis_server/test/src/plugin/plugin_isolate_test.dart b/pkg/analysis_server/test/src/plugin/plugin_isolate_test.dart
index 61b9ba6..70b33a4 100644
--- a/pkg/analysis_server/test/src/plugin/plugin_isolate_test.dart
+++ b/pkg/analysis_server/test/src/plugin/plugin_isolate_test.dart
@@ -5,6 +5,7 @@
 import 'dart:io' as io;
 
 import 'package:analysis_server/src/plugin/plugin_isolate.dart';
+import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/instrumentation/instrumentation.dart';
 import 'package:analyzer/src/context/packages.dart';
 import 'package:analyzer/src/dart/analysis/context_root.dart';
@@ -52,9 +53,8 @@
   }
 
   void test_addContextRoot() {
-    var contextRoot1 = _newContextRoot('/pkg1');
     var optionsFile = getFile('/pkg1/analysis_options.yaml');
-    contextRoot1.optionsFile = optionsFile;
+    var contextRoot1 = _newContextRoot('/pkg1', optionsFile: optionsFile);
     var session = PluginSession(pluginIsolate);
     var channel = TestServerCommunicationChannel(session);
     pluginIsolate.currentSession = session;
@@ -325,12 +325,13 @@
 }
 
 mixin _ContextRoot on ResourceProviderMixin {
-  ContextRootImpl _newContextRoot(String rootPath) {
+  ContextRootImpl _newContextRoot(String rootPath, {File? optionsFile}) {
     rootPath = convertPath(rootPath);
     return ContextRootImpl(
       resourceProvider,
       resourceProvider.getFolder(rootPath),
       BasicWorkspace.find(resourceProvider, Packages.empty, rootPath),
+      optionsFile: optionsFile,
     );
   }
 }
diff --git a/pkg/analyzer/lib/src/dart/analysis/context_locator.dart b/pkg/analyzer/lib/src/dart/analysis/context_locator.dart
index 5c3382a..6252ec7 100644
--- a/pkg/analyzer/lib/src/dart/analysis/context_locator.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/context_locator.dart
@@ -364,14 +364,18 @@
   }) {
     optionsFile ??= _findDefaultOptionsFile(workspace);
 
-    var root = ContextRootImpl(resourceProvider, rootFolder, workspace);
-    root.packagesFile = packagesFile;
-    root.optionsFile = optionsFile;
+    var root = ContextRootImpl(
+      resourceProvider,
+      rootFolder,
+      workspace,
+      optionsFile: optionsFile,
+      packagesFile: packagesFile,
+    );
     if (optionsFile != null) {
       root.optionsFileMap[rootFolder] = optionsFile;
     }
 
-    root.excludedGlobs = _getExcludedGlobs(optionsFile, workspace);
+    root.excludedGlobs.addAll(_getExcludedGlobs(optionsFile, workspace));
     roots.add(root);
     return root;
   }
@@ -446,13 +450,11 @@
         packagesFile: rootPackagesFile,
         buildGnFile: buildGnFile,
       );
-      var root = ContextRootImpl(resourceProvider, folder, workspace);
-      root.packagesFile = rootPackagesFile;
       // Check for analysis options file in the parent directories, from
       // root folder to the containing root folder. Pick the one closest
       // to the root.
       if (localOptionsFile == null) {
-        var parentFolder = root.root.parent;
+        var parentFolder = folder.parent;
         while (parentFolder != containingRoot.root) {
           localOptionsFile = parentFolder.existingAnalysisOptionsYamlFile;
           if (localOptionsFile != null) {
@@ -461,14 +463,20 @@
           parentFolder = parentFolder.parent;
         }
       }
-      root.optionsFile = localOptionsFile ?? containingRoot.optionsFile;
+      var root = ContextRootImpl(
+        resourceProvider,
+        folder,
+        workspace,
+        optionsFile: localOptionsFile ?? containingRoot.optionsFile,
+        packagesFile: rootPackagesFile,
+      );
       root.included.add(folder);
       containingRoot.excluded.add(folder);
       roots.add(root);
       containingRoot = root;
       containingRootEnabledLegacyPlugins = localEnabledPlugins;
       excludedGlobs = _getExcludedGlobs(root.optionsFile, workspace);
-      root.excludedGlobs = excludedGlobs;
+      root.excludedGlobs.addAll(excludedGlobs);
       usedThisRoot = false;
     }
 
diff --git a/pkg/analyzer/lib/src/dart/analysis/context_root.dart b/pkg/analyzer/lib/src/dart/analysis/context_root.dart
index 4c99ade..30661d3 100644
--- a/pkg/analyzer/lib/src/dart/analysis/context_root.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/context_root.dart
@@ -27,19 +27,25 @@
 
   /// A list of the globs for excluded files that were read from the analysis
   /// options file.
-  List<LocatedGlob> excludedGlobs = [];
+  final List<LocatedGlob> excludedGlobs = [];
 
   @override
-  File? optionsFile;
+  final File? optionsFile;
 
   /// Maintains a mapping of folders to associated analysis options files.
   final Map<Folder, File> optionsFileMap = {};
 
   @override
-  File? packagesFile;
+  final File? packagesFile;
 
   /// Initialize a newly created context root.
-  ContextRootImpl(this.resourceProvider, this.root, this.workspace);
+  ContextRootImpl(
+    this.resourceProvider,
+    this.root,
+    this.workspace, {
+    this.optionsFile,
+    this.packagesFile,
+  });
 
   @override
   Iterable<String> get excludedPaths =>