Version 2.12.0-203.0.dev

Merge commit '5c174c8d8c7ccbbbd0678f3f4d73ea3654fafa5a' into 'dev'
diff --git a/DEPS b/DEPS
index b286e0e..70da9eb 100644
--- a/DEPS
+++ b/DEPS
@@ -161,7 +161,7 @@
   "typed_data_tag": "f94fc57b8e8c0e4fe4ff6cfd8290b94af52d3719",
   "usage_rev": "6c64d9e7b6b3758d06d030efcb5afe20bfc04dde",
   "vector_math_rev": "0c9f5d68c047813a6dcdeb88ba7a42daddf25025",
-  "watcher_rev": "1e154d0900eabd5aed4b540e0416b8367e5c0248",
+  "watcher_rev": "14706140b3670ca1dfeca73480bdd3091b728ce9",
   "webdriver_rev": "5a8d6805d9cf8a3cbb4fcd64849b538b7491e50e",
   "web_components_rev": "8f57dac273412a7172c8ade6f361b407e2e4ed02",
   "web_socket_channel_rev": "680358915e331fda823908234f80beba1ed4ec83",
diff --git a/pkg/analysis_server/lib/src/services/correction/assist.dart b/pkg/analysis_server/lib/src/services/correction/assist.dart
index 04dc79d..2929cc2 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist.dart
@@ -72,9 +72,7 @@
   static const CONVERT_INTO_IS_NOT =
       AssistKind('dart.assist.convert.isNot', 30, 'Convert to is!');
   static const CONVERT_INTO_IS_NOT_EMPTY = AssistKind(
-      'dart.assist.convert.isNotEmpty', 30, "Convert to 'isNotEmpty'",
-      // todo (pq): unify w/ fix
-      associatedErrorCodes: <String>['prefer_is_not_empty']);
+      'dart.assist.convert.isNotEmpty', 30, "Convert to 'isNotEmpty'");
   static const CONVERT_PART_OF_TO_URI = AssistKind(
       'dart.assist.convert.partOfToPartUri', 30, 'Convert to use a URI');
   static const CONVERT_TO_DOUBLE_QUOTED_STRING = AssistKind(
@@ -92,11 +90,9 @@
   static const CONVERT_TO_INT_LITERAL = AssistKind(
       'dart.assist.convert.toIntLiteral', 30, 'Convert to an int literal');
   static const CONVERT_TO_LIST_LITERAL = AssistKind(
-      'dart.assist.convert.toListLiteral', 30, 'Convert to list literal',
-      associatedErrorCodes: <String>['prefer_collection_literals']);
+      'dart.assist.convert.toListLiteral', 30, 'Convert to list literal');
   static const CONVERT_TO_MAP_LITERAL = AssistKind(
-      'dart.assist.convert.toMapLiteral', 30, 'Convert to map literal',
-      associatedErrorCodes: <String>['prefer_collection_literals']);
+      'dart.assist.convert.toMapLiteral', 30, 'Convert to map literal');
   static const CONVERT_TO_MULTILINE_STRING = AssistKind(
       'dart.assist.convert.toMultilineString',
       30,
@@ -116,8 +112,7 @@
       30,
       'Convert to a relative import');
   static const CONVERT_TO_SET_LITERAL = AssistKind(
-      'dart.assist.convert.toSetLiteral', 30, 'Convert to set literal',
-      associatedErrorCodes: <String>['prefer_collection_literals']);
+      'dart.assist.convert.toSetLiteral', 30, 'Convert to set literal');
   static const CONVERT_TO_SINGLE_QUOTED_STRING = AssistKind(
       'dart.assist.convert.toSingleQuotedString',
       30,
diff --git a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
index 48fae93..f7fc919 100644
--- a/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
+++ b/pkg/analysis_server/lib/src/services/correction/bulk_fix_processor.dart
@@ -39,6 +39,7 @@
 import 'package:analysis_server/src/services/correction/dart/remove_empty_constructor_body.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_empty_else.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_empty_statement.dart';
+import 'package:analysis_server/src/services/correction/dart/remove_if_null_operator.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_initializer.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_interpolation_braces.dart';
 import 'package:analysis_server/src/services/correction/dart/remove_method_declaration.dart';
@@ -243,6 +244,9 @@
     LintNames.unnecessary_new: [
       RemoveUnnecessaryNew.newInstance,
     ],
+    LintNames.unnecessary_null_in_if_null_operators: [
+      RemoveIfNullOperator.newInstance,
+    ],
     LintNames.unnecessary_overrides: [
       RemoveMethodDeclaration.newInstance,
     ],
diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/remove_if_null_operator_test.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/remove_if_null_operator_test.dart
new file mode 100644
index 0000000..0003079
--- /dev/null
+++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/remove_if_null_operator_test.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// 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 'package:analysis_server/src/services/linter/lint_names.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'bulk_fix_processor.dart';
+
+void main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(UnnecessaryNullInIfNullOperatorsTest);
+  });
+}
+
+@reflectiveTest
+class UnnecessaryNullInIfNullOperatorsTest extends BulkFixProcessorTest {
+  @override
+  String get lintCode => LintNames.unnecessary_null_in_if_null_operators;
+
+  @failingTest
+  Future<void> test_null_null_left() async {
+    // The fix only addresses one null and results in:
+    //
+    //     var b = null ?? a;
+    //
+    // (not incorrect but not complete).
+    await resolveTestCode('''
+var a = '';
+var b = null ?? null ?? a;
+''');
+    await assertHasFix('''
+var a = '';
+var b = a;
+''');
+  }
+
+  Future<void> test_singleFile() async {
+    await resolveTestCode('''
+var a = '';
+var b = null ?? a ?? null;
+var c = a ?? null ?? null;
+''');
+    await assertHasFix('''
+var a = '';
+var b = a;
+var c = a;
+''');
+  }
+}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart b/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart
index 14012da..0f66ec9 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/bulk/test_all.dart
@@ -41,6 +41,7 @@
     as remove_empty_constructor_body;
 import 'remove_empty_else_test.dart' as remove_empty_else;
 import 'remove_empty_statement_test.dart' as remove_empty_statement;
