Version 2.12.0-1.0.dev

Merge commit '9ca05a40927142d3512b72055b72fccd7a1cc592' into 'dev'
diff --git a/pkg/analysis_server/lib/src/computer/import_elements_computer.dart b/pkg/analysis_server/lib/src/computer/import_elements_computer.dart
index 8dc38a7..7f5a9a4 100644
--- a/pkg/analysis_server/lib/src/computer/import_elements_computer.dart
+++ b/pkg/analysis_server/lib/src/computer/import_elements_computer.dart
@@ -325,7 +325,7 @@
     var scope = libraryResult.libraryElement.scope;
 
     if (prefix.isNotEmpty) {
-      var prefixElement = scope.lookup2(prefix).getter;
+      var prefixElement = scope.lookup(prefix).getter;
       if (prefixElement is PrefixElement) {
         scope = prefixElement.scope;
       } else {
@@ -333,7 +333,7 @@
       }
     }
 
-    var lookupResult = scope.lookup2(name);
+    var lookupResult = scope.lookup(name);
     return lookupResult.getter != null || lookupResult.setter != null;
   }
 
diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart
index 74b77aa..c31877f 100644
--- a/pkg/dartdev/lib/dartdev.dart
+++ b/pkg/dartdev/lib/dartdev.dart
@@ -2,7 +2,8 @@
 // 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:io' as io;
+// Do not call exit() directly. Use VmInteropHandler.exit() instead.
+import 'dart:io' as io hide exit;
 import 'dart:isolate';
 
 import 'package:analyzer/src/dart/analysis/experiments.dart';
@@ -29,8 +30,7 @@
 import 'src/vm_interop_handler.dart';
 
 /// This is typically called from bin/, but given the length of the method and
