Version 2.18.0-178.0.dev

Merge commit '5263e15e99d967bd9cec5f8b05fac95224bd768e' into 'dev'
diff --git a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
index 0b899cb..67d6386 100644
--- a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
+++ b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
@@ -151,10 +151,10 @@
     final nameTypeKey = '${item.name}|${item.runtimeType}';
     if (uniqueByName.containsKey(nameTypeKey)) {
       // At the time of writing, there were two duplicated types:
-      // - TextDocumentSyncKind (same defintion in both places)
+      // - TextDocumentSyncKind (same definition in both places)
       // - TextDocumentSyncOptions (first definition is just a subset)
       // If this list grows, consider handling this better - or try to have the
-      // spec updated to be unambigious.
+      // spec updated to be unambiguous.
       print('WARN: More than one definition for $nameTypeKey.');
     }
 
diff --git a/pkg/analyzer/lib/src/dart/micro/library_graph.dart b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
index 137aa84..7fe936e 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
@@ -467,7 +467,6 @@
 
     var unusedPaths = _pathToFile.keys.toSet();
     unusedPaths.removeAll(allReferenced);
-    testView.removedPaths = unusedPaths;
 
     var removedFiles = <FileState>[];
     for (var path in unusedPaths) {
@@ -512,7 +511,6 @@
 
 class FileSystemStateTestView {
   final List<String> partsDiscoveredLibraries = [];
-  Set<String> removedPaths = {};
 }
 
 class FileSystemStateTimer {
diff --git a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
index c99c47f..6e30935 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -116,11 +116,11 @@
   LibraryContext? libraryContext;
 
   /// List of keys for cache elements that are invalidated. Track elements that
-  /// are invalidated during [changeFile]. Used in [releaseAndClearRemovedIds]
+  /// are invalidated during [changeFiles]. Used in [releaseAndClearRemovedIds]
   /// to release the cache items and is then cleared.
   final Set<String> removedCacheKeys = {};
 
-  /// The cache of file results, cleared on [changeFile].
+  /// The cache of file results, cleared on [changeFiles].
   ///
   /// It is used to allow assists and fixes without resolving the same file
   /// multiple times, as we compute more than one assist, or fixes when there
@@ -158,9 +158,19 @@
   /// Update the resolver to reflect the fact that the file with the given
   /// [path] was changed. We need to make sure that when this file, of any file
   /// that directly or indirectly referenced it, is resolved, we used the new
-  /// state of the file. Updates [removedCacheIds] with the ids of the invalidated
+  /// state of the file. Updates [removedCacheKeys] with the ids of the invalidated
   /// items, used in [releaseAndClearRemovedIds] to release the cache items.
+  /// TODO(scheglov) Remove [releaseKeys] when removing [changeFile].
+  @Deprecated('Use changeFiles() instead')
   void changeFile(String path) {
+    changeFiles([path], releaseKeys: false);
+  }
+
+  /// Update the resolver to reflect the fact that the files with the given
+  /// [paths] were changed. For each specified file we need to make sure that
+  /// when the file, of any file that directly or indirectly referenced it,
+  /// is resolved, we use the new state of the file.
+  void changeFiles(List<String> paths, {bool releaseKeys = true}) {
     if (fsState == null) {
       return;
     }
@@ -168,19 +178,23 @@
     // Forget all results, anything is potentially affected.
     cachedResults.clear();
 
-    // Remove this file and all files that transitively depend on it.
-    var removedFiles = <FileState>[];
-    fsState!.changeFile(path, removedFiles);
+    // Remove the specified files and files that transitively depend on it.
+    final removedFiles = <FileState>[];
+    for (final path in paths) {
+      fsState!.changeFile(path, removedFiles);
+    }
 
     // Schedule disposing references to cached unlinked data.
-    for (var removedFile in removedFiles) {
+    for (final removedFile in removedFiles) {
       removedCacheKeys.add(removedFile.unlinkedKey);
     }
 
     // Remove libraries represented by removed files.
     // If we need these libraries later, we will relink and reattach them.
-    if (libraryContext != null) {
-      libraryContext!.remove(removedFiles, removedCacheKeys);
+    libraryContext?.remove(removedFiles, removedCacheKeys);
+
+    if (releaseKeys) {
+      releaseAndClearRemovedIds();
     }
   }
 
@@ -445,12 +459,14 @@
   /// Remove cached [FileState]'s that were not used in the current analysis
   /// session. The list of files analyzed is used to compute the set of unused
   /// [FileState]'s. Adds the cache id's for the removed [FileState]'s to
-  /// [removedCacheIds].
+  /// [removedCacheKeys].
   void removeFilesNotNecessaryForAnalysisOf(List<String> files) {
     var removedFiles = fsState!.removeUnusedFiles(files);
     for (var removedFile in removedFiles) {
       removedCacheKeys.add(removedFile.unlinkedKey);
     }
+    libraryContext?.remove(removedFiles, removedCacheKeys);
+    releaseAndClearRemovedIds();
   }
 
   /// The [completionLine] and [completionColumn] are zero based.
diff --git a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
index fe558a7..1ad2d13 100644
--- a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
+++ b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
@@ -18,13 +18,13 @@
 
 main() {
   defineReflectiveSuite(() {
-    defineReflectiveTests(FileResolver_changeFile_Test);
+    defineReflectiveTests(FileResolver_changeFiles_Test);
     defineReflectiveTests(FileResolverTest);
   });
 }
 
 @reflectiveTest
-class FileResolver_changeFile_Test extends FileResolutionTest {
+class FileResolver_changeFiles_Test extends FileResolutionTest {
   test_changeFile_refreshedFiles() async {
     final a = newFile('$testPackageLibPath/a.dart', r'''
 class A {}
@@ -128,8 +128,7 @@
     assertStateString(state_1);
 
     // Change a.dart, discard data for a.dart and c.dart, but not b.dart
-    fileResolver.changeFile(a.path);
-    fileResolver.releaseAndClearRemovedIds();
+    fileResolver.changeFiles([a.path]);
     assertStateString(r'''
 files
   /sdk/lib/_internal/internal.dart
@@ -294,7 +293,7 @@
 class A {}
 class B {}
 ''');
-    fileResolver.changeFile(a.path);
+    fileResolver.changeFiles([a.path]);
 
     result = await resolveFile(b.path);
     assertErrorsInResolvedUnit(result, []);
@@ -325,7 +324,7 @@
   int foo = 0;
 }
 ''');
-    fileResolver.changeFile(a.path);
+    fileResolver.changeFiles([a.path]);
 
     result = await resolveFile(b.path);
     assertErrorsInResolvedUnit(result, []);
@@ -351,7 +350,7 @@
 
 var b = B(1);
 ''');
-    fileResolver.changeFile(a.path);
+    fileResolver.changeFiles([a.path]);
 
     // Update b.dart, but do not notify the resolver.
     // If we try to read it now, it will throw.
@@ -368,7 +367,7 @@
     }, throwsStateError);
 
     // Notify the resolver about b.dart, it is OK now.
-    fileResolver.changeFile(b.path);
+    fileResolver.changeFiles([b.path]);
     result = await resolveFile(a.path);
     assertErrorsInResolvedUnit(result, []);
   }
@@ -448,8 +447,7 @@
 ''');
 
     // Change b.dart, discard both b.dart and a.dart
-    fileResolver.changeFile(b.path);
-    fileResolver.releaseAndClearRemovedIds();
+    fileResolver.changeFiles([b.path]);
     assertStateString(r'''
 files
   /sdk/lib/_internal/internal.dart
@@ -653,8 +651,7 @@
 ''');
 
     // Should invalidate a.dart, b.dart, c.dart
-    fileResolver.changeFile(b.path);
-    fileResolver.releaseAndClearRemovedIds();
+    fileResolver.changeFiles([b.path]);
     assertStateString(r'''
 files
   /sdk/lib/_internal/internal.dart
@@ -1007,7 +1004,7 @@
     var dartCorePath = a_result.session.uriConverter.uriToPath(
       Uri.parse('dart:core'),
     )!;
-    fileResolver.changeFile(dartCorePath);
+    fileResolver.changeFiles([dartCorePath]);
 
     // Analyze, this will read the element model for `dart:core`.
     // There was a bug that `root::dart:core::dynamic` had no element set.
@@ -1360,7 +1357,7 @@
 
     // Change the file, will be resolved again.
     newFile(testFilePath, 'var a = c;');
-    fileResolver.changeFile(testFile.path);
+    fileResolver.changeFiles([testFile.path]);
     expect((await getTestErrors()).errors, hasLength(1));
     _assertResolvedFiles([testFile]);
   }
@@ -1392,7 +1389,7 @@
     newFile(a.path, r'''
 var a = 4.2;
 ''');
-    fileResolver.changeFile(a.path);
+    fileResolver.changeFiles([a.path]);
     expect((await getTestErrors()).errors, hasLength(1));
     _assertResolvedFiles([testFile]);
   }
@@ -1994,39 +1991,179 @@
   }
 
   test_removeFilesNotNecessaryForAnalysisOf() async {
-    var aPath = convertPath('/workspace/dart/aaa/lib/a.dart');
-    var bPath = convertPath('/workspace/dart/aaa/lib/b.dart');
-    var cPath = convertPath('/workspace/dart/aaa/lib/c.dart');
-
-    newFile(aPath, r'''
-class A {}
-''');
-
-    newFile(bPath, r'''
-import 'a.dart';
-''');
-
-    newFile(cPath, r'''
-import 'a.dart';
-''');
-
-    await resolveFile(bPath);
-    await resolveFile(cPath);
-    fileResolver.removeFilesNotNecessaryForAnalysisOf([cPath]);
-    _assertRemovedPaths(unorderedEquals([bPath]));
-  }
-
-  test_removeFilesNotNecessaryForAnalysisOf_multiple() async {
-    var bPath = convertPath('/workspace/dart/aaa/lib/b.dart');
-    var dPath = convertPath('/workspace/dart/aaa/lib/d.dart');
-    var ePath = convertPath('/workspace/dart/aaa/lib/e.dart');
-    var fPath = convertPath('/workspace/dart/aaa/lib/f.dart');
-
     newFile('/workspace/dart/aaa/lib/a.dart', r'''
 class A {}
 ''');
 
-    newFile(bPath, r'''
+    final b = newFile('/workspace/dart/aaa/lib/b.dart', r'''
+import 'a.dart';
+class B {}
+''');
+
+    final c = newFile('/workspace/dart/aaa/lib/c.dart', r'''
+import 'a.dart';
+class C {}
+''');
+
+    await resolveFile(b.path);
+    await resolveFile(c.path);
+    assertStateString(r'''
+files
+  /sdk/lib/_internal/internal.dart
+    current
+      unlinkedKey: k00
+    unlinkedGet: []
+    unlinkedPut: [k00]
+  /sdk/lib/async/async.dart
+    current
+      unlinkedKey: k01
+    unlinkedGet: []
+    unlinkedPut: [k01]
+  /sdk/lib/async/stream.dart
+    current
+      unlinkedKey: k02
+    unlinkedGet: []
+    unlinkedPut: [k02]
+  /sdk/lib/core/core.dart
+    current
+      unlinkedKey: k03
+    unlinkedGet: []
+    unlinkedPut: [k03]
+  /sdk/lib/math/math.dart
+    current
+      unlinkedKey: k04
+    unlinkedGet: []
+    unlinkedPut: [k04]
+  /workspace/dart/aaa/lib/a.dart
+    current
+      unlinkedKey: k05
+    unlinkedGet: []
+    unlinkedPut: [k05]
+  /workspace/dart/aaa/lib/b.dart
+    current
+      unlinkedKey: k06
+    unlinkedGet: []
+    unlinkedPut: [k06]
+  /workspace/dart/aaa/lib/c.dart
+    current
+      unlinkedKey: k07
+    unlinkedGet: []
+    unlinkedPut: [k07]
+libraryCycles
+  /sdk/lib/_internal/internal.dart /sdk/lib/async/async.dart /sdk/lib/core/core.dart /sdk/lib/math/math.dart
+    current
+      key: k08
+    get: []
+    put: [k08]
+  /workspace/dart/aaa/lib/a.dart
+    current
+      key: k09
+    get: []
+    put: [k09]
+  /workspace/dart/aaa/lib/b.dart
+    current
+      key: k10
+    get: []
+    put: [k10]
+  /workspace/dart/aaa/lib/c.dart
+    current
+      key: k11
+    get: []
+    put: [k11]
+elementFactory
+  hasElement
+    dart:_internal
+    dart:async
+    dart:core
+    dart:math
+    package:dart.aaa/a.dart
+    package:dart.aaa/b.dart
+    package:dart.aaa/c.dart
+byteStore
+  1: [k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k10, k11]
+''');
+
+    fileResolver.removeFilesNotNecessaryForAnalysisOf([c.path]);
+
+    // No data for b.dart anymore.
+    assertStateString(r'''
+files
+  /sdk/lib/_internal/internal.dart
+    current
+      unlinkedKey: k00
+    unlinkedGet: []
+    unlinkedPut: [k00]
+  /sdk/lib/async/async.dart
+    current
+      unlinkedKey: k01
+    unlinkedGet: []
+    unlinkedPut: [k01]
+  /sdk/lib/async/stream.dart
+    current
+      unlinkedKey: k02
+    unlinkedGet: []
+    unlinkedPut: [k02]
+  /sdk/lib/core/core.dart
+    current
+      unlinkedKey: k03
+    unlinkedGet: []
+    unlinkedPut: [k03]
+  /sdk/lib/math/math.dart
+    current
+      unlinkedKey: k04
+    unlinkedGet: []
+    unlinkedPut: [k04]
+  /workspace/dart/aaa/lib/a.dart
+    current
+      unlinkedKey: k05
+    unlinkedGet: []
+    unlinkedPut: [k05]
+  /workspace/dart/aaa/lib/b.dart
+    unlinkedGet: []
+    unlinkedPut: [k06]
+  /workspace/dart/aaa/lib/c.dart
+    current
+      unlinkedKey: k07
+    unlinkedGet: []
+    unlinkedPut: [k07]
+libraryCycles
+  /sdk/lib/_internal/internal.dart /sdk/lib/async/async.dart /sdk/lib/core/core.dart /sdk/lib/math/math.dart
+    current
+      key: k08
+    get: []
+    put: [k08]
+  /workspace/dart/aaa/lib/a.dart
+    current
+      key: k09
+    get: []
+    put: [k09]
+  /workspace/dart/aaa/lib/b.dart
+    get: []
+    put: [k10]
+  /workspace/dart/aaa/lib/c.dart
+    current
+      key: k11
+    get: []
+    put: [k11]
+elementFactory
+  hasElement
+    dart:_internal
+    dart:async
+    dart:core
+    dart:math
+    package:dart.aaa/a.dart
+    package:dart.aaa/c.dart
+byteStore
+  1: [k00, k01, k02, k03, k04, k05, k07, k08, k09, k11]
+''');
+  }
+
+  test_removeFilesNotNecessaryForAnalysisOf_multiple() async {
+    newFile('/workspace/dart/aaa/lib/a.dart', r'''
+class A {}
+''');
+
+    newFile('/workspace/dart/aaa/lib/b.dart', r'''
 class B {}
 ''');
 
@@ -2034,38 +2171,297 @@
 class C {}
 ''');
 
-    newFile(dPath, r'''
+    final d = newFile('/workspace/dart/aaa/lib/d.dart', r'''
 import 'a.dart';
 ''');
 
-    newFile(ePath, r'''
+    final e = newFile('/workspace/dart/aaa/lib/e.dart', r'''
 import 'a.dart';
 import 'b.dart';
 ''');
 
-    newFile(fPath, r'''
+    final f = newFile('/workspace/dart/aaa/lib/f.dart', r'''
 import 'c.dart';
  ''');
 
-    await resolveFile(dPath);
-    await resolveFile(ePath);
-    await resolveFile(fPath);
-    fileResolver.removeFilesNotNecessaryForAnalysisOf([dPath, fPath]);
-    _assertRemovedPaths(unorderedEquals([bPath, ePath]));
+    await resolveFile(d.path);
+    await resolveFile(e.path);
+    await resolveFile(f.path);
+    assertStateString(r'''
+files
+  /sdk/lib/_internal/internal.dart
+    current
+      unlinkedKey: k00
+    unlinkedGet: []
+    unlinkedPut: [k00]
+  /sdk/lib/async/async.dart
+    current
+      unlinkedKey: k01
+    unlinkedGet: []
+    unlinkedPut: [k01]
+  /sdk/lib/async/stream.dart
+    current
+      unlinkedKey: k02
+    unlinkedGet: []
+    unlinkedPut: [k02]
+  /sdk/lib/core/core.dart
+    current
+      unlinkedKey: k03
+    unlinkedGet: []
+    unlinkedPut: [k03]
+  /sdk/lib/math/math.dart
+    current
+      unlinkedKey: k04
+    unlinkedGet: []
+    unlinkedPut: [k04]
+  /workspace/dart/aaa/lib/a.dart
+    current
+      unlinkedKey: k05
+    unlinkedGet: []
+    unlinkedPut: [k05]
+  /workspace/dart/aaa/lib/b.dart
+    current
+      unlinkedKey: k06
+    unlinkedGet: []
+    unlinkedPut: [k06]
+  /workspace/dart/aaa/lib/c.dart
+    current
+      unlinkedKey: k07
+    unlinkedGet: []
+    unlinkedPut: [k07]
+  /workspace/dart/aaa/lib/d.dart
+    current
+      unlinkedKey: k08
+    unlinkedGet: []
+    unlinkedPut: [k08]
+  /workspace/dart/aaa/lib/e.dart
+    current
+      unlinkedKey: k09
+    unlinkedGet: []
+    unlinkedPut: [k09]
+  /workspace/dart/aaa/lib/f.dart
+    current
+      unlinkedKey: k10
+    unlinkedGet: []
+    unlinkedPut: [k10]
+libraryCycles
+  /sdk/lib/_internal/internal.dart /sdk/lib/async/async.dart /sdk/lib/core/core.dart /sdk/lib/math/math.dart
+    current
+      key: k11
+    get: []
+    put: [k11]
+  /workspace/dart/aaa/lib/a.dart
+    current
+      key: k12
+    get: []
+    put: [k12]
+  /workspace/dart/aaa/lib/b.dart
+    current
+      key: k13
+    get: []
+    put: [k13]
+  /workspace/dart/aaa/lib/c.dart
+    current
+      key: k14
+    get: []
+    put: [k14]
+  /workspace/dart/aaa/lib/d.dart
+    current
+      key: k15
+    get: []
+    put: [k15]
+  /workspace/dart/aaa/lib/e.dart
+    current
+      key: k16
+    get: []
+    put: [k16]
+  /workspace/dart/aaa/lib/f.dart
+    current
+      key: k17
+    get: []
+    put: [k17]
+elementFactory
+  hasElement
+    dart:_internal
+    dart:async
+    dart:core
+    dart:math
+    package:dart.aaa/a.dart
+    package:dart.aaa/b.dart
+    package:dart.aaa/c.dart
+    package:dart.aaa/d.dart
+    package:dart.aaa/e.dart
+    package:dart.aaa/f.dart
+byteStore
+  1: [k00, k01, k02, k03, k04, k05, k06, k07, k08, k09, k10, k11, k12, k13, k14, k15, k16, k17]
+''');
+
+    fileResolver.removeFilesNotNecessaryForAnalysisOf([d.path, f.path]);
+    // No data for b.dart and e.dart anymore.
+    assertStateString(r'''
+files
+  /sdk/lib/_internal/internal.dart
+    current
+      unlinkedKey: k00
+    unlinkedGet: []
+    unlinkedPut: [k00]
+  /sdk/lib/async/async.dart
+    current
+      unlinkedKey: k01
+    unlinkedGet: []
+    unlinkedPut: [k01]
+  /sdk/lib/async/stream.dart
+    current
+      unlinkedKey: k02
+    unlinkedGet: []
+    unlinkedPut: [k02]
+  /sdk/lib/core/core.dart
+    current
+      unlinkedKey: k03
+    unlinkedGet: []
+    unlinkedPut: [k03]
+  /sdk/lib/math/math.dart
+    current
+      unlinkedKey: k04
+    unlinkedGet: []
+    unlinkedPut: [k04]
+  /workspace/dart/aaa/lib/a.dart
+    current
+      unlinkedKey: k05
+    unlinkedGet: []
+    unlinkedPut: [k05]
+  /workspace/dart/aaa/lib/b.dart
+    unlinkedGet: []
+    unlinkedPut: [k06]
+  /workspace/dart/aaa/lib/c.dart
+    current
+      unlinkedKey: k07
+    unlinkedGet: []
+    unlinkedPut: [k07]
+  /workspace/dart/aaa/lib/d.dart
+    current
+      unlinkedKey: k08
+    unlinkedGet: []
+    unlinkedPut: [k08]
+  /workspace/dart/aaa/lib/e.dart
+    unlinkedGet: []
+    unlinkedPut: [k09]
+  /workspace/dart/aaa/lib/f.dart
+    current
+      unlinkedKey: k10
+    unlinkedGet: []
+    unlinkedPut: [k10]
+libraryCycles
+  /sdk/lib/_internal/internal.dart /sdk/lib/async/async.dart /sdk/lib/core/core.dart /sdk/lib/math/math.dart
+    current
+      key: k11
+    get: []
+    put: [k11]
+  /workspace/dart/aaa/lib/a.dart
+    current
+      key: k12
+    get: []
+    put: [k12]
+  /workspace/dart/aaa/lib/b.dart
+    get: []
+    put: [k13]
+  /workspace/dart/aaa/lib/c.dart
+    current
+      key: k14
+    get: []
+    put: [k14]
+  /workspace/dart/aaa/lib/d.dart
+    current
+      key: k15
+    get: []
+    put: [k15]
+  /workspace/dart/aaa/lib/e.dart
+    get: []
+    put: [k16]
+  /workspace/dart/aaa/lib/f.dart
+    current
+      key: k17
+    get: []
+    put: [k17]
+elementFactory
+  hasElement
+    dart:_internal
+    dart:async
+    dart:core
+    dart:math
+    package:dart.aaa/a.dart
+    package:dart.aaa/c.dart
+    package:dart.aaa/d.dart
+    package:dart.aaa/f.dart
+byteStore
+  1: [k00, k01, k02, k03, k04, k05, k07, k08, k10, k11, k12, k14, k15, k17]
+''');
   }
 
   test_removeFilesNotNecessaryForAnalysisOf_unknown() async {
-    var aPath = convertPath('/workspace/dart/aaa/lib/a.dart');
-    var bPath = convertPath('/workspace/dart/aaa/lib/b.dart');
-
-    newFile(aPath, r'''
+    final a = newFile('/workspace/dart/aaa/lib/a.dart', r'''
 class A {}
 ''');
 
-    await resolveFile(aPath);
+    final b = getFile('/workspace/dart/aaa/lib/b.dart');
 
-    fileResolver.removeFilesNotNecessaryForAnalysisOf([aPath, bPath]);
-    _assertRemovedPaths(isEmpty);
+    await resolveFile(a.path);
+    fileResolver.removeFilesNotNecessaryForAnalysisOf([a.path, b.path]);
+
+    // No b.dart anywhere.
+    assertStateString(r'''
+files
+  /sdk/lib/_internal/internal.dart
+    current
+      unlinkedKey: k00
+    unlinkedGet: []
+    unlinkedPut: [k00]
+  /sdk/lib/async/async.dart
+    current
+      unlinkedKey: k01
+    unlinkedGet: []
+    unlinkedPut: [k01]
+  /sdk/lib/async/stream.dart
+    current
+      unlinkedKey: k02
+    unlinkedGet: []
+    unlinkedPut: [k02]
+  /sdk/lib/core/core.dart
+    current
+      unlinkedKey: k03
+    unlinkedGet: []
+    unlinkedPut: [k03]
+  /sdk/lib/math/math.dart
+    current
+      unlinkedKey: k04
+    unlinkedGet: []
+    unlinkedPut: [k04]
+  /workspace/dart/aaa/lib/a.dart
+    current
+      unlinkedKey: k05
+    unlinkedGet: []
+    unlinkedPut: [k05]
+libraryCycles
+  /sdk/lib/_internal/internal.dart /sdk/lib/async/async.dart /sdk/lib/core/core.dart /sdk/lib/math/math.dart
+    current
+      key: k06
+    get: []
+    put: [k06]
+  /workspace/dart/aaa/lib/a.dart
+    current
+      key: k07
+    get: []
+    put: [k07]
+elementFactory
+  hasElement
+    dart:_internal
+    dart:async
+    dart:core
+    dart:math
+    package:dart.aaa/a.dart
+byteStore
+  1: [k00, k01, k02, k03, k04, k05, k06, k07]
+''');
   }
 
   test_resolve_libraryWithPart_noLibraryDiscovery() async {
@@ -2155,7 +2551,7 @@
 
     // Change a file.
     var a_path = convertPath('/workspace/dart/test/lib/a.dart');
-    fileResolver.changeFile(a_path);
+    fileResolver.changeFiles([a_path]);
 
     // The was a change to a file, no matter which, resolve again.
     await resolveFile2(testFile.path);
@@ -2307,10 +2703,6 @@
     expect(fileResolver.fsState!.testView.partsDiscoveredLibraries, expected);
   }
 
-  void _assertRemovedPaths(Matcher matcher) {
-    expect(fileResolver.fsState!.testView.removedPaths, matcher);
-  }
-
   void _assertResolvedFiles(
     List<File> expected, {
     bool andClear = true,
diff --git a/pkg/front_end/test/flutter_gallery_leak_tester.dart b/pkg/front_end/test/flutter_gallery_leak_tester.dart
index d7e9630..ff245b5 100644
--- a/pkg/front_end/test/flutter_gallery_leak_tester.dart
+++ b/pkg/front_end/test/flutter_gallery_leak_tester.dart
@@ -334,7 +334,7 @@
     /* expression */ 'WidgetsBinding.instance.debugDidSendFirstFrameEvent',
     /* no definitions */
     /* <boundarykey> */ inputKey,
-    /* no type-defintions */
+    /* no type-definitions */
     /* <boundarykey> */ inputKey,
     /* libraryUri */ 'package:flutter/src/widgets/binding.dart',
     /* class */ '',
@@ -350,7 +350,7 @@
         'arg1, "dummy_68")',
     /* definition #1 */ 'arg1',
     /* <boundarykey> */ inputKey,
-    /* no type-defintions */
+    /* no type-definitions */
     /* <boundarykey> */ inputKey,
     /* libraryUri */ 'package:flutter/src/widgets/widget_inspector.dart',
     /* class */ '',
@@ -366,7 +366,7 @@
         '"inspector-836", "tree_112")',
     /* no definitions */
     /* <boundarykey> */ inputKey,
-    /* no type-defintions */
+    /* no type-definitions */
     /* <boundarykey> */ inputKey,
     /* libraryUri */ 'package:flutter/src/widgets/widget_inspector.dart',
     /* class */ '',
@@ -382,7 +382,7 @@
         'toObjectForSourceLocation("inspector-607", "tree_112")',
     /* no definitions */
     /* <boundarykey> */ inputKey,
-    /* no type-defintions */
+    /* no type-definitions */
     /* <boundarykey> */ inputKey,
     /* libraryUri */ 'package:flutter/src/widgets/widget_inspector.dart',
     /* class */ '',
diff --git a/pkg/front_end/test/spell_checking_list_tests.txt b/pkg/front_end/test/spell_checking_list_tests.txt
index 0b2a65c..11859bf 100644
--- a/pkg/front_end/test/spell_checking_list_tests.txt
+++ b/pkg/front_end/test/spell_checking_list_tests.txt
@@ -182,7 +182,6 @@
 decrease
 decrements
 dectcem
-defintions
 deleting
 denylist
 depended
diff --git a/pkg/frontend_server/lib/frontend_server.dart b/pkg/frontend_server/lib/frontend_server.dart
index 8bd5bbb..331ec7a 100644
--- a/pkg/frontend_server/lib/frontend_server.dart
+++ b/pkg/frontend_server/lib/frontend_server.dart
@@ -1242,7 +1242,7 @@
           // definitions (one per line)
           // ...
           // <boundarykey>
-          // type-defintions (one per line)
+          // type-definitions (one per line)
           // ...
           // <boundarykey>
           // <libraryUri: String>
diff --git a/pkg/frontend_server/test/frontend_server_test.dart b/pkg/frontend_server/test/frontend_server_test.dart
index 4c7fca1..12004ad 100644
--- a/pkg/frontend_server/test/frontend_server_test.dart
+++ b/pkg/frontend_server/test/frontend_server_test.dart
@@ -554,7 +554,7 @@
           // definitions (one per line)
           // ...
           // <boundarykey>
-          // type-defintions (one per line)
+          // type-definitions (one per line)
           // ...
           // <boundarykey>
           // <libraryUri: String>
@@ -644,7 +644,7 @@
           // definitions (one per line)
           // ...
           // <boundarykey>
-          // type-defintions (one per line)
+          // type-definitions (one per line)
           // ...
           // <boundarykey>
           // <libraryUri: String>
@@ -871,7 +871,7 @@
           // definitions (one per line)
           // ...
           // <boundarykey>
-          // type-defintions (one per line)
+          // type-definitions (one per line)
           // ...
           // <boundarykey>
           // <libraryUri: String>
@@ -2799,7 +2799,7 @@
           // definitions (one per line)
           // ...
           // <boundarykey>
-          // type-defintions (one per line)
+          // type-definitions (one per line)
           // ...
           // <boundarykey>
           // <libraryUri: String>
diff --git a/runtime/vm/compiler/backend/flow_graph_checker.cc b/runtime/vm/compiler/backend/flow_graph_checker.cc
index 4fa26ee..a106088 100644
--- a/runtime/vm/compiler/backend/flow_graph_checker.cc
+++ b/runtime/vm/compiler/backend/flow_graph_checker.cc
@@ -147,7 +147,7 @@
                                            ->definition(),
                 call);
       } else {
-        // Redefintion instructions and boxing/unboxing are inserted
+        // Redefinition instructions and boxing/unboxing are inserted
         // without updating environment uses (FlowGraph::RenameDominatedUses,
         // FlowGraph::InsertConversionsFor).
         // Also, constants may belong to different blocks (e.g. function entry
diff --git a/runtime/vm/debugger_api_impl_test.cc b/runtime/vm/debugger_api_impl_test.cc
index 3f048319..edb1440 100644
--- a/runtime/vm/debugger_api_impl_test.cc
+++ b/runtime/vm/debugger_api_impl_test.cc
@@ -182,7 +182,7 @@
             expr.ToCString(),
             /* definitions= */ Array::empty_array(),
             /* definition_types= */ Array::empty_array(),
-            /* type_defintions= */ Array::empty_array(),
+            /* type_definitions= */ Array::empty_array(),
             /* type_bounds= */ Array::empty_array(),
             /* type_defaults= */ Array::empty_array(),
             String::Handle(lib.url()).ToCString(),
diff --git a/tests/language/extension_methods/static_extension_internal_resolution_4_error_test.dart b/tests/language/extension_methods/static_extension_internal_resolution_4_error_test.dart
index 6f6944c..dd09d15 100644
--- a/tests/language/extension_methods/static_extension_internal_resolution_4_error_test.dart
+++ b/tests/language/extension_methods/static_extension_internal_resolution_4_error_test.dart
@@ -118,7 +118,7 @@
       // No errors: see static_extension_internal_resolution_4_test.dart
     }
 
-    // Extension members are ambigious.
+    // Extension members are ambiguous.
     {
       bool t0 = this.fieldInExtensionScope;
       //             ^^^^^^^^^^^^^^^^^^^^^
@@ -180,7 +180,7 @@
       // No errors: see static_extension_internal_resolution_4_test.dart
     }
 
-    // Extension members are ambigious.
+    // Extension members are ambiguous.
     {
       bool t0 = self.fieldInExtensionScope;
       //             ^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/language/extension_methods/static_extension_internal_resolution_4_test.dart b/tests/language/extension_methods/static_extension_internal_resolution_4_test.dart
index 590a359..ad56f70 100644
--- a/tests/language/extension_methods/static_extension_internal_resolution_4_test.dart
+++ b/tests/language/extension_methods/static_extension_internal_resolution_4_test.dart
@@ -130,7 +130,7 @@
       checkInstanceValue(t0);
     }
 
-    // Extension members are ambigious.
+    // Extension members are ambiguous.
     {
       // Error cases tested in static_extension_internal_resolution_4_error_test.dart
     }
@@ -168,7 +168,7 @@
       checkInstanceValue(t0);
     }
 
-    // Extension members are ambigious.
+    // Extension members are ambiguous.
     {
       // Error cases tested in static_extension_internal_resolution_4_error_test.dart
     }
@@ -219,7 +219,7 @@
       checkInstanceValue(t0);
     }
 
-    // Extension members are ambigious.
+    // Extension members are ambiguous.
     {
       // Error cases tested in static_extension_internal_resolution_4_error_test.dart
     }
@@ -263,7 +263,7 @@
     checkInstanceValue(t2);
   }
 
-  // Extension members are ambigious.
+  // Extension members are ambiguous.
   {
     // Error cases tested in static_extension_internal_resolution_4_error_test.dart
   }
diff --git a/tests/language_2/extension_methods/static_extension_internal_resolution_4_error_test.dart b/tests/language_2/extension_methods/static_extension_internal_resolution_4_error_test.dart
index f880ded..5acc485 100644
--- a/tests/language_2/extension_methods/static_extension_internal_resolution_4_error_test.dart
+++ b/tests/language_2/extension_methods/static_extension_internal_resolution_4_error_test.dart
@@ -120,7 +120,7 @@
       // No errors: see static_extension_internal_resolution_4_test.dart
     }
 
-    // Extension members are ambigious.
+    // Extension members are ambiguous.
     {
       bool t0 = this.fieldInExtensionScope;
       //             ^^^^^^^^^^^^^^^^^^^^^
@@ -182,7 +182,7 @@
       // No errors: see static_extension_internal_resolution_4_test.dart
     }
 
-    // Extension members are ambigious.
+    // Extension members are ambiguous.
     {
       bool t0 = self.fieldInExtensionScope;
       //             ^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/language_2/extension_methods/static_extension_internal_resolution_4_test.dart b/tests/language_2/extension_methods/static_extension_internal_resolution_4_test.dart
index 70e17ea..430b6f5 100644
--- a/tests/language_2/extension_methods/static_extension_internal_resolution_4_test.dart
+++ b/tests/language_2/extension_methods/static_extension_internal_resolution_4_test.dart
@@ -132,7 +132,7 @@
       checkInstanceValue(t0);
     }
 
-    // Extension members are ambigious.
+    // Extension members are ambiguous.
     {
       // Error cases tested in static_extension_internal_resolution_4_error_test.dart
     }
@@ -170,7 +170,7 @@
       checkInstanceValue(t0);
     }
 
-    // Extension members are ambigious.
+    // Extension members are ambiguous.
     {
       // Error cases tested in static_extension_internal_resolution_4_error_test.dart
     }
@@ -221,7 +221,7 @@
       checkInstanceValue(t0);
     }
 
-    // Extension members are ambigious.
+    // Extension members are ambiguous.
     {
       // Error cases tested in static_extension_internal_resolution_4_error_test.dart
     }
@@ -265,7 +265,7 @@
     checkInstanceValue(t2);
   }
 
-  // Extension members are ambigious.
+  // Extension members are ambiguous.
   {
     // Error cases tested in static_extension_internal_resolution_4_error_test.dart
   }
diff --git a/tools/VERSION b/tools/VERSION
index 4ffdc59..abce37d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 18
 PATCH 0
-PRERELEASE 177
+PRERELEASE 178
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/lib/src/firestore.dart b/tools/bots/lib/src/firestore.dart
index aed2175..882c3dd 100644
--- a/tools/bots/lib/src/firestore.dart
+++ b/tools/bots/lib/src/firestore.dart
@@ -2,8 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'dart:convert' show jsonDecode, jsonEncode;
 import 'dart:io' show File, HttpStatus;
 
@@ -28,11 +26,11 @@
   final Uri _commitUrl;
 
   /// The current transaction ID in base64 (or `null`)
-  String _currentTransaction;
+  String? _currentTransaction;
 
   /// Returns the current transaction escaped to be useable as part of a URI.
   String get _escapedCurrentTransaction {
-    return Uri.encodeFull(_currentTransaction)
+    return Uri.encodeFull(_currentTransaction!)
         // The Firestore API does not accept '+' in URIs
         .replaceAll("+", "%2B");
   }
@@ -117,7 +115,7 @@
     }
   }
 
-  Future<bool> commit([List<Write> writes]) async {
+  Future<bool> commit({required List<Write> writes}) async {
     if (_currentTransaction == null) {
       throw Exception('"commit" called without transaction');
     }
@@ -156,7 +154,7 @@
 class Update implements Write {
   @override
   final Map data;
-  Update(List<String> updateMask, Map document, {String updateTime})
+  Update(List<String> updateMask, Map document, {String? updateTime})
       : data = {
           if (updateTime != null) "currentDocument": {"updateTime": updateTime},
           "updateMask": {"fieldPaths": updateMask},
@@ -166,7 +164,7 @@
 
 class Query {
   final Map data;
-  Query(String collection, Filter filter, {int limit})
+  Query(String collection, Filter filter, {int? limit})
       : data = {
           'structuredQuery': {
             'from': [
diff --git a/tools/bots/update_blamelists.dart b/tools/bots/update_blamelists.dart
index 5488856..0776afc 100644
--- a/tools/bots/update_blamelists.dart
+++ b/tools/bots/update_blamelists.dart
@@ -141,7 +141,7 @@
       }
       result.blamelistStartIndex = newStartIndex;
       var updateIndex = Update(['blamelist_start_index'], result.data);
-      if (!await database.commit([updateIndex])) {
+      if (!await database.commit(writes: [updateIndex])) {
         // Commiting the change to the database had a conflict, retry.
         needsRetry = true;
         if (++attempts == maxAttempts) {
diff --git a/tools/diff_results.dart b/tools/diff_results.dart
index e00f65b..c2baeec 100644
--- a/tools/diff_results.dart
+++ b/tools/diff_results.dart
@@ -3,8 +3,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// @dart = 2.9
-
 import 'dart:async';
 import 'dart:convert';
 import 'dart:io';
@@ -29,7 +27,7 @@
 ${parser.usage}""");
 }
 
-bool verbose;
+late bool verbose;
 
 main(List<String> args) async {
   final options = parser.parse(args);
@@ -261,23 +259,23 @@
 }
 
 class Diff {
-  final Result before;
-  final Result after;
+  final Result? before;
+  final Result? after;
 
   Diff(this.before, this.after);
 
-  String get test => before?.name ?? after?.name;
-  String get builder => before?.builderName ?? after?.builderName;
+  String get test => (before?.name ?? after?.name)!;
+  String get builder => (before?.builderName ?? after?.builderName)!;
 
   bool sameExpectationDifferenceAs(Diff other) {
     if ((before == null) != (other.before == null)) return false;
     if ((after == null) != (other.after == null)) return false;
 
     if (before != null) {
-      if (!before.sameResult(other.before)) return false;
+      if (!before!.sameResult(other.before!)) return false;
     }
     if (after != null) {
-      if (!after.sameResult(other.after)) return false;
+      if (!after!.sameResult(other.after!)) return false;
     }
     return true;
   }
@@ -315,6 +313,7 @@
   int get hashCode => name.hashCode ^ builderName.hashCode;
 
   @override
+  // ignore: unnecessary_overrides
   bool operator ==(Object other) {
     // TODO: implement ==
     return super == other;
diff --git a/tools/generate_package_config.dart b/tools/generate_package_config.dart
index 536a5b6..7771bf7 100644
--- a/tools/generate_package_config.dart
+++ b/tools/generate_package_config.dart
@@ -16,21 +16,18 @@
 void main(List<String> args) {
   var packageDirs = [
     ...listSubdirectories(platform('pkg')),
-    ...listSubdirectories(platform('third_party/pkg_tested')),
     ...listSubdirectories(platform('third_party/pkg')),
-    ...listSubdirectories(platform('third_party/pkg/file/packages')),
-    ...listSubdirectories(platform('third_party/pkg/test/pkgs')),
-    ...listSubdirectories(platform('third_party/pkg/shelf/pkgs')),
+    ...listSubdirectories(platform('third_party/pkg_tested')),
     platform('pkg/vm_service/test/test_package'),
-    platform('runtime/observatory_2'),
     platform(
         'runtime/observatory_2/tests/service_2/observatory_test_package_2'),
     platform('runtime/observatory'),
     platform('runtime/observatory/tests/service/observatory_test_package'),
+    platform('runtime/observatory_2'),
     platform('sdk/lib/_internal/sdk_library_metadata'),
     platform('third_party/devtools/devtools_shared'),
-    platform('third_party/pkg/protobuf/protobuf'),
-    platform('third_party/pkg/webdev/frontend_server_client'),
+    // Explicitly add package:file (shadowed by //pubspec.yaml).
+    platform('third_party/pkg/file/packages/file'),
     platform('tools/package_deps'),
   ];
 
@@ -170,13 +167,23 @@
   }
 }
 
-/// Finds the paths of the immediate subdirectories of [dir] that
-/// contain pubspecs.
-Iterable<String> listSubdirectories(String dir) sync* {
-  for (var entry in Directory(join(repoRoot, dir)).listSync()) {
-    if (entry is! Directory) continue;
-    if (!File(join(entry.path, 'pubspec.yaml')).existsSync()) continue;
-    yield join(dir, basename(entry.path));
+/// Finds the paths of the subdirectories of [dirPath] that contain pubspecs.
+///
+/// This method recurses until it finds a pubspec.yaml file.
+Iterable<String> listSubdirectories(String parentPath) sync* {
+  final parent = Directory(join(repoRoot, parentPath));
+
+  for (var child in parent.listSync().whereType<Directory>()) {
+    var name = basename(child.path);
+
+    // Don't recurse into dot directories.
+    if (name.startsWith('.')) continue;
+
+    if (File(join(child.path, 'pubspec.yaml')).existsSync()) {
+      yield join(parentPath, name);
+    } else {
+      yield* listSubdirectories(join(parentPath, name));
+    }
   }
 }
 
diff --git a/tools/line_doc_comments.dart b/tools/line_doc_comments.dart
index e31109d..b4a5c92 100755
--- a/tools/line_doc_comments.dart
+++ b/tools/line_doc_comments.dart
@@ -2,7 +2,6 @@
 
 /// Converts block-style Doc comments in Dart code to line style.
 
-// @dart = 2.9
 library line_doc_comments;
 
 import 'dart:io';
@@ -46,10 +45,10 @@
   var buffer = StringBuffer();
   var linesOut = 0;
   var inBlock = false;
-  String indent;
+  String? indent;
 
-  for (var line in lines) {
-    var oldLine = line;
+  for (String? line in lines) {
+    var oldLine = line!;
     if (inBlock) {
       // See if it's the end of the comment.
       if (endBlock.hasMatch(line)) {
@@ -72,7 +71,7 @@
       // See if it's a one-line block comment like: /** Blah. */
       var match = oneLineBlock.firstMatch(line);
       if (match != null) {
-        var comment = match[2];
+        var comment = match[2]!;
         if (comment != '') {
           // Remove the extra space before the `*/`
           if (comment.endsWith(' ')) {
diff --git a/tools/validate_test_matrix.dart b/tools/validate_test_matrix.dart
index ce4e1f1..3f36bae 100644
--- a/tools/validate_test_matrix.dart
+++ b/tools/validate_test_matrix.dart
@@ -4,8 +4,6 @@
 
 // Test that the test matrix in the SDK can be parsed correctly.
 
-// @dart = 2.9
-
 import 'dart:convert' show jsonDecode;
 import 'dart:io' show File, Platform;