+import 'remove_if_null_operator_test.dart' as remove_if_null_operator;
 import 'remove_initializer_test.dart' as remove_initializer;
 import 'remove_interpolation_braces_test.dart' as remove_interpolation_braces;
 import 'remove_method_declaration_test.dart' as remove_method_declaration;
@@ -99,6 +100,7 @@
     remove_empty_constructor_body.main();
     remove_empty_else.main();
     remove_empty_statement.main();
+    remove_if_null_operator.main();
     remove_interpolation_braces.main();
     remove_method_declaration.main();
     remove_non_null_assertion.main();
diff --git a/pkg/analyzer_plugin/lib/utilities/assist/assist.dart b/pkg/analyzer_plugin/lib/utilities/assist/assist.dart
index d6fa2a4..28ff7fe 100644
--- a/pkg/analyzer_plugin/lib/utilities/assist/assist.dart
+++ b/pkg/analyzer_plugin/lib/utilities/assist/assist.dart
@@ -78,14 +78,9 @@
   /// message `"Create a component named '{0}' in '{1}'"` contains two parameters.
   final String message;
 
-  /// A list of any associated error codes. Assists with associated error codes
-  /// can be presented as "fixes" for the associated errors by clients.
-  final List<String> associatedErrorCodes;
-
   /// Initialize a newly created kind of assist to have the given [id],
-  /// [priority], [message] and optionally any [associatedErrorCodes].
-  const AssistKind(this.id, this.priority, this.message,
-      {this.associatedErrorCodes});
+  /// [priority] and [message].
+  const AssistKind(this.id, this.priority, this.message);
 
   @override
   String toString() => id;
diff --git a/pkg/dev_compiler/lib/src/kernel/compiler.dart b/pkg/dev_compiler/lib/src/kernel/compiler.dart
index fea201a..0098e51 100644
--- a/pkg/dev_compiler/lib/src/kernel/compiler.dart
+++ b/pkg/dev_compiler/lib/src/kernel/compiler.dart
@@ -5934,7 +5934,10 @@
 
   @override
   js_ast.Expression visitLoadLibrary(LoadLibrary node) =>
-      runtimeCall('loadLibrary()');
+      runtimeCall('loadLibrary(#, #)', [
+        js.string(jsLibraryName(node.import.enclosingLibrary)),
+        js.string(node.import.name)
+      ]);
 
   // TODO(jmesserly): DDC loads all libraries eagerly.
   // See
@@ -5942,7 +5945,10 @@
   // https://github.com/dart-lang/sdk/issues/27777
   @override
   js_ast.Expression visitCheckLibraryIsLoaded(CheckLibraryIsLoaded node) =>
-      js.boolean(true);
+      runtimeCall('checkDeferredIsLoaded(#, #)', [
+        js.string(jsLibraryName(node.import.enclosingLibrary)),
+        js.string(node.import.name)
+      ]);
 
   bool _reifyFunctionType(FunctionNode f) {
     if (_currentLibrary.importUri.scheme != 'dart') return true;
diff --git a/runtime/vm/runtime_entry.cc b/runtime/vm/runtime_entry.cc
index cecf9b1..4e038ae 100644
--- a/runtime/vm/runtime_entry.cc
+++ b/runtime/vm/runtime_entry.cc
@@ -2510,14 +2510,14 @@
           String::Handle(String::New("get:platformScript"))));
       Object& result = Object::Handle(
           DartEntry::InvokeFunction(func, Object::empty_array()));