-/// analytics logic, it has been moved here. Also note that this method calls
-/// [io.exit(code)] directly.
+/// analytics logic, it has been moved here.
 Future<void> runDartdev(List<String> args, SendPort port) async {
   VmInteropHandler.initialize(port);
 
@@ -69,13 +69,15 @@
 
     // Alert the user that analytics has been disabled.
     print(analyticsDisabledNoticeMessage);
-    io.exit(0);
+    VmInteropHandler.exit(0);
+    return;
   } else if (args.contains('--enable-analytics')) {
     analytics.enabled = true;
 
     // Alert the user again that anonymous data will be collected.
     print(analyticsNoticeOnFirstRunMessage);
-    io.exit(0);
+    VmInteropHandler.exit(0);
+    return;
   }
 
   try {
diff --git a/pkg/nnbd_migration/lib/migration_cli.dart b/pkg/nnbd_migration/lib/migration_cli.dart
index 34e70b7..d712d7b 100644
--- a/pkg/nnbd_migration/lib/migration_cli.dart
+++ b/pkg/nnbd_migration/lib/migration_cli.dart
@@ -314,11 +314,8 @@
         CommandLineOptions.skipPubOutdatedFlag,
         (parser, hide) => parser.addFlag(
               CommandLineOptions.skipPubOutdatedFlag,
-              // TODO(srawlins): Before "beta," change the default to "false," and
-              // negatable to "false." See
-              // https://github.com/dart-lang/sdk/issues/43774.
-              defaultsTo: true,
-              negatable: true,
+              defaultsTo: false,
+              negatable: false,
               help:
                   'Skip the `pub outdated --mode=null-safety` check. This allows a '
                   'migration to proceed even if some package dependencies have not yet '
diff --git a/pkg/nnbd_migration/lib/nnbd_migration.dart b/pkg/nnbd_migration/lib/nnbd_migration.dart
index 7e9d2f8..087685d 100644
--- a/pkg/nnbd_migration/lib/nnbd_migration.dart
+++ b/pkg/nnbd_migration/lib/nnbd_migration.dart
@@ -117,6 +117,12 @@
     kind: NullabilityFixKind.downcastExpression,
   );
 
+  /// Informative message: there is no valid migration for `null` in a
+  /// non-nullable context.
+  static const noValidMigrationForNull = NullabilityFixDescription._(
+      appliedMessage: 'No valid migration for `null` in a non-nullable context',
+      kind: NullabilityFixKind.noValidMigrationForNull);
+
   /// Informative message: a null-aware access won't be necessary in strong
   /// checking mode.
   static const nullAwarenessUnnecessaryInStrongMode =
@@ -276,6 +282,7 @@
   downcastExpression,
   makeTypeNullable,
   makeTypeNullableDueToHint,
+  noValidMigrationForNull,
   nullAwarenessUnnecessaryInStrongMode,
   nullAwareAssignmentUnnecessaryInStrongMode,
   otherCastExpression,
diff --git a/pkg/nnbd_migration/lib/src/fix_aggregator.dart b/pkg/nnbd_migration/lib/src/fix_aggregator.dart
index f6a9372..b006ba4 100644
--- a/pkg/nnbd_migration/lib/src/fix_aggregator.dart
+++ b/pkg/nnbd_migration/lib/src/fix_aggregator.dart
@@ -684,6 +684,10 @@
 /// Implementation of [NodeChange] specialized for operating on [Expression]
 /// nodes.
 class NodeChangeForExpression<N extends Expression> extends NodeChange<N> {
+  bool _addsNoValidMigration = false;
+
+  AtomicEditInfo _addNoValidMigrationInfo;
+
   bool _addsNullCheck = false;
 
   AtomicEditInfo _addNullCheckInfo;
@@ -696,9 +700,15 @@
 
   NodeChangeForExpression() : super._();
 
+  /// Gets the info for any added "no valid migration" comment.
+  AtomicEditInfo get addNoValidMigrationInfo => _addNoValidMigrationInfo;
+
   /// Gets the info for any added null check.
   AtomicEditInfo get addNullCheckInfo => _addNullCheckInfo;
 
+  /// Indicates whether [addNoValidMigration] has been called.
+  bool get addsNoValidMigration => _addsNoValidMigration;
+
   /// Indicates whether [addNullCheck] has been called.
   bool get addsNullCheck => _addsNullCheck;
 
@@ -711,10 +721,17 @@
 
   @override
   Iterable<String> get _toStringParts => [
+        if (_addsNoValidMigration) 'addsNoValidMigration',
         if (_addsNullCheck) 'addsNullCheck',
         if (_introducesAsType != null) 'introducesAsType'
       ];
 
+  void addNoValidMigration(AtomicEditInfo info) {
+    assert(!_addsNoValidMigration);
+    _addsNoValidMigration = true;
+    _addNoValidMigrationInfo = info;
+  }
+
   /// Causes a null check to be added to this expression, with the given [info].
   void addNullCheck(AtomicEditInfo info, {HintComment hint}) {
     assert(!_addsNullCheck);
@@ -754,6 +771,11 @@
             .addUnaryPostfix(plan, TokenType.BANG, info: _addNullCheckInfo);
       }
     }
+    if (_addsNoValidMigration) {
+      plan = aggregator.planner.addCommentPostfix(
+          plan, '/* no valid migration */',
+          info: _addNoValidMigrationInfo, isInformative: true);
+    }
     if (_introducesAsType != null) {
       plan = aggregator.planner.addBinaryPostfix(
           plan, TokenType.AS, aggregator.typeToCode(_introducesAsType),
diff --git a/pkg/nnbd_migration/lib/src/fix_builder.dart b/pkg/nnbd_migration/lib/src/fix_builder.dart
index b3e2698..3699d66 100644
--- a/pkg/nnbd_migration/lib/src/fix_builder.dart
+++ b/pkg/nnbd_migration/lib/src/fix_builder.dart
@@ -573,12 +573,21 @@
       {AtomicEditInfo info, HintComment hint}) {
     var checks =
         _fixBuilder._variables.expressionChecks(_fixBuilder.source, node);
+    bool noValidMigration = node is NullLiteral && hint == null;
     info ??= checks != null
         ? AtomicEditInfo(
-            NullabilityFixDescription.checkExpression, checks.edges)
+            noValidMigration
+                ? NullabilityFixDescription.noValidMigrationForNull
+                : NullabilityFixDescription.checkExpression,
+            checks.edges)
         : null;
-    (_fixBuilder._getChange(node) as NodeChangeForExpression)
-        .addNullCheck(info, hint: hint);
+    var nodeChangeForExpression =
+        _fixBuilder._getChange(node) as NodeChangeForExpression;
+    if (noValidMigration) {
+      nodeChangeForExpression.addNoValidMigration(info);
+    } else {
+      nodeChangeForExpression.addNullCheck(info, hint: hint);
+    }
     _flowAnalysis.nonNullAssert_end(node);
     return _fixBuilder._typeSystem.promoteToNonNull(type as TypeImpl);
   }
diff --git a/pkg/nnbd_migration/lib/src/front_end/info_builder.dart b/pkg/nnbd_migration/lib/src/front_end/info_builder.dart
index 01a0d05..2f9f0fb 100644
--- a/pkg/nnbd_migration/lib/src/front_end/info_builder.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/info_builder.dart
@@ -211,6 +211,10 @@
         // their "OrNull" equivalents.  We don't offer any hints around
         // this transformation.
         break;
+      case NullabilityFixKind.noValidMigrationForNull:
+        // We don't offer any edits around unmigratable `null`s.  The user has
+        // to fix manually.
+        break;
     }
     return edits;
   }
diff --git a/pkg/nnbd_migration/lib/src/front_end/migration_summary.dart b/pkg/nnbd_migration/lib/src/front_end/migration_summary.dart
index 3d74d30..a551df2 100644
--- a/pkg/nnbd_migration/lib/src/front_end/migration_summary.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/migration_summary.dart
@@ -94,6 +94,8 @@
         return 'makeTypeNullable';
       case NullabilityFixKind.makeTypeNullableDueToHint:
         return 'makeTypeNullableDueToHint';
