Version 2.12.0-52.0.dev

Merge commit '95e4e03f8818bf3fd96e356e5c06080f36308fa0' into 'dev'
diff --git a/pkg/analyzer/lib/src/dart/micro/library_graph.dart b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
index 527a89b..96b4548 100644
--- a/pkg/analyzer/lib/src/dart/micro/library_graph.dart
+++ b/pkg/analyzer/lib/src/dart/micro/library_graph.dart
@@ -2,6 +2,7 @@
 // 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.
 
+import 'dart:collection';
 import 'dart:convert';
 import 'dart:typed_data';
 
@@ -136,6 +137,18 @@
   /// Return the [uri] string.
   String get uriStr => uri.toString();
 
+  /// Recursively traverse imports, exports, and parts to collect all
+  /// files that are accessed.
+  void collectAllReferencedFiles(Set<String> referencedFiles) {
+    var deps = {...importedFiles, ...exportedFiles, ...partedFiles};
+    for (var file in deps) {
+      if (!referencedFiles.contains(file.path)) {
+        referencedFiles.add(file.path);
+        file.collectAllReferencedFiles(referencedFiles);
+      }
+    }
+  }
+
   /// Return the content of the file, the empty string if cannot be read.
   String getContent() {
     try {
@@ -601,10 +614,34 @@
     }
     return source.fullName;
   }
+
+  /// Computes the set of [FileState]'s used/not used to analyze the given
+  /// [files]. Removes the [FileState]'s of the files not used for analysis from
+  /// the cache. Returns the set of unused [FileState]'s.
+  List<FileState> removeUnusedFiles(List<String> files) {
+    var removedFiles = <FileState>[];
+    var unusedFiles = _pathToFile.keys.toSet();
+    var deps = HashSet<String>();
+    for (var path in files) {
+      unusedFiles.remove(path);
+      _pathToFile[path].collectAllReferencedFiles(deps);
+    }
+    for (var path in deps) {
+      unusedFiles.remove(path);
+    }
+    for (var path in unusedFiles) {
+      var file = _pathToFile.remove(path);
+      _uriToFile.remove(file.uri);
+      removedFiles.add(file);
+    }
+    testView.unusedFiles = unusedFiles;
+    return removedFiles;
+  }
 }
 
 class FileSystemStateTestView {
   final List<String> refreshedFiles = [];
+  Set<String> unusedFiles = {};
 }
 
 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 b0e1808..26bed2a 100644
--- a/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
+++ b/pkg/analyzer/lib/src/dart/micro/resolve_file.dart
@@ -304,6 +304,17 @@
     removedCacheIds.clear();
   }
 
+  /// 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].
+  void removeFilesNotNecessaryForAnalysisOf(List<String> files) {
+    var removedFiles = fsState.removeUnusedFiles(files);
+    for (var removedFile in removedFiles) {
+      removedCacheIds.add(removedFile.id);
+    }
+  }
+
   /// The [completionLine] and [completionColumn] are zero based.
   ResolvedUnitResult resolve({
     int completionLine,
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 549701d..61f26ab 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
@@ -569,4 +569,65 @@
       error(CompileTimeErrorCode.URI_DOES_NOT_EXIST, 7, 9),
     ]);
   }
+
+  test_unusedFiles() async {
+    var bPath = '/workspace/dart/aaa/lib/b.dart';
+    var cPath = '/workspace/dart/aaa/lib/c.dart';
+
+    newFile('/workspace/dart/aaa/lib/a.dart', content: r'''
+class A {}
+''');
+
+    newFile(bPath, content: r'''
+import 'a.dart';
+''');
+
+    newFile(cPath, content: r'''
+import 'a.dart';
+''');
+
+    await resolveFile(bPath);
+    await resolveFile(cPath);
+    fileResolver.removeFilesNotNecessaryForAnalysisOf([cPath]);
+    expect(fileResolver.fsState.testView.unusedFiles.contains(bPath), true);
+    expect(fileResolver.fsState.testView.unusedFiles.length, 1);
+  }
+
+  test_unusedFiles_mutilple() async {
+    var dPath = '/workspace/dart/aaa/lib/d.dart';
+    var ePath = '/workspace/dart/aaa/lib/e.dart';
+    var fPath = '/workspace/dart/aaa/lib/f.dart';
+
+    newFile('/workspace/dart/aaa/lib/a.dart', content: r'''
+class A {}
+''');
+
+    newFile('/workspace/dart/aaa/lib/b.dart', content: r'''
+class B {}
+''');
+
+    newFile('/workspace/dart/aaa/lib/c.dart', content: r'''
+class C {}
+''');
+
+    newFile(dPath, content: r'''
+import 'a.dart';
+''');
+
+    newFile(ePath, content: r'''
+import 'a.dart';
+import 'b.dart';
+''');
+
+    newFile(fPath, content: r'''
+import 'c.dart';
+ ''');
+
+    await resolveFile(dPath);
+    await resolveFile(ePath);
+    await resolveFile(fPath);
+    fileResolver.removeFilesNotNecessaryForAnalysisOf([dPath, fPath]);
+    expect(fileResolver.fsState.testView.unusedFiles.contains(ePath), true);
+    expect(fileResolver.fsState.testView.unusedFiles.length, 2);
+  }
 }
diff --git a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
index 7b21c71..136711e 100644
--- a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
@@ -122,7 +122,7 @@
   /// file, and the analysis_options.yaml file, each only if necessary.
   ///
   /// [neededPackages] is a map whose keys are the names of packages that should
-  /// be dependend upon by the package's pubspec, and whose values are the
+  /// be depended upon by the package's pubspec, and whose values are the
   /// minimum required versions of those packages.
   void processPackage(Folder pkgFolder, Map<String, Version> neededPackages) {
     var pubspecFile = pkgFolder.getChildAssumingFile('pubspec.yaml');
@@ -156,7 +156,6 @@
   Future<MigrationState> rerun() async {
     reset();
     var state = await rerunFunction();
-    await state.refresh(_logger);
     return state;
   }
 
diff --git a/pkg/nnbd_migration/lib/src/preview/preview_site.dart b/pkg/nnbd_migration/lib/src/preview/preview_site.dart
index f433446..4fabe98 100644
--- a/pkg/nnbd_migration/lib/src/preview/preview_site.dart
+++ b/pkg/nnbd_migration/lib/src/preview/preview_site.dart
@@ -364,7 +364,9 @@
 
   Future<void> rerunMigration() async {
     migrationState = await rerunFunction();
-    reset();
+    if (!migrationState.hasErrors) {
+      reset();
+    }
   }
 
   void reset() {
diff --git a/tools/VERSION b/tools/VERSION
index ccc77af..76eb55b 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 51
+PRERELEASE 52
 PRERELEASE_PATCH 0
\ No newline at end of file