-      if (result.IsUnwindError()) {
+      if (result.IsError()) {
         Exceptions::PropagateError(Error::Cast(result));
       }
       if (!result.IsInstance()) {
         FATAL1("Bad script uri hook: %s", result.ToCString());
       }
       result = DartLibraryCalls::ToString(Instance::Cast(result));
-      if (result.IsUnwindError()) {
+      if (result.IsError()) {
         Exceptions::PropagateError(Error::Cast(result));
       }
       if (!result.IsString()) {
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart
index fd80d9c..36ebdf8 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart
@@ -17,6 +17,11 @@
   throw UnimplementedError(message);
 }
 
+throwDeferredIsLoadedError(
+    @notNull String enclosingLibrary, @notNull String importPrefix) {
+  throw DeferredNotLoadedError(enclosingLibrary, importPrefix);
+}
+
 // TODO(nshahan) Cleanup embeded strings and extract file location at runtime
 // from the stacktrace.
 assertFailed(String? message,
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
index 4a224cd..af8e2e1 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart
@@ -759,11 +759,34 @@
   return name;
 }
 
+/// A map from libraries to a set of import prefixes that have been loaded.
+///
+/// Used to validate deferred library conventions.
+final deferredImports = JS<Object>('!', 'new Map()');
+
 /// Emulates the implicit "loadLibrary" function provided by a deferred library.
 ///
-/// Libraries are not actually deferred in DDC, so this just returns a future
-/// that completes immediately.
-Future loadLibrary() => Future.value();
+/// Libraries are not actually deferred in DDC, so this just records the import
+/// for runtime validation, then returns a future that completes immediately.
+Future loadLibrary(
+    @notNull String enclosingLibrary, @notNull String importPrefix) {
+  var result = JS('', '#.get(#)', deferredImports, enclosingLibrary);
+  if (JS<bool>('', '# === void 0', result)) {
+    JS('', '#.set(#, # = new Set())', deferredImports, enclosingLibrary,
+        result);
+  }
+  JS('', '#.add(#)', result, importPrefix);
+  return Future.value();
+}
+
+void checkDeferredIsLoaded(
+    @notNull String enclosingLibrary, @notNull String importPrefix) {
+  var loaded = JS('', '#.get(#)', deferredImports, enclosingLibrary);
+  if (JS<bool>('', '# === void 0', loaded) ||
+      JS<bool>('', '!#.has(#)', loaded, importPrefix)) {
+    throwDeferredIsLoadedError(enclosingLibrary, importPrefix);
+  }
+}
 
 /// Defines lazy statics.
 ///
diff --git a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart
index 21c6f31..e4afbf4 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/runtime.dart
@@ -18,6 +18,7 @@
         BooleanConversionAssertionError,
         CastErrorImpl,
         DartIterator,
+        DeferredNotLoadedError,
         TypeErrorImpl,
         JsLinkedHashMap,
         ImmutableMap,
@@ -204,6 +205,7 @@
   _cacheMaps.clear();
   JS('', '#.clear()', _nullComparisonSet);
   JS('', '#.clear()', constantMaps);
+  JS('', '#.clear()', deferredImports);
 }
 
 /// Marks enqueuing an async operation.
diff --git a/sdk/lib/_internal/js_dev_runtime/private/js_helper.dart b/sdk/lib/_internal/js_dev_runtime/private/js_helper.dart
index 94d7cf8..51eb666 100644
--- a/sdk/lib/_internal/js_dev_runtime/private/js_helper.dart
+++ b/sdk/lib/_internal/js_dev_runtime/private/js_helper.dart
@@ -725,6 +725,17 @@
   String toString() => "RuntimeError: $message";
 }
 
+class DeferredNotLoadedError extends Error implements NoSuchMethodError {
+  String enclosingLibrary;
+  String importPrefix;
+
+  DeferredNotLoadedError(this.enclosingLibrary, this.importPrefix);
+
+  String toString() {
+    return 'Deferred import $importPrefix (from $enclosingLibrary) was not loaded.';
+  }
+}
+
 /// Error thrown by DDC when an `assert()` fails (with or without a message).
 class AssertionErrorImpl extends AssertionError {
   final String? _fileUri;
diff --git a/tools/VERSION b/tools/VERSION
index 959ece6..947dc6d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 202
+PRERELEASE 203
 PRERELEASE_PATCH 0
\ No newline at end of file