+      case NullabilityFixKind.noValidMigrationForNull:
+        return 'noValidMigrationForNull';
       case NullabilityFixKind.nullAwarenessUnnecessaryInStrongMode:
         return 'nullAwarenessUnnecessaryInStrongMode';
       case NullabilityFixKind.nullAwareAssignmentUnnecessaryInStrongMode:
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 7a2ae53..3c09f9b 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
@@ -4,7 +4,6 @@
 
 import 'dart:convert' show jsonDecode, JsonEncoder;
 
-import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/ast/ast.dart';
 import 'package:analyzer/file_system/file_system.dart';
@@ -30,11 +29,11 @@
   // TODO(srawlins): Refactor to use
   //  `Feature.non_nullable.releaseVersion` when this becomes non-null (perhaps
   //  after "Beta").
-  static final Version _intendedMinimumSdkVersion =
-      Feature.non_nullable.experimentalReleaseVersion;
+  static final Version _intendedMinimumSdkVersion = Version.parse('2.12.0-0');
 
   // In the package_config.json file, the patch number is omitted.
-  static const String _intendedLanguageVersion = '2.10';
+  static final String _intendedLanguageVersion =
+      '${_intendedMinimumSdkVersion.major}.${_intendedMinimumSdkVersion.minor}';
 
   static final String _intendedSdkVersionConstraint =
       '>=$_intendedMinimumSdkVersion <2.12.0';
@@ -153,9 +152,6 @@
     if (updated) {
       _processConfigFile(pkgFolder, pubspec);
     }
-    // TODO(https://github.com/dart-lang/sdk/issues/43806): stop processing
-    // analysis options file when the experiment is no longer needed.
-    _processAnalysisOptionsFile(pkgFolder);
   }
 
   Future<void> processUnit(ResolvedUnitResult result) async {
@@ -212,91 +208,6 @@
     }
   }
 
-  void _processAnalysisOptionsException(
-      String action, String analysisOptionsPath, error) {
-    listener.addRecommendation('''Failed to $action analysis options file
-  $analysisOptionsPath
-  $error
-
-  Manually update this file to enable the Null Safety language feature in static
-  analysis by adding:
-
-    analyzer:
-      enable-experiment:
-        - non-nullable
-''');
-  }
-
-  void _processAnalysisOptionsFile(Folder pkgFolder) {
-    var analysisOptionsFile =
-        pkgFolder.getChildAssumingFile('analysis_options.yaml');
-    if (!analysisOptionsFile.exists) {
-      // A source file edit cannot be made for a file which doesn't exist.
-      // Instead of using the fix listener, just write the file directly.
-      analysisOptionsFile.writeAsStringSync('''
-analyzer:
-  enable-experiment:
-    - non-nullable
-
-''');
-      return;
-    }
-
-    _YamlFile analysisOptions;
-    try {
-      analysisOptions = _YamlFile._parseFrom(analysisOptionsFile);
-    } on FileSystemException catch (e) {
-      _processAnalysisOptionsException('read', analysisOptionsFile.path, e);
-      return;
-    } on FormatException catch (e) {
-      _processAnalysisOptionsException('parse', analysisOptionsFile.path, e);
-      return;
-    }
-
-    var analysisOptionsMap = analysisOptions.content;
-    YamlNode analyzerOptions;
-    if (analysisOptionsMap is YamlMap) {
-      analyzerOptions = analysisOptionsMap.nodes['analyzer'];
-    }
-    if (analyzerOptions == null) {
-      // There is no top-level "analyzer" section. We can write one in its
-      // entirety, and use a 2-space indentation. This is a valid indentation,
-      // even if the file contains another top-level section (perhaps "linter")
-      // which uses a different indentation.
-      var start = SourceLocation(0, line: 0, column: 0);
-      var content = '''
-analyzer:
-  enable-experiment:
-    - non-nullable
-
-''';
-      analysisOptions._insertAfterParent(
-          SourceSpan(start, start, ''), content, listener);
-    } else if (analyzerOptions is YamlMap) {
-      var enableExperiment = analyzerOptions.nodes['enable-experiment'];
-      if (enableExperiment == null) {
-        var analyzerIndentation =
-            analysisOptions._getMapEntryIndentation(analyzerOptions);
-        var indent = ' ' * analyzerIndentation;
-        var content = '\n'
-            '${indent}enable-experiment:\n'
-            '$indent  - non-nullable';
-        analysisOptions._insertAfterParent(
-            analyzerOptions.span, content, listener);
-      } else if (enableExperiment is YamlList) {
-        var enableExperimentIndentation =
-            analysisOptions._getListIndentation(enableExperiment);
-        var indent = ' ' * enableExperimentIndentation;
-        var nonNullableIsEnabled = enableExperiment.value
-            .any((experiment) => experiment == 'non-nullable');
-        if (nonNullableIsEnabled) return;
-        var content = '\n' '$indent- non-nullable';
-        analysisOptions._insertAfterParent(
-            enableExperiment.span, content, listener);
-      }
-    }
-  }
-
   /// Updates the Package Config file to specify a minimum Dart SDK version
   /// which supports null safety.
   void _processConfigFile(Folder pkgFolder, _YamlFile pubspec) {
@@ -516,7 +427,6 @@
 }
 
 class _YamlFile {
-  static final _newlineCharacter = RegExp('[\r\n]');
   final String path;
   final String textContent;
 
@@ -524,40 +434,6 @@
 
   _YamlFile._(this.path, this.textContent, this.content);
 
-  /// Returns the indentation of the entries in [node].
-  int _getListIndentation(YamlList node) {
-    return node.span.start.column;
-  }
-
-  /// Returns the indentation of the first (and presumably all) entry of [node].
-  int _getMapEntryIndentation(YamlMap node) {
-    if (node.isEmpty) return 2;
-
-    var value = node.nodes.values.first;
-    if (value is YamlScalar) {
-      // A YamlScalar value indicates that a "key: value" pair is on a single
-      // line. The span's start column is the start column of the value, not the
-      // key.
-      var offset = value.span.start.offset;
-      var firstSpaceIndex =
-          textContent.lastIndexOf(_newlineCharacter, offset) + 1;
-      var index = firstSpaceIndex;
-      while (textContent.codeUnitAt(index) == $space) {
-        index++;
-      }
-      return index - firstSpaceIndex;
-    } else if (value is YamlMap) {
-      // If the first entry of [node] is a YamlMap, then the span for [node]
-      // indicates the start of the first entry.
-      return node.span.start.column;
-    } else {
-      assert(value is YamlList);
-      // If the first entry of [node] is a YamlList, then the span for [value]
-      // indicates the start of the first list entry.
-      return value.span.start.column;
-    }
-  }
-
   String _getName() {
     YamlNode packageNameNode;
 
diff --git a/pkg/nnbd_migration/lib/src/front_end/unit_renderer.dart b/pkg/nnbd_migration/lib/src/front_end/unit_renderer.dart
index b76c5ff..cddd307 100644
--- a/pkg/nnbd_migration/lib/src/front_end/unit_renderer.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/unit_renderer.dart
@@ -23,6 +23,7 @@
   /// "proposed edits" area, in the order in which they should be displayed.
   @visibleForTesting
   static const List<NullabilityFixKind> kindPriorityOrder = [
+    NullabilityFixKind.noValidMigrationForNull,
     NullabilityFixKind.compoundAssignmentHasBadCombinedType,
     NullabilityFixKind.compoundAssignmentHasNullableSource,
     NullabilityFixKind.removeDeadCode,
@@ -316,6 +317,8 @@
         return '$count type$s made nullable';
       case NullabilityFixKind.makeTypeNullableDueToHint:
         return '$count nullability hint$s converted to ?$s';
+      case NullabilityFixKind.noValidMigrationForNull:
+        return '$count literal `null`$s could not be migrated';
       case NullabilityFixKind.nullAwarenessUnnecessaryInStrongMode:
         return '$count null-aware access$es will be unnecessary in strong '
             'checking mode';
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index 2100509..81066cf 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -785,27 +785,27 @@
 
   Future<void> test_conditional_expression_guard_subexpression() async {
     var content = '''
-void f(String s, int x) {
-  s == null ? (x = null) : (x = s.length);
+void f(String s, int x, int/*?*/ n) {
+  s == null ? (x = n) : (x = s.length);
 }
 ''';
     var expected = '''
-void f(String s, int x) {
-  s == null ? (x = null!) : (x = s.length);
+void f(String s, int x, int? n) {
+  s == null ? (x = n!) : (x = s.length);
 }
 ''';
     await _checkSingleFileChanges(content, expected, warnOnWeakCode: true);
   }
 
   Future<void> test_conditional_expression_guard_value_ifFalse() async {
-    var content = 'int f(String s) => s != null ? s.length : null;';
-    var expected = 'int f(String s) => s != null ? s.length : null!;';
+    var content = 'int f(String s, int/*?*/ n) => s != null ? s.length : n;';
+    var expected = 'int f(String s, int? n) => s != null ? s.length : n!;';
     await _checkSingleFileChanges(content, expected, warnOnWeakCode: true);
   }
 
   Future<void> test_conditional_expression_guard_value_ifTrue() async {
-    var content = 'int f(String s) => s == null ? null : s.length;';
-    var expected = 'int f(String s) => s == null ? null! : s.length;';
+    var content = 'int f(String s, int/*?*/ n) => s == null ? n : s.length;';
+    var expected = 'int f(String s, int? n) => s == null ? n! : s.length;';
     await _checkSingleFileChanges(content, expected, warnOnWeakCode: true);
   }
 
@@ -3879,16 +3879,16 @@
 class C<T extends Object/*!*/> {
   C(T/*!*/ t);
 }
-main() {
-  C<int> c = C<int>(null);
+test(int/*?*/ n) {
+  C<int> c = C<int>(n);
 }
 ''';
     var expected = '''
 class C<T extends Object> {
   C(T t);
 }
-main() {
-  C<int> c = C<int>(null!);
+test(int? n) {
+  C<int> c = C<int>(n!);
 }
 ''';
     await _checkSingleFileChanges(content, expected);
@@ -3920,16 +3920,16 @@
 class C<T extends Object/*!*/> {
   C(T/*!*/ t);
 }
-main() {
-  C<int> c = C(null);
+test(int/*?*/ n) {
+  C<int> c = C(n);
 }
 ''';
     var expected = '''
 class C<T extends Object> {
   C(T t);
 }
-main() {
-  C<int> c = C(null!);
+test(int? n) {
+  C<int> c = C(n!);
 }
 ''';
     await _checkSingleFileChanges(content, expected);
@@ -4372,6 +4372,22 @@
         {path2: file2, path1: file1}, {path1: expected1, path2: expected2});
   }
 
+  Future<void> test_literal_null_without_valid_migration() async {
+    var content = '''
+void f(int/*!*/ x) {}
+void g() {
+  f(null);
+}
+''';
+    var expected = '''
+void f(int x) {}
+void g() {
+  f(null);
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
   Future<void> test_literals_maintain_nullability() async {
     // See #40590. Without exact nullability, this would migrate to
     // `List<int?> list = <int>[1, 2]`. While the function of exact nullability
@@ -4705,14 +4721,14 @@
       test_methodInvocation_typeArguments_explicit_nonNullable() async {
     var content = '''
 T f<T extends Object/*!*/>(T/*!*/ t) => t;
-void g() {
-  int x = f<int>(null);
+void g(int/*?*/ n) {
+  int x = f<int>(n);
 }
 ''';
     var expected = '''
 T f<T extends Object>(T t) => t;
-void g() {
-  int x = f<int>(null!);
+void g(int? n) {
+  int x = f<int>(n!);
 }
 ''';
     await _checkSingleFileChanges(content, expected);
@@ -4755,14 +4771,14 @@
       test_methodInvocation_typeArguments_inferred_nonNullable() async {
     var content = '''
 T f<T extends Object/*!*/>(T/*!*/ t) => t;
-void g() {
-  int x = f(null);
+void g(int/*?*/ n) {
+  int x = f(n);
 }
 ''';
     var expected = '''
 T f<T extends Object>(T t) => t;
-void g() {
-  int x = f(null!);
+void g(int? n) {
+  int x = f(n!);
 }
 ''';
     await _checkSingleFileChanges(content, expected);
@@ -7079,8 +7095,8 @@
   }
   i4.toDouble();
 }
-main() {
-  g(false, null, null);
+test(int/*?*/ n) {
+  g(false, n, null);
 }
 ''';
     var expected = '''
@@ -7097,8 +7113,8 @@
   }
   i4!.toDouble();
 }
-main() {
-  g(false, null!, null);
+test(int? n) {
+  g(false, n!, null);
 }
 ''';
     await _checkSingleFileChanges(content, expected);
diff --git a/pkg/nnbd_migration/test/fix_aggregator_test.dart b/pkg/nnbd_migration/test/fix_aggregator_test.dart
index e0839dc..123de0a 100644
--- a/pkg/nnbd_migration/test/fix_aggregator_test.dart
+++ b/pkg/nnbd_migration/test/fix_aggregator_test.dart
@@ -1134,6 +1134,16 @@
     expect(previewInfo, null);
   }
 
+  Future<void> test_noValidMigration() async {
+    await analyze('f(a) => null;');
+    var literal = findNode.nullLiteral('null');
+    var previewInfo = run(
+        {literal: NodeChangeForExpression()..addNoValidMigration(_MockInfo())});
+    expect(previewInfo.applyTo(code), code);
+    expect(previewInfo.applyTo(code, includeInformative: true),
+        'f(a) => null /* no valid migration */;');
+  }
+
   Future<void> test_nullCheck_index_cascadeResult() async {
     await analyze('f(a) => a..[0].c;');
     var index = findNode.index('[0]');
diff --git a/pkg/nnbd_migration/test/fix_builder_test.dart b/pkg/nnbd_migration/test/fix_builder_test.dart
index 1795fb0..29e39622 100644
--- a/pkg/nnbd_migration/test/fix_builder_test.dart
+++ b/pkg/nnbd_migration/test/fix_builder_test.dart
@@ -68,6 +68,9 @@
   static final isNodeChangeForExpression =
       TypeMatcher<NodeChangeForExpression>();
 
+  static final isNoValidMigration =
+      isNodeChangeForExpression.havingNoValidMigrationWithInfo(anything);
+
   static final isNullCheck =
       isNodeChangeForExpression.havingNullCheckWithInfo(anything);
 
@@ -2060,6 +2063,16 @@
     visitSubexpression(assignment, 'int?');
   }
 
+  Future<void> test_nullable_value_in_null_context() async {
+    await analyze('int/*!*/ f(int/*?*/ i) => i;');
+    var iRef = findNode.simple('i;');
+    visitSubexpression(iRef, 'int', changes: {
+      iRef: isNodeChangeForExpression.havingNullCheckWithInfo(isInfo(
+          NullabilityFixDescription.checkExpression,
+          {FixReasonTarget.root: TypeMatcher<NullabilityEdge>()}))
+    });
+  }
+
   Future<void> test_nullAssertion_promotes() async {
     await analyze('''
 _f(bool/*?*/ x) => x && x;
@@ -2077,6 +2090,36 @@
     visitSubexpression(findNode.nullLiteral('null'), 'Null');
   }
 
+  Future<void> test_nullLiteral_hinted() async {
+    await analyze('''
+int/*!*/ f() => null/*!*/;
+''');
+    var literal = findNode.nullLiteral('null');
+    // Normally we would leave the null literal alone and add an informative
+    // comment saying there's no valid migration for it.  But since the user
+    // specifically hinted that `!` should be added, we respect that.
+    visitSubexpression(literal, 'Never', changes: {
+      literal: isNodeChangeForExpression.havingNullCheckWithInfo(isInfo(
+          NullabilityFixDescription.checkExpressionDueToHint,
+          {FixReasonTarget.root: TypeMatcher<FixReason_NullCheckHint>()}))
+    });
+  }
+
+  Future<void> test_nullLiteral_noValidMigration() async {
+    await analyze('''
+int/*!*/ f() => null;
+''');
+    var literal = findNode.nullLiteral('null');
+    // Note: in spite of the fact that we leave the literal as `null`, we
+    // analyze it as though it has type `Never`, because it's in a context where
+    // `null` doesn't work.
+    visitSubexpression(literal, 'Never', changes: {
+      literal: isNodeChangeForExpression.havingNoValidMigrationWithInfo(isInfo(
+          NullabilityFixDescription.noValidMigrationForNull,
+          {FixReasonTarget.root: TypeMatcher<NullabilityEdge>()}))
+    });
+  }
+
   Future<void> test_parenthesizedExpression() async {
     await analyze('''
 f() => (1);
@@ -3516,6 +3559,12 @@
       having((c) => c.addsNullCheck, 'addsNullCheck', true)
           .having((c) => c.addNullCheckInfo, 'addNullCheckInfo', matcher);
 
+  TypeMatcher<NodeChangeForExpression> havingNoValidMigrationWithInfo(
+          dynamic matcher) =>
+      having((c) => c.addsNoValidMigration, 'addsNoValidMigration', true)
+          .having((c) => c.addNoValidMigrationInfo, 'addNoValidMigrationInfo',
+              matcher);
+
   TypeMatcher<NodeChangeForExpression> havingIndroduceAsWithInfo(
           dynamic typeStringMatcher, dynamic infoMatcher) =>
       having((c) => c.introducesAsType.toString(), 'introducesAsType (string)',
diff --git a/pkg/nnbd_migration/test/migration_cli_test.dart b/pkg/nnbd_migration/test/migration_cli_test.dart
index e27e23b..c62fc75 100644
--- a/pkg/nnbd_migration/test/migration_cli_test.dart
+++ b/pkg/nnbd_migration/test/migration_cli_test.dart
@@ -432,7 +432,7 @@
           '''
 name: test
 environment:
-  sdk: '${migrated ? '>=2.10.0 <2.12.0' : '>=2.6.0 <3.0.0'}'
+  sdk: '${migrated ? '>=2.12.0-0 <2.12.0' : '>=2.6.0 <3.0.0'}'
 ''',
       '.dart_tool/package_config.json':
           packageConfigText ?? _getPackageConfigText(migrated: migrated),
@@ -449,210 +449,6 @@
     NonNullableFix.shutdownAllServers();
   }
 
-  test_analysis_options_analyzer_is_missing_enable_experiment() async {
-    var projectContents = simpleProject(analysisOptionsText: '''
-analyzer:
-  foo: 1
-''');
-    var projectDir = createProjectDir(projectContents);
-    var cliRunner = _createCli()
-        .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
-    await cliRunner.run();
-    // The Dart source code should still be migrated.
-    assertProjectContents(
-        projectDir, simpleProject(migrated: true, analysisOptionsText: '''
-analyzer:
-  foo: 1
-  enable-experiment:
-    - non-nullable
-'''));
-  }
-
-  test_analysis_options_analyzer_is_missing_enable_experiment_big_indent() async {
-    var projectContents = simpleProject(analysisOptionsText: '''
-analyzer:
-      foo: 1
-''');
-    var projectDir = createProjectDir(projectContents);
-    var cliRunner = _createCli()
-        .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
-    await cliRunner.run();
-    // The Dart source code should still be migrated.
-    assertProjectContents(
-        projectDir, simpleProject(migrated: true, analysisOptionsText: '''
-analyzer:
-      foo: 1
-      enable-experiment:
-        - non-nullable
-'''));
-  }
-
-  test_analysis_options_analyzer_is_missing_enable_experiment_nested_map() async {
-    var projectContents = simpleProject(analysisOptionsText: '''
-analyzer:
-  exclude:
-    foo:
-      bar: 1
-''');
-    var projectDir = createProjectDir(projectContents);
-    var cliRunner = _createCli()
-        .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
-    await cliRunner.run();
-    // The Dart source code should still be migrated.
-    assertProjectContents(
-        projectDir, simpleProject(migrated: true, analysisOptionsText: '''
-analyzer:
-  exclude:
-    foo:
-      bar: 1
-  enable-experiment:
-    - non-nullable
-'''));
-  }
-
-  test_analysis_options_analyzer_is_not_a_map() async {
-    var analysisOptionsText = '''
-analyzer: 1
-''';
-    var projectContents =
-        simpleProject(analysisOptionsText: analysisOptionsText);
-    var projectDir = createProjectDir(projectContents);
-    var cliRunner = _createCli()
-        .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
-    await cliRunner.run();
-    // The Dart source code should still be migrated.
-    assertProjectContents(
-        projectDir,
-        simpleProject(
-            migrated: true, analysisOptionsText: analysisOptionsText));
-  }
-
-  test_analysis_options_does_not_exist() async {
-    var projectContents = simpleProject();
-    var projectDir = createProjectDir(projectContents);
-    var cliRunner = _createCli()
-        .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
-    await cliRunner.run();
-    // The Dart source code should still be migrated.
-    assertProjectContents(
-        projectDir, simpleProject(migrated: true, analysisOptionsText: '''
-analyzer:
-  enable-experiment:
-    - non-nullable
-
-'''));
-  }
-
-  test_analysis_options_enable_experiment_contains_non_nullable() async {
-    var analysisOptionsText = '''
-analyzer:
-  enable-experiment:
-    - non-nullable
-''';
-    var projectContents =
-        simpleProject(analysisOptionsText: analysisOptionsText);
-    var projectDir = createProjectDir(projectContents);
-    var cliRunner = _createCli()
-        .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
-    await cliRunner.run();
-    // The Dart source code should still be migrated.
-    assertProjectContents(
-        projectDir,
-        simpleProject(
-            migrated: true, analysisOptionsText: analysisOptionsText));
-  }
-
-  test_analysis_options_enable_experiment_is_not_list() async {
-    var analysisOptionsText = '''
-analyzer:
-  enable-experiment: 1
-''';
-    var projectContents =
-        simpleProject(analysisOptionsText: analysisOptionsText);
-    var projectDir = createProjectDir(projectContents);
-    var cliRunner = _createCli()
-        .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
-    await cliRunner.run();
-    // The Dart source code should still be migrated.
-    assertProjectContents(
-        projectDir,
-        simpleProject(
-            migrated: true, analysisOptionsText: analysisOptionsText));
-  }
-
-  test_analysis_options_enable_experiment_missing_non_nullable() async {
-    var analysisOptionsText = '''
-analyzer:
-  enable-experiment:
-    - foo
-''';
-    var projectContents =
-        simpleProject(analysisOptionsText: analysisOptionsText);
-    var projectDir = createProjectDir(projectContents);
-    var cliRunner = _createCli()
-        .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
-    await cliRunner.run();
-    // The Dart source code should still be migrated.
-    assertProjectContents(
-        projectDir, simpleProject(migrated: true, analysisOptionsText: '''
-analyzer:
-  enable-experiment:
-    - foo
-    - non-nullable
-'''));
-  }
-
-  test_analysis_options_enable_experiment_missing_non_nullable_big_indent() async {
-    var analysisOptionsText = '''
-analyzer:
-  enable-experiment:
-        - foo
-''';
-    var projectContents =
-        simpleProject(analysisOptionsText: analysisOptionsText);
-    var projectDir = createProjectDir(projectContents);
-    var cliRunner = _createCli()
-        .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
-    await cliRunner.run();
-    // The Dart source code should still be migrated.
-    assertProjectContents(
-        projectDir, simpleProject(migrated: true, analysisOptionsText: '''
-analyzer:
-  enable-experiment:
-        - foo
-        - non-nullable
-'''));
-  }
-
-  test_analysis_options_is_missing_analyzer() async {
-    var projectContents = simpleProject(analysisOptionsText: '''
-name: test
-''');
-    var projectDir = createProjectDir(projectContents);
-    var cliRunner = _createCli()
-        .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
-    await cliRunner.run();
-    // The Dart source code should still be migrated.
-    assertProjectContents(
-        projectDir, simpleProject(migrated: true, analysisOptionsText:
-            // This is strange-looking, but valid.
-            '''
-analyzer:
-  enable-experiment:
-    - non-nullable
-
-name: test
-'''));
-  }
-
-  test_analysis_options_is_not_a_map() async {
-    var projectContents = simpleProject(analysisOptionsText: 'not-a-map');
-    var projectDir = createProjectDir(projectContents);
-    var cliRunner = _createCli()
-        .decodeCommandLineArgs(_parseArgs(['--apply-changes', projectDir]));
-    expect(() async => await cliRunner.run(), throwsUnsupportedError);
-  }
-
   test_default_logger() {
     // When running normally, we don't override the logger; make sure it has a
     // non-null default so that there won't be a crash.
@@ -772,7 +568,12 @@
   }
 
   test_flag_skip_pub_outdated_default() {
-    expect(assertParseArgsSuccess([]).skipPubOutdated, isTrue);
+    expect(assertParseArgsSuccess([]).skipPubOutdated, isFalse);
+  }
+
+  test_flag_skip_pub_outdated_disable() async {
+    // "--no-skip-pub-outdated" is not an option.
+    await assertParseArgsFailure(['--no-skip-pub-outdated']);
   }
 
   test_flag_skip_pub_outdated_enable() {
@@ -780,11 +581,6 @@
         isTrue);
   }
 
-  test_flag_skip_pub_outdated_negated() async {
-    expect(assertParseArgsSuccess(['--no-skip-pub-outdated']).skipPubOutdated,
-        isFalse);
-  }
-
   test_flag_web_preview_default() {
     expect(assertParseArgsSuccess([]).webPreview, isTrue);
   }
@@ -1524,8 +1320,7 @@
 }
 ''' /* stdout */,
         '' /* stderr */);
-    var output = await assertRunFailure([projectDir, '--no-skip-pub-outdated'],
-        expectedExitCode: 1);
+    var output = await assertRunFailure([projectDir], expectedExitCode: 1);
     expect(output,
         contains('Warning: not all current dependencies have migrated'));
   }
@@ -1999,7 +1794,7 @@
 name: test
 environment:
   foo: 1
-  sdk: '>=2.10.0 <2.12.0'
+  sdk: '>=2.12.0-0 <2.12.0'
 '''));
   }
 
@@ -2052,7 +1847,7 @@
         // This is strange-looking, but valid.
         '''
 environment:
-  sdk: '>=2.10.0 <2.12.0'
+  sdk: '>=2.12.0-0 <2.12.0'
 
 name: test
 '''));
@@ -2115,7 +1910,7 @@
       "name": "test",
       "rootUri": "../",
       "packageUri": "lib/",
-      "languageVersion": "${migrated ? '2.10' : '2.6'}"
+      "languageVersion": "${migrated ? '2.12' : '2.6'}"
     }
   ]
 }
diff --git a/runtime/bin/dartdev_isolate.cc b/runtime/bin/dartdev_isolate.cc
index c5a8908..0153f2f 100644
--- a/runtime/bin/dartdev_isolate.cc
+++ b/runtime/bin/dartdev_isolate.cc
@@ -145,9 +145,7 @@
 
       // If we're given a non-zero exit code, DartDev is signaling for us to
       // shutdown.
-      if (dartdev_exit_code != 0) {
-        Process::SetGlobalExitCode(dartdev_exit_code);
-      }
+      Process::SetGlobalExitCode(dartdev_exit_code);
 
       // If DartDev hasn't signaled for us to do anything else, we can assume
       // there's nothing else for the VM to run and that we can exit.
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 8ab0091..16fca32 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -4841,6 +4841,10 @@
                      "<extractor parameter names>");
     s->AddBaseObject(Object::empty_context_scope().raw(), "ContextScope",
                      "<empty>");
+    s->AddBaseObject(Object::empty_object_pool().raw(), "ObjectPool",
+                     "<empty>");
+    s->AddBaseObject(Object::empty_compressed_stackmaps().raw(),
+                     "CompressedStackMaps", "<empty>");
     s->AddBaseObject(Object::empty_descriptors().raw(), "PcDescriptors",
                      "<empty>");
     s->AddBaseObject(Object::empty_var_descriptors().raw(),
@@ -4925,6 +4929,8 @@
     ASSERT(Object::extractor_parameter_names().raw() != Object::null());
     d->AddBaseObject(Object::extractor_parameter_names().raw());
     d->AddBaseObject(Object::empty_context_scope().raw());
+    d->AddBaseObject(Object::empty_object_pool().raw());
+    d->AddBaseObject(Object::empty_compressed_stackmaps().raw());
     d->AddBaseObject(Object::empty_descriptors().raw());
     d->AddBaseObject(Object::empty_var_descriptors().raw());
     d->AddBaseObject(Object::empty_exception_handlers().raw());
diff --git a/tools/FAKE_COMMITS b/tools/FAKE_COMMITS
index 2a622d1..471f72f 100644
--- a/tools/FAKE_COMMITS
+++ b/tools/FAKE_COMMITS
@@ -26,6 +26,8 @@
 Force build while trybots are broken, to check builders for brokenness.
 Force build to test recipe changes
 Force build to test recipe changes
+Trigger bots
+Trigger bots
 
 Analyzer branch commits:
 Force build on new analyzer-branch linux build with new workflow
diff --git a/tools/VERSION b/tools/VERSION
index 966c0c1..ca382e0 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 0
+PRERELEASE 1
 PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/bots/flutter/analyze_flutter_engine.sh b/tools/bots/flutter/analyze_flutter_engine.sh
index bdf8244..eac0e39 100755
--- a/tools/bots/flutter/analyze_flutter_engine.sh
+++ b/tools/bots/flutter/analyze_flutter_engine.sh
@@ -25,6 +25,8 @@
 # analyze lib/web_ui
 echo Analyzing lib/web_ui...
 pushd lib/web_ui
+
 $dart pub get
-$dart analyze
+$dart analyze --fatal-infos
+
 popd