Version 2.14.0-122.0.dev

Merge commit 'df5d0a5f4e321a6b0ffaa58815e8694f44c8c583' into 'dev'
diff --git a/.dart_tool/package_config.json b/.dart_tool/package_config.json
index 35641bf..f524ec3 100644
--- a/.dart_tool/package_config.json
+++ b/.dart_tool/package_config.json
@@ -11,7 +11,7 @@
     "constraint, update this by running tools/generate_package_config.dart."
   ],
   "configVersion": 2,
-  "generated": "2021-05-11T11:47:02.674706",
+  "generated": "2021-05-17T10:34:01.378194",
   "generator": "tools/generate_package_config.dart",
   "packages": [
     {
@@ -413,6 +413,12 @@
       "languageVersion": "2.12"
     },
     {
+      "name": "lints",
+      "rootUri": "../third_party/pkg/lints",
+      "packageUri": "lib/",
+      "languageVersion": "2.12"
+    },
+    {
       "name": "logging",
       "rootUri": "../third_party/pkg/logging",
       "packageUri": "lib/",
diff --git a/.packages b/.packages
index 1631e03..2a1f1af 100644
--- a/.packages
+++ b/.packages
@@ -60,6 +60,7 @@
 json_rpc_2:third_party/pkg/json_rpc_2/lib
 kernel:pkg/kernel/lib
 linter:third_party/pkg/linter/lib
+lints:third_party/pkg/lints/lib
 logging:third_party/pkg/logging/lib
 markdown:third_party/pkg/markdown/lib
 matcher:third_party/pkg/matcher/lib
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3a1ba65..9a9d3ac 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -182,19 +182,18 @@
 
 #### `dart:collection`
 
-- The `SplayTreeMap` was changed to allow `null` as key if the `compare`
-  function allows it. It now checks that a new key can be used as an
-  argument to the `compare` function when the member is added,
-  *even if the map is empty* (in which case it just compares the key
-  to itself).
-- The `SplayTreeSet` was changed to checks that a new element can be used as an
-  argument to the `compare` function when the member is added,
-  *even if the set is empty* (in which case it just compares the element
-  to itself).
+*   The `SplayTreeMap` was changed to allow `null` as key if the `compare`
+    function allows it. It now checks that a new key can be used as an argument
+    to the `compare` function when the member is added, *even if the map is
+    empty* (in which case it just compares the key to itself).
+
+*   The `SplayTreeSet` was changed to checks that a new element can be used as
+    an argument to the `compare` function when the member is added, *even if the
+    set is empty* (in which case it just compares the element to itself).
 
 #### `dart:developer`
 
-- Added `serverWebSocketUri` property to `ServiceProtocolInfo`.
+*   Added `serverWebSocketUri` property to `ServiceProtocolInfo`.
 
 ### Dart VM
 
@@ -202,21 +201,21 @@
 
 #### Analyzer
 
-- Static analyses with "error" severity can once again be ignored with
-  comments like `// ignore: code` and `// ignore_for_file: code`. To declare
-  that certain analysis codes, or codes with certain severities ("error",
-  "warning", and "info") cannot be ignored with such comments, list them in
-  `analysis_options.yaml`, under the `analyzer` heading, with a new YAML key,
-  `cannot-ignore`. For example, to declare that "error" codes and
-  `unused_import` cannot be ignored, write the following into
-  `analysis_options.yaml`:
+*   Static analyses with "error" severity can once again be ignored with
+    comments like `// ignore: code` and `// ignore_for_file: code`. To declare
+    that certain analysis codes, or codes with certain severities ("error",
+    "warning", and "info") cannot be ignored with such comments, list them in
+    `analysis_options.yaml`, under the `analyzer` heading, with a new YAML key,
+    `cannot-ignore`. For example, to declare that "error" codes and
+    `unused_import` cannot be ignored, write the following into
+    `analysis_options.yaml`:
 
-  ```yaml
-  analyzer:
-    cannot-ignore:
-      - error
-      - unused_import
-  ```
+    ```yaml
+    analyzer:
+      cannot-ignore:
+        - error
+        - unused_import
+    ```
 
 #### dart format
 
@@ -227,19 +226,17 @@
 
 Updated the Linter to `1.2.1`, which includes:
 
-- improvements to `iterable_contains_unrelated_type` to better support `List`
-  content checks.
-- fixes to `camel_case_types` and `prefer_mixin` to support non-function
-  type aliases.
-- fixed `prefer_mixin` to properly make exceptions for `dart.collection`
-  legacy mixins.
-- new lint: `use_build_context_synchronously` (experimental).
-- new lint: `avoid_multiple_declarations_per_line`.
-- full library migration to null-safety.
-- new lint: `use_if_null_to_convert_nulls_to_bools`.
-- new lint: `deprecated_consistency`.
-- new lint: `use_named_constants`.
-- deprecation of `avoid_as`.
+*   Improved `iterable_contains_unrelated_type` to better support `List` content
+    checks.
+*   Fixed `camel_case_types` and `prefer_mixin` to support non-function type
+    aliases.
+*   Fixed `prefer_mixin` to properly make exceptions for `dart.collection`
+    legacy mixins.
+*   Added new lints `avoid_multiple_declarations_per_line`,
+    `use_if_null_to_convert_nulls_to_bools`, `deprecated_consistency`,
+    `use_named_constants`, `use_build_context_synchronously` (experimental).
+*   Deprecated `avoid_as`.
+*   Migrated library to null-safety.
 
 ### Other libraries
 
diff --git a/DEPS b/DEPS
index f0a9838..614956d 100644
--- a/DEPS
+++ b/DEPS
@@ -126,6 +126,7 @@
   "jinja2_rev": "2222b31554f03e62600cd7e383376a7c187967a1",
   "json_rpc_2_rev": "7e00f893440a72de0637970325e4ea44bd1e8c8e",
   "linter_tag": "1.4.0",
+  "lints_tag": "f9670df2a66e0ec12eb51554e70c1cbf56c8f5d0",
   "logging_rev": "e2f633b543ef89c54688554b15ca3d7e425b86a2",
   "markupsafe_rev": "8f45f5cfa0009d2a70589bcda0349b8cb2b72783",
   "markdown_rev": "9c4beaac96d8f008078e00b027915f81b665d2de",
@@ -391,6 +392,8 @@
       Var("dart_git") + "json_rpc_2.git" + "@" + Var("json_rpc_2_rev"),
   Var("dart_root") + "/third_party/pkg/linter":
       Var("dart_git") + "linter.git" + "@" + Var("linter_tag"),
+  Var("dart_root") + "/third_party/pkg/lints":
+      Var("dart_git") + "lints.git" + "@" + Var("lints_tag"),
   Var("dart_root") + "/third_party/pkg/logging":
       Var("dart_git") + "logging.git" + "@" + Var("logging_rev"),
   Var("dart_root") + "/third_party/pkg/markdown":
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_missing_required_argument_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_missing_required_argument_test.dart
index 593c68c..ac92cd1 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_missing_required_argument_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_missing_required_argument_test.dart
@@ -33,7 +33,6 @@
   Future<void> test_constructor_flutter_children() async {
     await resolveTestCode('''
 import 'package:flutter/widgets.dart';
-import 'package:meta/meta.dart';
 
 class MyWidget extends Widget {
   MyWidget({@required List<Widget> children});
@@ -45,7 +44,6 @@
 ''');
     await assertHasFix('''
 import 'package:flutter/widgets.dart';
-import 'package:meta/meta.dart';
 
 class MyWidget extends Widget {
   MyWidget({@required List<Widget> children});
@@ -60,7 +58,6 @@
   Future<void> test_constructor_flutter_hasTrailingComma() async {
     await resolveTestCode('''
 import 'package:flutter/widgets.dart';
-import 'package:meta/meta.dart';
 
 class MyWidget extends Widget {
   MyWidget({@required int a, @required int b});
@@ -72,7 +69,6 @@
 ''');
     await assertHasFix('''
 import 'package:flutter/widgets.dart';
-import 'package:meta/meta.dart';
 
 class MyWidget extends Widget {
   MyWidget({@required int a, @required int b});
@@ -335,7 +331,6 @@
   Future<void> test_param_child() async {
     await resolveTestCode('''
 import 'package:flutter/widgets.dart';
-import 'package:meta/meta.dart';
 
 class MyWidget extends Widget {
   MyWidget({@required String foo, @required Widget child});
@@ -349,7 +344,6 @@
 ''');
     await assertHasFix('''
 import 'package:flutter/widgets.dart';
-import 'package:meta/meta.dart';
 
 class MyWidget extends Widget {
   MyWidget({@required String foo, @required Widget child});
@@ -367,7 +361,6 @@
   Future<void> test_param_children() async {
     await resolveTestCode('''
 import 'package:flutter/widgets.dart';
-import 'package:meta/meta.dart';
 
 class MyWidget extends Widget {
   MyWidget({@required String foo, @required List<Widget> children});
@@ -381,7 +374,6 @@
 ''');
     await assertHasFix('''
 import 'package:flutter/widgets.dart';
-import 'package:meta/meta.dart';
 
 class MyWidget extends Widget {
   MyWidget({@required String foo, @required List<Widget> children});
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index f56612b..a0f2061 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -592,6 +592,7 @@
   HintCode.UNIGNORABLE_IGNORE,
   HintCode.UNNECESSARY_CAST,
   HintCode.UNNECESSARY_IGNORE,
+  HintCode.UNNECESSARY_IMPORT,
   HintCode.UNNECESSARY_NO_SUCH_METHOD,
   HintCode.UNNECESSARY_NULL_COMPARISON_FALSE,
   HintCode.UNNECESSARY_NULL_COMPARISON_TRUE,
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index 49162d7..73797af 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -299,6 +299,8 @@
       verifier.generateDuplicateShownHiddenNameHints(errorReporter);
       verifier.generateUnusedImportHints(errorReporter);
       verifier.generateUnusedShownNameHints(errorReporter);
+      verifier.generateUnnecessaryImportHints(
+          errorReporter, _usedImportedElementsList);
     }
 
     // Unused local elements.
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 6924788..f6fae99 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -3040,33 +3040,11 @@
   ExecutableElementImpl(String name, int offset, {Reference? reference})
       : super(name, offset, reference: reference);
 
-  /// Initialize using the given linked node.
-  ExecutableElementImpl.forLinkedNode(
-      ElementImpl enclosing, Reference reference, AstNode? linkedNode)
-      : super.forLinkedNode(enclosing, reference, linkedNode) {
-    if (linkedNode is MethodDeclarationImpl) {
-      linkedNode.name.staticElement = this;
-    } else if (linkedNode is FunctionDeclarationImpl) {
-      linkedNode.name.staticElement = this;
-    }
-  }
-
-  @override
-  String get displayName {
-    if (linkedNode != null) {
-      return reference!.name;
-    }
-    return super.displayName;
-  }
-
   @override
   Element get enclosingElement => super.enclosingElement!;
 
   @override
   bool get hasImplicitReturnType {
-    if (linkedNode != null) {
-      return linkedContext!.hasImplicitReturnType(linkedNode!);
-    }
     return hasModifier(Modifier.IMPLICIT_TYPE);
   }
 
@@ -3077,18 +3055,11 @@
 
   @override
   bool get isAbstract {
-    if (linkedNode != null) {
-      return !isExternal &&
-          enclosingUnit.linkedContext!.isAbstract(linkedNode!);
-    }
     return hasModifier(Modifier.ABSTRACT);
   }
 
   @override
   bool get isAsynchronous {
-    if (linkedNode != null) {
-      return enclosingUnit.linkedContext!.isAsynchronous(linkedNode!);
-    }
     return hasModifier(Modifier.ASYNCHRONOUS);
   }
 
@@ -3099,9 +3070,6 @@
 
   @override
   bool get isExternal {
-    if (linkedNode != null) {
-      return enclosingUnit.linkedContext!.isExternal(linkedNode!);
-    }
     return hasModifier(Modifier.EXTERNAL);
   }
 
@@ -3112,9 +3080,6 @@
 
   @override
   bool get isGenerator {
-    if (linkedNode != null) {
-      return enclosingUnit.linkedContext!.isGenerator(linkedNode!);
-    }
     return hasModifier(Modifier.GENERATOR);
   }
 
@@ -3137,22 +3102,10 @@
 
   @override
   String get name {
-    if (linkedNode != null) {
-      return reference!.name;
-    }
     return super.name!;
   }
 
   @override
-  int get nameOffset {
-    if (linkedNode != null) {
-      return enclosingUnit.linkedContext!.getNameOffset(linkedNode!);
-    }
-
-    return super.nameOffset;
-  }
-
-  @override
   List<ParameterElement> get parameters =>
       ElementTypeProvider.current.getExecutableParameters(this);
 
@@ -3175,22 +3128,6 @@
   /// In most cases, the [parameters] getter should be used instead.
   List<ParameterElement> get parametersInternal {
     linkedData?.read(this);
-    if (!identical(_parameters, _Sentinel.parameterElement)) {
-      return _parameters;
-    }
-
-    if (linkedNode != null) {
-      var context = enclosingUnit.linkedContext!;
-      var containerRef = reference!.getChild('@parameter');
-      var formalParameters = context.getFormalParameters(linkedNode!);
-      _parameters = ParameterElementImpl.forLinkedNodeList(
-        this,
-        context,
-        containerRef,
-        formalParameters,
-      );
-    }
-
     return _parameters;
   }
 
@@ -3740,8 +3677,8 @@
   FunctionType? _type;
 
   GenericFunctionTypeElementImpl.forLinkedNode(
-      ElementImpl enclosingElement, Reference? reference, AstNode linkedNode)
-      : super.forLinkedNode(enclosingElement, reference, linkedNode) {
+      ElementImpl enclosingElement, AstNode linkedNode)
+      : super.forLinkedNode(enclosingElement, null, linkedNode) {
     if (linkedNode is GenericFunctionTypeImpl) {
       linkedNode.declaredElement = this;
     }
@@ -5133,26 +5070,10 @@
   NonParameterVariableElementImpl(String name, int offset)
       : super(name, offset);
 
-  NonParameterVariableElementImpl.forLinkedNode(
-      ElementImpl enclosing, Reference reference, AstNode linkedNode)
-      : super.forLinkedNode(enclosing, reference, linkedNode);
-
   @override
   Element get enclosingElement => super.enclosingElement!;
 
-  @override
-  bool get hasImplicitType {
-    if (linkedNode != null) {
-      return linkedContext!.hasImplicitType(linkedNode!);
-    }
-    return super.hasImplicitType;
-  }
-
   bool get hasInitializer {
-    if (linkedNode != null) {
-      return linkedContext!
-          .hasInitializer(linkedNode as VariableDeclarationImpl);
-    }
     return hasModifier(Modifier.HAS_INITIALIZER);
   }
 
@@ -5160,31 +5081,6 @@
   set hasInitializer(bool hasInitializer) {
     setModifier(Modifier.HAS_INITIALIZER, hasInitializer);
   }
-
-  @override
-  String get name {
-    if (linkedNode != null) {
-      return reference!.name;
-    }
-    return super.name;
-  }
-
-  @override
-  int get nameOffset {
-    if (linkedNode != null) {
-      return enclosingUnit.linkedContext!.getNameOffset(linkedNode!);
-    }
-
-    return super.nameOffset;
-  }
-
-  @override
-  DartType get type => ElementTypeProvider.current.getVariableType(this);
-
-  @override
-  set type(DartType type) {
-    _type = type;
-  }
 }
 
 /// A concrete implementation of a [ParameterElement].
@@ -5910,10 +5806,6 @@
   /// [offset].
   PropertyInducingElementImpl(String name, int offset) : super(name, offset);
 
-  PropertyInducingElementImpl.forLinkedNode(
-      ElementImpl enclosing, Reference reference, AstNode linkedNode)
-      : super.forLinkedNode(enclosing, reference, linkedNode);
-
   bool get hasTypeInferred => hasModifier(Modifier.HAS_TYPE_INFERRED);
 
   set hasTypeInferred(bool value) {
@@ -5925,9 +5817,6 @@
 
   @override
   bool get isLate {
-    if (linkedNode != null) {
-      return enclosingUnit.linkedContext!.isLate(linkedNode!);
-    }
     return hasModifier(Modifier.LATE);
   }
 
@@ -5939,19 +5828,6 @@
     linkedData?.read(this);
     if (_type != null) return _type!;
 
-    if (linkedNode != null) {
-      // While performing inference during linking, the first step is to collect
-      // dependencies. During this step we resolve the expression, but we might
-      // reference elements that don't have their types inferred yet. So, here
-      // we give some type. A better solution would be to infer recursively, but
-      // we are not doing this yet.
-      if (_type == null) {
-        assert(linkedContext!.isLinking);
-        return DynamicTypeImpl.instance;
-      }
-
-      return _type!;
-    }
     if (isSynthetic && _type == null) {
       if (getter != null) {
         _type = getter!.returnType;
@@ -6301,10 +6177,8 @@
               type.declaredElement as GenericFunctionTypeElementImpl?;
           // TODO(scheglov) Do we need this?
           // We probably should set it when linking and when applying.
-          // TODO(scheglov) And remove the reference!.
           _aliasedElement ??= GenericFunctionTypeElementImpl.forLinkedNode(
             this,
-            reference!.getChild('@function'),
             type,
           );
         } else if (isNonFunctionTypeAliasesEnabled) {
@@ -6317,10 +6191,9 @@
             ..returnType = DynamicTypeImpl.instance;
         }
       } else {
-        // TODO(scheglov) Same as above (both).
+        // TODO(scheglov) Same as above.
         _aliasedElement = GenericFunctionTypeElementImpl.forLinkedNode(
           this,
-          reference!.getChild('@function'),
           linkedNode,
         );
       }
diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
index 3df1687..da49e89 100644
--- a/pkg/analyzer/lib/src/dart/error/hint_codes.dart
+++ b/pkg/analyzer/lib/src/dart/error/hint_codes.dart
@@ -2684,6 +2684,12 @@
           "Try removing the name from the list, or removing the whole comment "
           "if this is the only name in the list.");
 
+  static const HintCode UNNECESSARY_IMPORT = HintCode(
+      'UNNECESSARY_IMPORT',
+      "The import of '{0}' is unnecessary as all of the used elements are also "
+          "provided by the import of '{1}'.",
+      correction: 'Try removing the import directive.');
+
   /**
    * Unnecessary `noSuchMethod` declaration.
    */
diff --git a/pkg/analyzer/lib/src/error/imports_verifier.dart b/pkg/analyzer/lib/src/error/imports_verifier.dart
index 6759bd2..4568292 100644
--- a/pkg/analyzer/lib/src/error/imports_verifier.dart
+++ b/pkg/analyzer/lib/src/error/imports_verifier.dart
@@ -126,6 +126,7 @@
     return false;
   }
 
+  /// Records use of an unprefixed [element].
   void _recordUsedElement(Element element) {
     // Ignore if an unknown library.
     var containingLibrary = element.library;
@@ -353,6 +354,20 @@
     });
   }
 
+  /// Report an [HintCode.UNNECESSARY_IMPORT] hint for each unnecessary import.
+  ///
+  /// Only call this method after unused imports have been determined by
+  /// [removeUsedElements].
+  void generateUnnecessaryImportHints(ErrorReporter errorReporter,
+      List<UsedImportedElements> usedImportedElementsList) {
+    var usedImports = {..._allImports}..removeAll(_unusedImports);
+
+    var verifier = _UnnecessaryImportsVerifier(_namespaceMap, usedImports);
+    verifier.processUsedElements(
+        usedImportedElementsList, _prefixElementMap, _allImports);
+    verifier.reportImports(errorReporter);
+  }
+
   /// Report an [HintCode.UNUSED_IMPORT] hint for each unused import.
   ///
   /// Only call this method after all of the compilation units have been visited
@@ -433,17 +448,12 @@
           // shouldn't confuse by also reporting an unused prefix.
           _unusedImports.remove(importDirective);
         }
-        var namespace = _computeNamespace(importDirective);
+        var namespace = _namespaceMap.computeNamespace(importDirective);
         if (namespace == null) {
           continue;
         }
         for (var element in elements) {
-          var elementFromNamespace =
-              namespace.getPrefixed(prefix.name, element.name!);
-          if (elementFromNamespace != null) {
-            if (_isShadowing(element, elementFromNamespace)) {
-              continue;
-            }
+          if (namespace.providesPrefixed(prefix.name, element)) {
             _unusedImports.remove(importDirective);
             _removeFromUnusedShownNamesMap(element, importDirective);
           }
@@ -458,15 +468,11 @@
       }
       // Find import directives using namespaces.
       for (ImportDirective importDirective in _allImports) {
-        var namespace = _computeNamespace(importDirective);
+        var namespace = _namespaceMap.computeNamespace(importDirective);
         if (namespace == null) {
           continue;
         }
-        var elementFromNamespace = namespace.get(element.name!);
-        if (elementFromNamespace != null) {
-          if (_isShadowing(element, elementFromNamespace)) {
-            continue;
-          }
+        if (namespace.provides(element)) {
           _unusedImports.remove(importDirective);
           _removeFromUnusedShownNamesMap(element, importDirective);
         }
@@ -477,14 +483,14 @@
       if (everythingIsKnownToBeUsed()) {
         return;
       }
+      var elementName = extensionElement.name!;
       // Find import directives using namespaces.
       for (ImportDirective importDirective in _allImports) {
-        var namespace = _computeNamespace(importDirective);
+        var namespace = _namespaceMap.computeNamespace(importDirective);
         if (namespace == null) {
           continue;
         }
         var prefix = importDirective.prefix?.name;
-        var elementName = extensionElement.name!;
         if (prefix == null) {
           if (namespace.get(elementName) == extensionElement) {
             _unusedImports.remove(importDirective);
@@ -552,45 +558,6 @@
     }
   }
 
-  /// Lookup and return the [Namespace] from the [_namespaceMap].
-  ///
-  /// If the map does not have the computed namespace, compute it and cache it
-  /// in the map. If [importDirective] is not resolved or is not resolvable,
-  /// `null` is returned.
-  ///
-  /// @param importDirective the import directive used to compute the returned
-  ///        namespace
-  /// @return the computed or looked up [Namespace]
-  Namespace? _computeNamespace(ImportDirective importDirective) {
-    var namespace = _namespaceMap[importDirective];
-    if (namespace == null) {
-      // If the namespace isn't in the namespaceMap, then compute and put it in
-      // the map.
-      var importElement = importDirective.element;
-      if (importElement != null) {
-        namespace = importElement.namespace;
-        _namespaceMap[importDirective] = namespace;
-      }
-    }
-    return namespace;
-  }
-
-  /// Returns whether [e1] shadows [e2], assuming each is an imported element,
-  /// and that each is imported with the same prefix.
-  ///
-  /// Returns false if the source of either element is `null`.
-  bool _isShadowing(Element e1, Element e2) {
-    var source1 = e1.source;
-    if (source1 == null) {
-      return false;
-    }
-    var source2 = e2.source;
-    if (source2 == null) {
-      return false;
-    }
-    return !source1.isInSystemLibrary && source2.isInSystemLibrary;
-  }
-
   /// Remove [element] from the list of names shown by [importDirective].
   void _removeFromUnusedShownNamesMap(
       Element element, ImportDirective importDirective) {
@@ -633,3 +600,189 @@
   /// The set of extensions defining members that are referenced.
   final Set<ExtensionElement> usedExtensions = {};
 }
+
+/// A class which verifies (and reports) whether any import directives are
+/// unnecessary.
+///
+/// In a given library, every import directive has a set of "used elements," the
+/// subset of elements provided by the import which are used in the library. In
+/// a given library, an import directive is "unnecessary" if there exists at
+/// least one other import directive with the same prefix as the aforementioned
+/// import directive, and a "used elements" set which is a proper superset of
+/// the aforementioned import directive's "used elements" set.
+class _UnnecessaryImportsVerifier {
+  /// The cache of [Namespace]s for [ImportDirective]s.
+  final Map<ImportDirective, Namespace> _namespaceMap;
+
+  /// The set of imports which provide at least one element used in the library.
+  final Set<ImportDirective> _usedImports;
+
+  /// The mapping of each import to its "used elements" set.
+  ///
+  /// This is computed in [processUsedElements].
+  final Map<ImportDirective, Set<Element>> _usedElementSets = {};
+
+  _UnnecessaryImportsVerifier(this._namespaceMap, this._usedImports);
+
+  /// Determines the "used elements" set for each import directive in
+  /// [allImports].
+  void processUsedElements(
+    List<UsedImportedElements> usedImportedElementsList,
+    Map<PrefixElement, List<ImportDirective>> prefixElementMap,
+    List<ImportDirective> allImports,
+  ) {
+    assert(_usedElementSets.isEmpty);
+    for (var usedElements in usedImportedElementsList) {
+      _processPrefixedElements(usedElements, prefixElementMap);
+      _processUnprefixedElements(usedElements);
+      _processExtensionElements(usedElements, allImports);
+    }
+  }
+
+  /// Reports the import directives which are unnecessary.
+  void reportImports(ErrorReporter errorReporter) {
+    for (var importDirective in _usedImports) {
+      if (!_usedElementSets.containsKey(importDirective)) continue;
+      for (var otherImport in _usedImports) {
+        if (otherImport == importDirective) continue;
+        if (importDirective.prefix?.name != otherImport.prefix?.name) continue;
+        if (!_usedElementSets.containsKey(otherImport)) continue;
+        var importElementSet = _usedElementSets[importDirective]!;
+        var otherElementSet = _usedElementSets[otherImport]!;
+        if (otherElementSet.containsAll(importElementSet)) {
+          if (otherElementSet.length > importElementSet.length) {
+            StringLiteral uri = importDirective.uri;
+            errorReporter.reportErrorForNode(HintCode.UNNECESSARY_IMPORT, uri,
+                [uri.stringValue, otherImport.uri.stringValue]);
+            // Break out of the loop of "other imports" to prevent reporting
+            // UNNECESSARY_IMPORT on [importDirective] multiple times.
+            break;
+          }
+        }
+      }
+    }
+  }
+
+  void _processExtensionElements(
+      UsedImportedElements usedElements, List<ImportDirective> allImports) {
+    for (ExtensionElement extensionElement in usedElements.usedExtensions) {
+      var elementName = extensionElement.name;
+      if (elementName == null) break;
+      // Find import directives using namespaces.
+      for (ImportDirective importDirective in allImports) {
+        var namespace = _namespaceMap.computeNamespace(importDirective);
+        if (namespace == null) {
+          continue;
+        }
+        var prefix = importDirective.prefix?.name;
+        if (prefix == null) {
+          if (namespace.get(elementName) == extensionElement) {
+            _usedElementSets
+                .putIfAbsent(importDirective, () => {})
+                .add(extensionElement);
+          }
+        } else {
+          // An extension might be used solely because one or more instance
+          // members are referenced, which does not require explicit use of
+          // the prefix. We still indicate that the import directive is used.
+          if (namespace.getPrefixed(prefix, elementName) == extensionElement) {
+            _usedElementSets
+                .putIfAbsent(importDirective, () => {})
+                .add(extensionElement);
+          }
+        }
+      }
+    }
+  }
+
+  void _processPrefixedElements(UsedImportedElements usedElements,
+      Map<PrefixElement, List<ImportDirective>> prefixElementMap) {
+    usedElements.prefixMap
+        .forEach((PrefixElement prefix, List<Element> elements) {
+      var importsForPrefix = prefixElementMap[prefix];
+      if (importsForPrefix == null) {
+        return;
+      }
+      for (var importDirective in importsForPrefix) {
+        var namespace = _namespaceMap.computeNamespace(importDirective);
+        if (namespace == null) {
+          continue;
+        }
+        for (var element in elements) {
+          if (namespace.providesPrefixed(prefix.name, element)) {
+            _usedElementSets
+                .putIfAbsent(importDirective, () => {})
+                .add(element);
+          }
+        }
+      }
+    });
+  }
+
+  void _processUnprefixedElements(UsedImportedElements usedElements) {
+    for (var element in usedElements.elements) {
+      for (var importDirective in _usedImports) {
+        var namespace = _namespaceMap.computeNamespace(importDirective);
+        if (namespace == null) {
+          continue;
+        }
+        if (namespace.provides(element)) {
+          _usedElementSets.putIfAbsent(importDirective, () => {}).add(element);
+        }
+      }
+    }
+  }
+}
+
+extension on Map<ImportDirective, Namespace> {
+  /// Lookup and return the [Namespace] in this Map.
+  ///
+  /// If this map does not have the computed namespace, compute it and cache it
+  /// in this map. If [importDirective] is not resolved or is not resolvable,
+  /// `null` is returned.
+  Namespace? computeNamespace(ImportDirective importDirective) {
+    var namespace = this[importDirective];
+    if (namespace == null) {
+      var importElement = importDirective.element;
+      if (importElement != null) {
+        namespace = importElement.namespace;
+        this[importDirective] = namespace;
+      }
+    }
+    return namespace;
+  }
+}
+
+extension on Namespace {
+  /// Returns whether this provides [element], taking into account system
+  /// library shadowing.
+  bool provides(Element element) {
+    var elementFromNamespace = get(element.name!);
+    return elementFromNamespace != null &&
+        !_isShadowing(element, elementFromNamespace);
+  }
+
+  /// Returns whether this provides [element] with [prefix], taking into account
+  /// system library shadowing.
+  bool providesPrefixed(String prefix, Element element) {
+    var elementFromNamespace = getPrefixed(prefix, element.name!);
+    return elementFromNamespace != null &&
+        !_isShadowing(element, elementFromNamespace);
+  }
+
+  /// Returns whether [e1] shadows [e2], assuming each is an imported element,
+  /// and that each is imported with the same prefix.
+  ///
+  /// Returns false if the source of either element is `null`.
+  bool _isShadowing(Element e1, Element e2) {
+    var source1 = e1.source;
+    if (source1 == null) {
+      return false;
+    }
+    var source2 = e2.source;
+    if (source2 == null) {
+      return false;
+    }
+    return !source1.isInSystemLibrary && source2.isInSystemLibrary;
+  }
+}
diff --git a/pkg/analyzer/lib/src/summary2/element_builder.dart b/pkg/analyzer/lib/src/summary2/element_builder.dart
index 3f10367..d4aa9f2 100644
--- a/pkg/analyzer/lib/src/summary2/element_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/element_builder.dart
@@ -219,7 +219,11 @@
       _linker.elementNodes[element] = node;
       _enclosingContext.addParameter(null, element);
     }
-    element.hasImplicitType = node.type == null && node.parameters == null;
+
+    // TODO(scheglov) https://github.com/dart-lang/sdk/issues/46039
+    // element.hasImplicitType = node.type == null && node.parameters == null;
+    element.hasImplicitType = false;
+
     element.isExplicitlyCovariant = node.covariantKeyword != null;
     element.isFinal = node.isFinal;
     element.metadata = _buildAnnotations(node.metadata);
@@ -631,18 +635,31 @@
     required PropertyAccessorElementImpl accessorElement,
   }) {
     var enclosingRef = _enclosingContext.reference;
-    var isTopLevel = enclosingRef.isUnit;
-    var containerName = isTopLevel ? '@variable' : '@field';
-    var containerRef = enclosingRef.getChild(containerName);
+    var enclosingElement = _enclosingContext.element;
 
-    var propertyRef = containerRef.getChild(name);
-    var property = propertyRef.element as PropertyInducingElementImpl?;
-    if (property == null) {
-      var variable = TopLevelVariableElementImpl(name, -1);
-      variable.isSynthetic = true;
-      variable.isFinal = accessorElement.isGetter;
-      property = variable;
-      _enclosingContext.addTopLevelVariable(name, variable);
+    PropertyInducingElementImpl? property;
+    if (enclosingElement is CompilationUnitElement) {
+      var containerRef = enclosingRef.getChild('@variable');
+      var propertyRef = containerRef.getChild(name);
+      property = propertyRef.element as PropertyInducingElementImpl?;
+      if (property == null) {
+        var variable = TopLevelVariableElementImpl(name, -1);
+        variable.isSynthetic = true;
+        variable.isFinal = accessorElement.isGetter;
+        _enclosingContext.addTopLevelVariable(name, variable);
+        property = variable;
+      }
+    } else {
+      var containerRef = enclosingRef.getChild('@field');
+      var propertyRef = containerRef.getChild(name);
+      property = propertyRef.element as PropertyInducingElementImpl?;
+      if (property == null) {
+        var field = FieldElementImpl(name, -1);
+        field.isSynthetic = true;
+        field.isFinal = accessorElement.isGetter;
+        _enclosingContext.addField(name, field);
+        property = field;
+      }
     }
 
     accessorElement.variable = property;
diff --git a/pkg/analyzer/lib/src/summary2/reference_resolver.dart b/pkg/analyzer/lib/src/summary2/reference_resolver.dart
index ecb1d5b..64c3567 100644
--- a/pkg/analyzer/lib/src/summary2/reference_resolver.dart
+++ b/pkg/analyzer/lib/src/summary2/reference_resolver.dart
@@ -37,10 +37,6 @@
   /// Indicates whether the library is opted into NNBD.
   final bool isNNBD;
 
-  /// The depth-first number of the next [GenericFunctionType] node.
-  int _nextGenericFunctionTypeId = 0;
-
-  Reference? reference;
   Scope scope;
 
   ReferenceResolver(
@@ -50,8 +46,7 @@
     this.unitReference,
     this.isNNBD,
     this.scope,
-  )   : _typeSystem = libraryElement.typeSystem,
-        reference = unitReference;
+  ) : _typeSystem = libraryElement.typeSystem;
 
   @override
   void visitBlockFunctionBody(BlockFunctionBody node) {}
@@ -59,10 +54,8 @@
   @override
   void visitClassDeclaration(ClassDeclaration node) {
     var outerScope = scope;
-    var outerReference = reference;
 
     var element = node.declaredElement as ClassElementImpl;
-    reference = element.reference;
     element.accessors; // create elements
     element.constructors; // create elements
     element.methods; // create elements
@@ -82,16 +75,13 @@
     nodesToBuildType.addDeclaration(node);
 
     scope = outerScope;
-    reference = outerReference;
   }
 
   @override
   void visitClassTypeAlias(ClassTypeAlias node) {
     var outerScope = scope;
-    var outerReference = reference;
 
     var element = node.declaredElement as ClassElementImpl;
-    reference = element.reference;
 
     _createTypeParameterElements(element, node.typeParameters);
     scope = TypeParameterScope(scope, element.typeParameters);
@@ -104,7 +94,6 @@
     nodesToBuildType.addDeclaration(node);
 
     scope = outerScope;
-    reference = outerReference;
   }
 
   @override
@@ -116,10 +105,8 @@
   @override
   void visitConstructorDeclaration(ConstructorDeclaration node) {
     var outerScope = scope;
-    var outerReference = reference;
 
     var element = node.declaredElement as ConstructorElementImpl;
-    reference = element.reference;
     element.parameters; // create elements
 
     scope = TypeParameterScope(scope, element.typeParameters);
@@ -128,7 +115,6 @@
     node.parameters.accept(this);
 
     scope = outerScope;
-    reference = outerReference;
   }
 
   @override
@@ -150,10 +136,8 @@
   @override
   void visitExtensionDeclaration(ExtensionDeclaration node) {
     var outerScope = scope;
-    var outerReference = reference;
 
     var element = node.declaredElement as ExtensionElementImpl;
-    reference = element.reference;
 
     _createTypeParameterElements(element, node.typeParameters);
     scope = TypeParameterScope(scope, element.typeParameters);
@@ -168,7 +152,6 @@
     nodesToBuildType.addDeclaration(node);
 
     scope = outerScope;
-    reference = outerReference;
   }
 
   @override
@@ -179,10 +162,8 @@
   @override
   void visitFieldFormalParameter(FieldFormalParameter node) {
     var outerScope = scope;
-    var outerReference = reference;
 
     var element = node.declaredElement as FieldFormalParameterElementImpl;
-    reference = element.reference;
     element.parameters; // create elements
 
     _createTypeParameterElements(element, node.typeParameters);
@@ -194,7 +175,6 @@
     nodesToBuildType.addDeclaration(node);
 
     scope = outerScope;
-    reference = outerReference;
   }
 
   @override
@@ -205,10 +185,8 @@
   @override
   void visitFunctionDeclaration(FunctionDeclaration node) {
     var outerScope = scope;
-    var outerReference = reference;
 
     var element = node.declaredElement as ExecutableElementImpl;
-    reference = element.reference;
     element.parameters; // create elements
 
     _createTypeParameterElements(
@@ -223,7 +201,6 @@
     nodesToBuildType.addDeclaration(node);
 
     scope = outerScope;
-    reference = outerReference;
   }
 
   @override
@@ -235,10 +212,8 @@
   @override
   void visitFunctionTypeAlias(FunctionTypeAlias node) {
     var outerScope = scope;
-    var outerReference = reference;
 
     var element = node.declaredElement as TypeAliasElementImpl;
-    reference = element.reference;
 
     _createTypeParameterElements(element, node.typeParameters);
     scope = TypeParameterScope(outerScope, element.typeParameters);
@@ -247,23 +222,19 @@
     node.typeParameters?.accept(this);
 
     var function = element.aliasedElement as GenericFunctionTypeElementImpl;
-    reference = function.reference;
     function.parameters; // create elements
     node.parameters.accept(this);
 
     nodesToBuildType.addDeclaration(node);
 
     scope = outerScope;
-    reference = outerReference;
   }
 
   @override
   void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
     var outerScope = scope;
-    var outerReference = reference;
 
     var element = node.declaredElement as ParameterElementImpl;
-    reference = element.reference;
     element.parameters; // create elements
 
     _createTypeParameterElements(element, node.typeParameters);
@@ -275,23 +246,15 @@
     nodesToBuildType.addDeclaration(node);
 
     scope = outerScope;
-    reference = outerReference;
   }
 
   @override
   void visitGenericFunctionType(GenericFunctionType node) {
     var nodeImpl = node as GenericFunctionTypeImpl;
     var outerScope = scope;
-    var outerReference = reference;
-
-    // TODO(scheglov) remove reference
-    var id = _nextGenericFunctionTypeId++;
-    var containerRef = unitReference.getChild('@genericFunctionType');
-    reference = containerRef.getChild('$id');
 
     var element = GenericFunctionTypeElementImpl.forLinkedNode(
       unitReference.element as CompilationUnitElementImpl,
-      reference,
       node,
     );
     element.parameters; // create elements
@@ -309,16 +272,13 @@
     nodesToBuildType.addTypeBuilder(builder);
 
     scope = outerScope;
-    reference = outerReference;
   }
 
   @override
   void visitGenericTypeAlias(GenericTypeAlias node) {
     var outerScope = scope;
-    var outerReference = reference;
 
     var element = node.declaredElement as TypeAliasElementImpl;
-    reference = element.reference;
 
     _createTypeParameterElements(element, node.typeParameters);
     scope = TypeParameterScope(outerScope, element.typeParameters);
@@ -335,7 +295,6 @@
     }
 
     scope = outerScope;
-    reference = outerReference;
   }
 
   @override
@@ -346,10 +305,8 @@
   @override
   void visitMethodDeclaration(MethodDeclaration node) {
     var outerScope = scope;
-    var outerReference = reference;
 
     var element = node.declaredElement as ExecutableElementImpl;
-    reference = element.reference;
     element.parameters; // create elements
 
     _createTypeParameterElements(element, node.typeParameters);
@@ -363,16 +320,13 @@
     nodesToBuildType.addDeclaration(node);
 
     scope = outerScope;
-    reference = outerReference;
   }
 
   @override
   void visitMixinDeclaration(MixinDeclaration node) {
     var outerScope = scope;
-    var outerReference = reference;
 
     var element = node.declaredElement as MixinElementImpl;
-    reference = element.reference;
     element.accessors; // create elements
     element.constructors; // create elements
     element.methods; // create elements
@@ -391,7 +345,6 @@
     nodesToBuildType.addDeclaration(node);
 
     scope = outerScope;
-    reference = outerReference;
   }
 
   @override
diff --git a/pkg/analyzer/lib/src/summary2/top_level_inference.dart b/pkg/analyzer/lib/src/summary2/top_level_inference.dart
index d63db66..158f28a 100644
--- a/pkg/analyzer/lib/src/summary2/top_level_inference.dart
+++ b/pkg/analyzer/lib/src/summary2/top_level_inference.dart
@@ -7,6 +7,7 @@
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/scope.dart';
 import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/dart/ast/ast.dart';
 import 'package:analyzer/src/dart/ast/extensions.dart';
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/type.dart';
@@ -132,7 +133,7 @@
   _ConstructorInferenceNode(this._walker, this._constructor) {
     for (var parameter in _constructor.parameters) {
       if (parameter is FieldFormalParameterElementImpl) {
-        if (parameter.hasImplicitType) {
+        if (_hasImplicitType(parameter)) {
           var field = parameter.field;
           if (field != null) {
             _parameters.add(
@@ -172,6 +173,18 @@
     }
     isEvaluated = true;
   }
+
+  /// TODO(scheglov) https://github.com/dart-lang/sdk/issues/46039
+  bool _hasImplicitType(FieldFormalParameterElementImpl parameter) {
+    var parameterNode = _walker._linker.getLinkingNode(parameter);
+    if (parameterNode is DefaultFormalParameter) {
+      parameterNode = parameterNode.parameter;
+    }
+    return parameterNode is FieldFormalParameterImpl &&
+        parameterNode.type == null &&
+        parameterNode.parameters == null;
+    // return parameter.hasImplicitType;
+  }
 }
 
 /// A field formal parameter with a non-nullable field.
diff --git a/pkg/analyzer/test/src/diagnostics/avoid_types_as_parameter_names_test.dart b/pkg/analyzer/test/src/diagnostics/avoid_types_as_parameter_names_test.dart
new file mode 100644
index 0000000..f6563a0
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/avoid_types_as_parameter_names_test.dart
@@ -0,0 +1,48 @@
+// 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:analyzer/src/dart/error/lint_codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+/// TODO(scheglov) Remove the file after fixing the linter.
+/// https://github.com/dart-lang/sdk/issues/46039
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(AvoidTypesAsParameterNamesTest);
+  });
+}
+
+@reflectiveTest
+class AvoidTypesAsParameterNamesTest extends PubPackageResolutionTest {
+  @override
+  void setUp() {
+    super.setUp();
+    writeTestPackageAnalysisOptionsFile(
+      AnalysisOptionsFileConfig(
+        lints: [
+          'avoid_types_as_parameter_names',
+        ],
+      ),
+    );
+  }
+
+  test_fieldFormalParameter() async {
+    await assertNoErrorsInCode(r'''
+class A {
+  final int num;
+  const A(this.num);
+}
+''');
+  }
+
+  test_simpleFormalParameter_function() async {
+    await assertErrorsInCode(r'''
+void f(int) {}
+''', [
+      error(LintCode('avoid_types_as_parameter_names', ''), 7, 3),
+    ]);
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/duplicate_import_test.dart b/pkg/analyzer/test/src/diagnostics/duplicate_import_test.dart
index 427f023..85eb884 100644
--- a/pkg/analyzer/test/src/diagnostics/duplicate_import_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/duplicate_import_test.dart
@@ -63,7 +63,6 @@
 library L;
 import 'lib1.dart';
 import 'lib1.dart' hide A;
-A a = A();
 B b = B();
 ''');
 
@@ -75,12 +74,13 @@
     newFile('$testPackageLibPath/lib1.dart', content: r'''
 library lib1;
 class A {}
-class B {}''');
+class B {}
+''');
 
     newFile('$testPackageLibPath/lib2.dart', content: r'''
 library L;
 import 'lib1.dart';
-import 'lib1.dart' show A;
+import 'lib1.dart' show A; // ignore: unnecessary_import
 A a = A();
 B b = B();
 ''');
diff --git a/pkg/analyzer/test/src/diagnostics/mixin_declares_constructor_test.dart b/pkg/analyzer/test/src/diagnostics/mixin_declares_constructor_test.dart
index 00b7f90..89c5750 100644
--- a/pkg/analyzer/test/src/diagnostics/mixin_declares_constructor_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/mixin_declares_constructor_test.dart
@@ -4,6 +4,7 @@
 
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/src/dart/error/syntactic_errors.dart';
+import 'package:analyzer/src/error/codes.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import '../dart/resolution/context_collection_resolution.dart';
@@ -24,6 +25,10 @@
 }
 ''', [
       error(ParserErrorCode.MIXIN_DECLARES_CONSTRUCTOR, 27, 1),
+      // TODO(srawlins): Don't report this from within a mixin.
+      // TODO(scheglov) https://github.com/dart-lang/sdk/issues/46039
+      error(
+          CompileTimeErrorCode.FIELD_INITIALIZING_FORMAL_NOT_ASSIGNABLE, 29, 6),
     ]);
 
     var element = findElement.mixin('M');
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 29b0461..5be1286 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -34,6 +34,8 @@
 import 'async_for_in_wrong_context_test.dart' as async_for_in_wrong_context;
 import 'async_keyword_used_as_identifier_test.dart'
     as async_keyword_used_as_identifier;
+import 'avoid_types_as_parameter_names_test.dart'
+    as avoid_types_as_parameter_names;
 import 'await_in_late_local_variable_initializer_test.dart'
     as await_in_late_local_variable_initializer;
 import 'await_in_wrong_context_test.dart' as await_in_wrong_context;
@@ -646,6 +648,7 @@
 import 'unignorable_ignore_test.dart' as unignorable_ignore;
 import 'unnecessary_cast_test.dart' as unnecessary_cast;
 import 'unnecessary_ignore_test.dart' as unnecessary_ignore;
+import 'unnecessary_import_test.dart' as unnecessary_import;
 import 'unnecessary_no_such_method_test.dart' as unnecessary_no_such_method;
 import 'unnecessary_non_null_assertion_test.dart'
     as unnecessary_non_null_assertion;
@@ -709,6 +712,7 @@
     assignment_to_type.main();
     async_for_in_wrong_context.main();
     async_keyword_used_as_identifier.main();
+    avoid_types_as_parameter_names.main();
     await_in_late_local_variable_initializer.main();
     await_in_wrong_context.main();
     binary_operator_written_out.main();
@@ -1115,6 +1119,7 @@
     undefined_setter.main();
     undefined_shown_name.main();
     unignorable_ignore.main();
+    unnecessary_import.main();
     unnecessary_cast.main();
     unnecessary_ignore.main();
     unnecessary_no_such_method.main();
diff --git a/pkg/analyzer/test/src/diagnostics/unnecessary_import_test.dart b/pkg/analyzer/test/src/diagnostics/unnecessary_import_test.dart
new file mode 100644
index 0000000..b6071fc
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/unnecessary_import_test.dart
@@ -0,0 +1,228 @@
+// 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:analyzer/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(UnnecessaryImportTest);
+  });
+}
+
+@reflectiveTest
+class UnnecessaryImportTest extends PubPackageResolutionTest {
+  test_annotationOnDirective() async {
+    newFile('$testPackageLibPath/lib1.dart', content: r'''
+class A {
+  const A() {}
+}
+''');
+    await assertNoErrorsInCode(r'''
+@A()
+import 'lib1.dart';
+''');
+  }
+
+  test_as() async {
+    newFile('$testPackageLibPath/lib1.dart', content: '''
+class A {}
+''');
+    newFile('$testPackageLibPath/lib2.dart', content: '''
+export 'lib1.dart';
+class B {}
+''');
+    await assertNoErrorsInCode('''
+import 'lib1.dart';
+import 'lib2.dart' as two;
+f(A a, two.B b) {}
+''');
+  }
+
+  test_as_differentPrefixes() async {
+    newFile('$testPackageLibPath/lib1.dart', content: '''
+class A {}
+''');
+    newFile('$testPackageLibPath/lib2.dart', content: '''
+export 'lib1.dart';
+class B {}
+''');
+    await assertNoErrorsInCode('''
+import 'lib1.dart' as one;
+import 'lib2.dart' as two;
+f(one.A a, two.B b) {}
+''');
+  }
+
+  test_as_equalPrefixes_referenced() async {
+    newFile('$testPackageLibPath/lib1.dart', content: r'''
+class A {}
+''');
+    newFile('$testPackageLibPath/lib2.dart', content: r'''
+class B {}
+''');
+    await assertNoErrorsInCode(r'''
+import 'lib1.dart' as one;
+import 'lib2.dart' as one;
+f(one.A a, one.B b) {}
+''');
+  }
+
+  test_as_equalPrefixes_referenced_via_export() async {
+    newFile('$testPackageLibPath/lib1.dart', content: r'''
+class A {}
+''');
+    newFile('$testPackageLibPath/lib2.dart', content: r'''
+class B {}
+''');
+    newFile('$testPackageLibPath/lib3.dart', content: r'''
+export 'lib2.dart';
+''');
+    await assertNoErrorsInCode(r'''
+import 'lib1.dart' as one;
+import 'lib3.dart' as one;
+f(one.A a, one.B b) {}
+''');
+  }
+
+  test_as_equalPrefixes_unreferenced() async {
+    newFile('$testPackageLibPath/lib1.dart', content: r'''
+class A {}
+''');
+    newFile('$testPackageLibPath/lib2.dart', content: r'''
+class B {}
+''');
+    await assertNoErrorsInCode(r'''
+import 'lib1.dart' as one;
+import 'lib2.dart' as one; // ignore: unused_import
+f(one.A a) {}
+''');
+  }
+
+  test_as_show_multipleElements() async {
+    newFile('$testPackageLibPath/lib1.dart', content: r'''
+class A {}
+class B {}
+''');
+    await assertNoErrorsInCode(r'''
+import 'lib1.dart' as one show A, B;
+f(one.A a, one.B b) {}
+''');
+  }
+
+  test_as_showTopLevelFunction() async {
+    newFile('$testPackageLibPath/lib1.dart', content: r'''
+class One {}
+topLevelFunction() {}
+''');
+    await assertNoErrorsInCode(r'''
+import 'lib1.dart' hide topLevelFunction;
+import 'lib1.dart' as one show topLevelFunction;
+f(One o) {
+  one.topLevelFunction();
+}
+''');
+  }
+
+  test_as_showTopLevelFunction_multipleDirectives() async {
+    newFile('$testPackageLibPath/lib1.dart', content: r'''
+class One {}
+topLevelFunction() {}
+''');
+    await assertNoErrorsInCode(r'''
+import 'lib1.dart' hide topLevelFunction;
+import 'lib1.dart' as one show topLevelFunction;
+import 'lib1.dart' as two show topLevelFunction;
+f(One o) {
+  one.topLevelFunction();
+  two.topLevelFunction();
+}
+''');
+  }
+
+  test_as_systemShadowing() async {
+    newFile('$testPackageLibPath/lib1.dart', content: '''
+class File {}
+''');
+    await assertNoErrorsInCode('''
+import 'dart:io' as io;
+import 'lib1.dart' as io;
+g(io.Directory d, io.File f) {}
+''');
+  }
+
+  test_as_unnecessary() async {
+    newFile('$testPackageLibPath/lib1.dart', content: '''
+class A {}
+''');
+    newFile('$testPackageLibPath/lib2.dart', content: '''
+export 'lib1.dart';
+class B {}
+''');
+    await assertErrorsInCode('''
+import 'lib1.dart' as p;
+import 'lib2.dart' as p;
+f(p.A a, p.B b) {}
+''', [
+      error(HintCode.UNNECESSARY_IMPORT, 7, 11),
+    ]);
+  }
+
+  test_duplicteImport_differentPrefix() async {
+    newFile('$testPackageLibPath/lib1.dart', content: '''
+class A {}
+class B {}
+''');
+    await assertNoErrorsInCode('''
+import 'lib1.dart';
+import 'lib1.dart' as p;
+f(A a1, p.A a2, B b) {}
+''');
+  }
+
+  test_hide() async {
+    newFile('$testPackageLibPath/lib1.dart', content: '''
+class A {}
+''');
+    newFile('$testPackageLibPath/lib2.dart', content: '''
+export 'lib1.dart' hide A;
+class B {}
+''');
+    await assertNoErrorsInCode('''
+import 'lib1.dart';
+import 'lib2.dart';
+f(A a, B b) {}
+''');
+  }
+
+  test_systemShadowing() async {
+    newFile('$testPackageLibPath/lib1.dart', content: '''
+class File {}
+''');
+    await assertNoErrorsInCode('''
+import 'dart:io';
+import 'lib1.dart';
+g(Directory d, File f) {}
+''');
+  }
+
+  test_unnecessaryImport() async {
+    newFile('$testPackageLibPath/lib1.dart', content: '''
+class A {}
+''');
+    newFile('$testPackageLibPath/lib2.dart', content: '''
+export 'lib1.dart';
+class B {}
+''');
+    await assertErrorsInCode('''
+import 'lib1.dart';
+import 'lib2.dart';
+f(A a, B b) {}
+''', [
+      error(HintCode.UNNECESSARY_IMPORT, 7, 11),
+    ]);
+  }
+}
diff --git a/pkg/analyzer/test/src/summary/element_text.dart b/pkg/analyzer/test/src/summary/element_text.dart
index 978991d..38c1c87 100644
--- a/pkg/analyzer/test/src/summary/element_text.dart
+++ b/pkg/analyzer/test/src/summary/element_text.dart
@@ -922,7 +922,8 @@
       }
     }
 
-    if (e.enclosingElement is ClassElement) {
+    if (e.enclosingElement is ClassElement ||
+        e.enclosingElement is ExtensionElement) {
       writeDocumentation(e, '  ');
       writeMetadata(e, '  ', '\n');
 
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index 8734347..2cf8b1b 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -13106,6 +13106,21 @@
 ''');
   }
 
+  test_type_inference_using_extension_getter() async {
+    var library = await checkLibrary('''
+extension on String {
+  int get foo => 0;
+}
+var v = 'a'.foo;
+''');
+    checkElementText(library, '''
+extension  on String {
+  int get foo {}
+}
+int v;
+''');
+  }
+
   test_type_invalid_topLevelVariableElement_asType() async {
     var library = await checkLibrary('''
 class C<T extends V> {}
diff --git a/pkg/dartdev/README.md b/pkg/dartdev/README.md
index eaee08f..6679562 100644
--- a/pkg/dartdev/README.md
+++ b/pkg/dartdev/README.md
@@ -3,7 +3,7 @@
 ```
 A command-line utility for Dart development.
 
-Usage: dart [<vm-flags>] <command|dart-file> [<arguments>]
+Usage: dart [vm-options] <command|dart-file> [arguments]
 
 Global options:
 -h, --help                 Print this usage information.
diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart
index 5e581c5..cbe7945 100644
--- a/pkg/dartdev/lib/dartdev.dart
+++ b/pkg/dartdev/lib/dartdev.dart
@@ -69,12 +69,14 @@
     allowTrailingOptions: false,
   );
 
+  final bool verbose;
+
   static const String dartdevDescription =
       'A command-line utility for Dart development';
 
-  DartdevRunner(List<String> args) : super('dart', '$dartdevDescription.') {
-    final bool verbose = args.contains('-v') || args.contains('--verbose');
-
+  DartdevRunner(List<String> args)
+      : verbose = args.contains('-v') || args.contains('--verbose'),
+        super('dart', '$dartdevDescription.') {
     argParser.addFlag('verbose',
         abbr: 'v', negatable: false, help: 'Show additional command output.');
     argParser.addFlag('version',
@@ -112,7 +114,7 @@
 
   @override
   String get invocation =>
-      'dart [<vm-flags>] <command|dart-file> [<arguments>]';
+      'dart ${verbose ? '[vm-options] ' : ''}<command|dart-file> [arguments]';
 
   @visibleForTesting
   Analytics get analytics => _analytics;
diff --git a/pkg/dartdev/lib/src/commands/analyze.dart b/pkg/dartdev/lib/src/commands/analyze.dart
index 3292669..ea90429 100644
--- a/pkg/dartdev/lib/src/commands/analyze.dart
+++ b/pkg/dartdev/lib/src/commands/analyze.dart
@@ -36,7 +36,7 @@
   static final int _return = '\r'.codeUnitAt(0);
 
   AnalyzeCommand({bool verbose = false})
-      : super(cmdName, 'Analyze Dart code in a directory.') {
+      : super(cmdName, 'Analyze Dart code in a directory.', verbose) {
     argParser
       ..addFlag('fatal-infos',
           help: 'Treat info level issues as fatal.', negatable: false)
diff --git a/pkg/dartdev/lib/src/commands/compile.dart b/pkg/dartdev/lib/src/commands/compile.dart
index c7e2e8f..3b476e4 100644
--- a/pkg/dartdev/lib/src/commands/compile.dart
+++ b/pkg/dartdev/lib/src/commands/compile.dart
@@ -67,7 +67,7 @@
   static const String cmdName = 'js';
 
   CompileJSCommand({bool verbose})
-      : super(cmdName, 'Compile Dart to JavaScript.') {
+      : super(cmdName, 'Compile Dart to JavaScript.', verbose) {
     argParser
       ..addOption(
         commonOptions['outputFile'].flag,
@@ -173,7 +173,7 @@
     this.fileExt,
     this.formatName,
     bool verbose,
-  }) : super(commandName, 'Compile Dart $help') {
+  }) : super(commandName, 'Compile Dart $help', verbose) {
     argParser
       ..addOption(
         commonOptions['outputFile'].flag,
@@ -267,7 +267,7 @@
     this.format,
     this.help,
     bool verbose,
-  }) : super(commandName, 'Compile Dart $help') {
+  }) : super(commandName, 'Compile Dart $help', verbose) {
     argParser
       ..addOption(
         commonOptions['outputFile'].flag,
@@ -348,15 +348,15 @@
 }
 
 abstract class CompileSubcommandCommand extends DartdevCommand {
-  CompileSubcommandCommand(String name, String description,
+  CompileSubcommandCommand(String name, String description, bool verbose,
       {bool hidden = false})
-      : super(name, description, hidden: hidden);
+      : super(name, description, verbose, hidden: hidden);
 }
 
 class CompileCommand extends DartdevCommand {
   static const String cmdName = 'compile';
   CompileCommand({bool verbose = false})
-      : super(cmdName, 'Compile Dart to various formats.') {
+      : super(cmdName, 'Compile Dart to various formats.', verbose) {
     addSubcommand(CompileJSCommand(
       verbose: verbose,
     ));
diff --git a/pkg/dartdev/lib/src/commands/create.dart b/pkg/dartdev/lib/src/commands/create.dart
index d387bd4..c7a3ca9 100644
--- a/pkg/dartdev/lib/src/commands/create.dart
+++ b/pkg/dartdev/lib/src/commands/create.dart
@@ -24,7 +24,7 @@
       generators.map((generator) => generator.id).toList();
 
   CreateCommand({bool verbose = false})
-      : super(cmdName, 'Create a new Dart project.') {
+      : super(cmdName, 'Create a new Dart project.', verbose) {
     argParser.addOption(
       'template',
       allowed: legalTemplateIds,
diff --git a/pkg/dartdev/lib/src/commands/fix.dart b/pkg/dartdev/lib/src/commands/fix.dart
index a9feca7..546d5fa 100644
--- a/pkg/dartdev/lib/src/commands/fix.dart
+++ b/pkg/dartdev/lib/src/commands/fix.dart
@@ -26,7 +26,7 @@
 
 To use the tool, run either ['dart fix --dry-run'] for a preview of the proposed changes for a project, or ['dart fix --apply'] to apply the changes.''';
 
-  FixCommand({bool verbose = false}) : super(cmdName, cmdDescription) {
+  FixCommand({bool verbose = false}) : super(cmdName, cmdDescription, verbose) {
     argParser.addFlag('dry-run',
         abbr: 'n',
         defaultsTo: false,
diff --git a/pkg/dartdev/lib/src/commands/language_server.dart b/pkg/dartdev/lib/src/commands/language_server.dart
index 924ae19..2821682 100644
--- a/pkg/dartdev/lib/src/commands/language_server.dart
+++ b/pkg/dartdev/lib/src/commands/language_server.dart
@@ -25,7 +25,7 @@
   https://github.com/dart-lang/sdk/tree/master/pkg/analysis_server''';
 
   LanguageServerCommand({bool verbose = false})
-      : super(commandName, commandDescription, hidden: !verbose);
+      : super(commandName, commandDescription, verbose, hidden: !verbose);
 
   @override
   ArgParser createArgParser() {
diff --git a/pkg/dartdev/lib/src/commands/run.dart b/pkg/dartdev/lib/src/commands/run.dart
index d996c3c..f6015bd 100644
--- a/pkg/dartdev/lib/src/commands/run.dart
+++ b/pkg/dartdev/lib/src/commands/run.dart
@@ -41,6 +41,7 @@
       : super(
           cmdName,
           'Run a Dart program.',
+          verbose,
         ) {
     // NOTE: When updating this list of flags, be sure to add any VM flags to
     // the list of flags in Options::ProcessVMDebuggingOptions in
@@ -167,7 +168,8 @@
   }
 
   @override
-  String get invocation => '${super.invocation} <dart file | package target>';
+  String get invocation =>
+      '${super.invocation} [<dart-file|package-target> [args]]';
 
   @override
   FutureOr<int> run() async {
diff --git a/pkg/dartdev/lib/src/commands/test.dart b/pkg/dartdev/lib/src/commands/test.dart
index 7261896..c6711c7 100644
--- a/pkg/dartdev/lib/src/commands/test.dart
+++ b/pkg/dartdev/lib/src/commands/test.dart
@@ -17,7 +17,7 @@
 class TestCommand extends DartdevCommand {
   static const String cmdName = 'test';
 
-  TestCommand() : super(cmdName, 'Run tests for a project.');
+  TestCommand() : super(cmdName, 'Run tests for a project.', false);
 
   // This argument parser is here solely to ensure that VM specific flags are
   // provided before any command and to provide a more consistent help message
diff --git a/pkg/dartdev/lib/src/core.dart b/pkg/dartdev/lib/src/core.dart
index 9a62f6d..2da96fc 100644
--- a/pkg/dartdev/lib/src/core.dart
+++ b/pkg/dartdev/lib/src/core.dart
@@ -21,13 +21,15 @@
 abstract class DartdevCommand extends Command<int> {
   final String _name;
   final String _description;
+  final bool _verbose;
 
   Project _project;
 
   @override
   final bool hidden;
 
-  DartdevCommand(this._name, this._description, {this.hidden = false});
+  DartdevCommand(this._name, this._description, this._verbose,
+      {this.hidden = false});
 
   @override
   String get name => _name;
@@ -40,6 +42,17 @@
   @override
   ArgParser get argParser => _argParser ??= createArgParser();
 
+  @override
+  String get invocation {
+    if (_verbose) {
+      final splitInvocation = super.invocation.split(' ');
+      splitInvocation.insert(1, '[vm-options]');
+      return splitInvocation.join(' ');
+    } else {
+      return super.invocation;
+    }
+  }
+
   /// Create the ArgParser instance for this command.
   ///
   /// Subclasses can override this in order to create a customized ArgParser.
diff --git a/pkg/dartdev/test/commands/analyze_test.dart b/pkg/dartdev/test/commands/analyze_test.dart
index 8017a0c..fdd5bac 100644
--- a/pkg/dartdev/test/commands/analyze_test.dart
+++ b/pkg/dartdev/test/commands/analyze_test.dart
@@ -19,6 +19,9 @@
 const String _analyzeUsageText =
     'Usage: dart analyze [arguments] [<directory>]';
 
+const String _analyzeVerboseUsageText =
+    'Usage: dart [vm-options] analyze [arguments] [<directory>]';
+
 const String _unusedImportAnalysisOptions = '''
 analyzer:
   errors:
@@ -72,6 +75,16 @@
     expect(result.stdout, contains(_analyzeUsageText));
   });
 
+  test('--help --verbose', () {
+    p = project();
+    var result = p.runSync(['analyze', '--help', '--verbose']);
+
+    expect(result.exitCode, 0);
+    expect(result.stderr, isEmpty);
+    expect(result.stdout, contains(_analyzeDescriptionText));
+    expect(result.stdout, contains(_analyzeVerboseUsageText));
+  });
+
   test('multiple directories', () {
     p = project();
     var result = p.runSync(['analyze', '/no/such/dir1/', '/no/such/dir2/']);
diff --git a/pkg/dartdev/test/commands/compile_test.dart b/pkg/dartdev/test/commands/compile_test.dart
index 6b47a28..4bf62f0 100644
--- a/pkg/dartdev/test/commands/compile_test.dart
+++ b/pkg/dartdev/test/commands/compile_test.dart
@@ -45,6 +45,27 @@
       ['compile', '--help'],
     );
     expect(result.stdout, contains('Compile Dart'));
+    expect(
+      result.stdout,
+      contains(
+        'Usage: dart compile <subcommand> [arguments]',
+      ),
+    );
+    expect(result.exitCode, 0);
+  });
+
+  test('--help --verbose', () {
+    final p = project();
+    final result = p.runSync(
+      ['compile', '--help', '--verbose'],
+    );
+    expect(result.stdout, contains('Compile Dart'));
+    expect(
+      result.stdout,
+      contains(
+        'Usage: dart [vm-options] compile <subcommand> [arguments]',
+      ),
+    );
     expect(result.exitCode, 0);
   });
 
diff --git a/pkg/dartdev/test/commands/create_test.dart b/pkg/dartdev/test/commands/create_test.dart
index 86b2d48..969120c 100644
--- a/pkg/dartdev/test/commands/create_test.dart
+++ b/pkg/dartdev/test/commands/create_test.dart
@@ -23,6 +23,36 @@
 
   tearDown(() => p?.dispose());
 
+  test('--help', () {
+    p = project();
+    var result = p.runSync(['create', '--help']);
+
+    expect(result.stdout, contains('Create a new Dart project.'));
+    expect(
+      result.stdout,
+      contains(
+        'Usage: dart create [arguments] <directory>',
+      ),
+    );
+    expect(result.stderr, isEmpty);
+    expect(result.exitCode, 0);
+  });
+
+  test('--help --verbose', () {
+    p = project();
+    var result = p.runSync(['create', '--help', '--verbose']);
+
+    expect(result.stdout, contains('Create a new Dart project.'));
+    expect(
+      result.stdout,
+      contains(
+        'Usage: dart [vm-options] create [arguments] <directory>',
+      ),
+    );
+    expect(result.stderr, isEmpty);
+    expect(result.exitCode, 0);
+  });
+
   test('default template exists', () {
     expect(CreateCommand.legalTemplateIds,
         contains(CreateCommand.defaultTemplateId));
diff --git a/pkg/dartdev/test/commands/fix_test.dart b/pkg/dartdev/test/commands/fix_test.dart
index 69b11c9..97cddf9 100644
--- a/pkg/dartdev/test/commands/fix_test.dart
+++ b/pkg/dartdev/test/commands/fix_test.dart
@@ -60,7 +60,7 @@
     return p.runSync(['fix', ...args], workingDir: workingDir);
   }
 
-  test('help', () {
+  test('--help', () {
     p = project(mainSrc: 'int get foo => 1;\n');
 
     var result = runFix([p.dirPath, '--help']);
@@ -68,7 +68,31 @@
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(
-        result.stdout, contains('Apply automated fixes to Dart source code.'));
+      result.stdout,
+      contains(
+        'Apply automated fixes to Dart source code.',
+      ),
+    );
+    expect(result.stdout, contains('Usage: dart fix [arguments]'));
+  });
+
+  test('--help --verbose', () {
+    p = project(mainSrc: 'int get foo => 1;\n');
+
+    var result = runFix([p.dirPath, '--help', '--verbose']);
+
+    expect(result.exitCode, 0);
+    expect(result.stderr, isEmpty);
+    expect(
+      result.stdout,
+      contains(
+        'Apply automated fixes to Dart source code.',
+      ),
+    );
+    expect(
+      result.stdout,
+      contains('Usage: dart [vm-options] fix [arguments]'),
+    );
   });
 
   test('none', () {
diff --git a/pkg/dartdev/test/commands/flag_test.dart b/pkg/dartdev/test/commands/flag_test.dart
index 0475ee3..bfcc02c 100644
--- a/pkg/dartdev/test/commands/flag_test.dart
+++ b/pkg/dartdev/test/commands/flag_test.dart
@@ -66,8 +66,8 @@
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains(DartdevRunner.dartdevDescription));
-    expect(result.stdout,
-        contains('Usage: dart [<vm-flags>] <command|dart-file> [<arguments>]'));
+    expect(
+        result.stdout, contains('Usage: dart <command|dart-file> [arguments]'));
     expect(result.stdout, contains('Global options:'));
     expect(result.stdout, contains('Available commands:'));
     expect(result.stdout, contains('analyze '));
@@ -104,8 +104,8 @@
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(result.stdout, contains(DartdevRunner.dartdevDescription));
-    expect(result.stdout,
-        contains('Usage: dart [<vm-flags>] <command|dart-file> [<arguments>]'));
+    expect(
+        result.stdout, contains('Usage: dart <command|dart-file> [arguments]'));
     expect(result.stdout, contains('Global options:'));
     expect(result.stdout, contains('Available commands:'));
     expect(result.stdout, contains('analyze '));
@@ -120,6 +120,8 @@
     var result = p.runSync(['help', '--verbose']);
 
     expect(result.exitCode, 0);
+    expect(result.stdout,
+        contains('Usage: dart [vm-options] <command|dart-file> [arguments]'));
     expect(result.stdout, contains('migrate '));
   });
 
@@ -128,6 +130,8 @@
     var result = p.runSync(['help', '-v']);
 
     expect(result.exitCode, 0);
+    expect(result.stdout,
+        contains('Usage: dart [vm-options] <command|dart-file> [arguments]'));
     expect(result.stdout, contains('migrate '));
   });
 }
diff --git a/pkg/dartdev/test/commands/migrate_test.dart b/pkg/dartdev/test/commands/migrate_test.dart
index 500d587..94795be 100644
--- a/pkg/dartdev/test/commands/migrate_test.dart
+++ b/pkg/dartdev/test/commands/migrate_test.dart
@@ -26,9 +26,31 @@
     expect(result.exitCode, 0);
     expect(result.stderr, isEmpty);
     expect(
-        result.stdout, contains('Perform null safety migration on a project.'));
-    expect(result.stdout,
-        contains('Usage: dart migrate [arguments] [project or directory]'));
+      result.stdout,
+      contains('Perform null safety migration on a project.'),
+    );
+    expect(
+      result.stdout,
+      contains('Usage: dart migrate [arguments] [project or directory]'),
+    );
+  });
+
+  test('--help --verbose', () {
+    p = project();
+    var result = p.runSync(['migrate', '--help', '--verbose']);
+
+    expect(result.exitCode, 0);
+    expect(result.stderr, isEmpty);
+    expect(
+      result.stdout,
+      contains('Perform null safety migration on a project.'),
+    );
+    expect(
+      result.stdout,
+      contains(
+        'Usage: dart [vm-options] migrate [arguments] [project or directory]',
+      ),
+    );
   });
 
   test('directory implicit', () {
diff --git a/pkg/dartdev/test/commands/run_test.dart b/pkg/dartdev/test/commands/run_test.dart
index f10f092..cfccec8 100644
--- a/pkg/dartdev/test/commands/run_test.dart
+++ b/pkg/dartdev/test/commands/run_test.dart
@@ -28,6 +28,28 @@
 
     expect(result.stdout, contains('Run a Dart program.'));
     expect(result.stdout, contains('Debugging options:'));
+    expect(
+      result.stdout,
+      contains(
+        'Usage: dart run [arguments] [<dart-file|package-target> [args]]',
+      ),
+    );
+    expect(result.stderr, isEmpty);
+    expect(result.exitCode, 0);
+  });
+
+  test('--help --verbose', () {
+    p = project();
+    var result = p.runSync(['run', '--help', '--verbose']);
+
+    expect(result.stdout, contains('Run a Dart program.'));
+    expect(result.stdout, contains('Debugging options:'));
+    expect(
+      result.stdout,
+      contains(
+        'Usage: dart [vm-options] run [arguments] [<dart-file|package-target> [args]]',
+      ),
+    );
     expect(result.stderr, isEmpty);
     expect(result.exitCode, 0);
   });
diff --git a/pkg/nnbd_migration/lib/migration_cli.dart b/pkg/nnbd_migration/lib/migration_cli.dart
index bfdec77..f1b09ac 100644
--- a/pkg/nnbd_migration/lib/migration_cli.dart
+++ b/pkg/nnbd_migration/lib/migration_cli.dart
@@ -152,7 +152,7 @@
   final bool verbose;
 
   MigrateCommand({this.verbose = false})
-      : super(cmdName, '$cmdDescription\n\n$migrationGuideLink') {
+      : super(cmdName, '$cmdDescription\n\n$migrationGuideLink', verbose) {
     MigrationCli._defineOptions(argParser, !verbose);
   }
 
diff --git a/sdk/lib/convert/ascii.dart b/sdk/lib/convert/ascii.dart
index 9d98016..9f62c2d 100644
--- a/sdk/lib/convert/ascii.dart
+++ b/sdk/lib/convert/ascii.dart
@@ -77,10 +77,6 @@
   Uint8List convert(String string, [int start = 0, int? end]) {
     var stringLength = string.length;
     end = RangeError.checkValidRange(start, end, stringLength);
-    // TODO(38725): Remove workaround when assignment promotion is implemented
-    if (end == null) {
-      throw RangeError("Invalid range");
-    }
     var length = end - start;
     var result = Uint8List(length);
     for (var i = 0; i < length; i++) {
@@ -167,10 +163,6 @@
   /// `start` to `end` (`end` not inclusive) is used as input to the conversion.
   String convert(List<int> bytes, [int start = 0, int? end]) {
     end = RangeError.checkValidRange(start, end, bytes.length);
-    // TODO(38725): Remove workaround when assignment promotion is implemented
-    if (end == null) {
-      throw RangeError("Invalid range");
-    }
     for (var i = start; i < end; i++) {
       var byte = bytes[i];
       if ((byte & ~_subsetMask) != 0) {
diff --git a/sdk/lib/convert/base64.dart b/sdk/lib/convert/base64.dart
index 5fcab0a..c4420c6 100644
--- a/sdk/lib/convert/base64.dart
+++ b/sdk/lib/convert/base64.dart
@@ -96,10 +96,6 @@
   /// * Validate that the length is correct (a multiple of four).
   String normalize(String source, [int start = 0, int? end]) {
     end = RangeError.checkValidRange(start, end, source.length);
-    // TODO(38725): Remove workaround when assignment promotion is implemented
-    if (end == null) {
-      throw RangeError("Invalid range");
-    }
     const percent = 0x25;
     const equals = 0x3d;
     StringBuffer? buffer;
@@ -407,10 +403,6 @@
     if (buffer == null || buffer.length < bufferLength) {
       bufferCache = buffer = Uint8List(bufferLength);
     }
-    // TODO(38725): Remove workaround when assignment promotion is implemented
-    if (buffer == null) {
-      throw "unreachable";
-    }
     // Return a view of the buffer, so it has the requested length.
     return Uint8List.view(buffer.buffer, buffer.offsetInBytes, bufferLength);
   }
@@ -490,10 +482,6 @@
   /// The [Uint8List.buffer] may be larger than the decoded bytes.
   Uint8List convert(String input, [int start = 0, int? end]) {
     end = RangeError.checkValidRange(start, end, input.length);
-    // TODO(38725): Remove workaround when assignment promotion is implemented
-    if (end == null) {
-      throw RangeError("Invalid range");
-    }
     if (start == end) return Uint8List(0);
     var decoder = _Base64Decoder();
     var buffer = decoder.decode(input, start, end)!;
diff --git a/sdk/lib/convert/html_escape.dart b/sdk/lib/convert/html_escape.dart
index 317e6c1..69b63a0 100644
--- a/sdk/lib/convert/html_escape.dart
+++ b/sdk/lib/convert/html_escape.dart
@@ -185,11 +185,6 @@
       }
       if (replacement != null) {
         result ??= StringBuffer();
-        // TODO(38725): Remove workaround when assignment promotion is
-        // implemented
-        if (result == null) {
-          throw "unreachable";
-        }
         if (i > start) result.write(text.substring(start, i));
         result.write(replacement);
         start = i + 1;
diff --git a/sdk/lib/convert/line_splitter.dart b/sdk/lib/convert/line_splitter.dart
index fa0be3f..85f0ab6 100644
--- a/sdk/lib/convert/line_splitter.dart
+++ b/sdk/lib/convert/line_splitter.dart
@@ -27,10 +27,6 @@
   /// (`0 <= start <= end <= lines.length`).
   static Iterable<String> split(String lines, [int start = 0, int? end]) sync* {
     end = RangeError.checkValidRange(start, end, lines.length);
-    // TODO(38725): Remove workaround when assignment promotion is implemented
-    if (end == null) {
-      throw RangeError("Invalid range");
-    }
     var sliceStart = start;
     var char = 0;
     for (var i = start; i < end; i++) {
diff --git a/sdk/lib/convert/utf.dart b/sdk/lib/convert/utf.dart
index bbfba77..57a2dac 100644
--- a/sdk/lib/convert/utf.dart
+++ b/sdk/lib/convert/utf.dart
@@ -88,10 +88,6 @@
   Uint8List convert(String string, [int start = 0, int? end]) {
     var stringLength = string.length;
     end = RangeError.checkValidRange(start, end, stringLength);
-    // TODO(38725): Remove workaround when assignment promotion is implemented
-    if (end == null) {
-      throw RangeError("Invalid range");
-    }
     var length = end - start;
     if (length == 0) return Uint8List(0);
     // Create a new encoder with a length that is guaranteed to be big enough.
diff --git a/sdk/lib/typed_data/typed_data.dart b/sdk/lib/typed_data/typed_data.dart
index df48112..5d07d41 100644
--- a/sdk/lib/typed_data/typed_data.dart
+++ b/sdk/lib/typed_data/typed_data.dart
@@ -475,8 +475,6 @@
     int elementSize = data.elementSizeInBytes;
     end = RangeError.checkValidRange(
         start, end, data.lengthInBytes ~/ elementSize);
-    if (end == null)
-      throw "unreachable"; // TODO(38725): Remove when promotion works.
     return data.buffer.asByteData(
         data.offsetInBytes + start * elementSize, (end - start) * elementSize);
   }
@@ -771,7 +769,6 @@
     int elementSize = data.elementSizeInBytes;
     end = RangeError.checkValidRange(
         start, end, data.lengthInBytes ~/ elementSize);
-    if (end == null) throw "unreachable"; // TODO(38725)
     return data.buffer.asInt8List(
         data.offsetInBytes + start * elementSize, (end - start) * elementSize);
   }
@@ -882,7 +879,6 @@
     int elementSize = data.elementSizeInBytes;
     end = RangeError.checkValidRange(
         start, end, data.lengthInBytes ~/ elementSize);
-    if (end == null) throw "unreachable"; // TODO(38725)
     return data.buffer.asUint8List(
         data.offsetInBytes + start * elementSize, (end - start) * elementSize);
   }
@@ -1002,7 +998,6 @@
     int elementSize = data.elementSizeInBytes;
     end = RangeError.checkValidRange(
         start, end, data.lengthInBytes ~/ elementSize);
-    if (end == null) throw "unreachable"; // TODO(38725)
     return data.buffer.asUint8ClampedList(
         data.offsetInBytes + start * elementSize, (end - start) * elementSize);
   }
@@ -1120,7 +1115,6 @@
     int elementSize = data.elementSizeInBytes;
     end = RangeError.checkValidRange(
         start, end, data.lengthInBytes ~/ elementSize);
-    if (end == null) throw "unreachable"; // TODO(38725)
     int byteLength = (end - start) * elementSize;
     if (byteLength % bytesPerElement != 0) {
       throw ArgumentError("The number of bytes to view must be a multiple of " +
@@ -1244,7 +1238,6 @@
     int elementSize = data.elementSizeInBytes;
     end = RangeError.checkValidRange(
         start, end, data.lengthInBytes ~/ elementSize);
-    if (end == null) throw "unreachable"; // TODO(38725)
     int byteLength = (end - start) * elementSize;
     if (byteLength % bytesPerElement != 0) {
       throw ArgumentError("The number of bytes to view must be a multiple of " +
@@ -1367,7 +1360,6 @@
     int elementSize = data.elementSizeInBytes;
     end = RangeError.checkValidRange(
         start, end, data.lengthInBytes ~/ elementSize);
-    if (end == null) throw "unreachable"; // TODO(38725)
     int byteLength = (end - start) * elementSize;
     if (byteLength % bytesPerElement != 0) {
       throw ArgumentError("The number of bytes to view must be a multiple of " +
@@ -1491,7 +1483,6 @@
     int elementSize = data.elementSizeInBytes;
     end = RangeError.checkValidRange(
         start, end, data.lengthInBytes ~/ elementSize);
-    if (end == null) throw "unreachable"; // TODO(38725)
     int byteLength = (end - start) * elementSize;
     if (byteLength % bytesPerElement != 0) {
       throw ArgumentError("The number of bytes to view must be a multiple of " +
@@ -1614,7 +1605,6 @@
     int elementSize = data.elementSizeInBytes;
     end = RangeError.checkValidRange(
         start, end, data.lengthInBytes ~/ elementSize);
-    if (end == null) throw "unreachable"; // TODO(38725)
     int byteLength = (end - start) * elementSize;
     if (byteLength % bytesPerElement != 0) {
       throw ArgumentError("The number of bytes to view must be a multiple of " +
@@ -1738,7 +1728,6 @@
     int elementSize = data.elementSizeInBytes;
     end = RangeError.checkValidRange(
         start, end, data.lengthInBytes ~/ elementSize);
-    if (end == null) throw "unreachable"; // TODO(38725)
     int byteLength = (end - start) * elementSize;
     if (byteLength % bytesPerElement != 0) {
       throw ArgumentError("The number of bytes to view must be a multiple of " +
@@ -1862,7 +1851,6 @@
     int elementSize = data.elementSizeInBytes;
     end = RangeError.checkValidRange(
         start, end, data.lengthInBytes ~/ elementSize);
-    if (end == null) throw "unreachable"; // TODO(38725)
     int byteLength = (end - start) * elementSize;
     if (byteLength % bytesPerElement != 0) {
       throw ArgumentError("The number of bytes to view must be a multiple of " +
@@ -1979,7 +1967,6 @@
     int elementSize = data.elementSizeInBytes;
     end = RangeError.checkValidRange(
         start, end, data.lengthInBytes ~/ elementSize);
-    if (end == null) throw "unreachable"; // TODO(38725)
     int byteLength = (end - start) * elementSize;
     if (byteLength % bytesPerElement != 0) {
       throw ArgumentError("The number of bytes to view must be a multiple of " +
@@ -2095,7 +2082,6 @@
     int elementSize = data.elementSizeInBytes;
     end = RangeError.checkValidRange(
         start, end, data.lengthInBytes ~/ elementSize);
-    if (end == null) throw "unreachable"; // TODO(38725)
     int byteLength = (end - start) * elementSize;
     if (byteLength % bytesPerElement != 0) {
       throw ArgumentError("The number of bytes to view must be a multiple of " +
@@ -2217,7 +2203,6 @@
     int elementSize = data.elementSizeInBytes;
     end = RangeError.checkValidRange(
         start, end, data.lengthInBytes ~/ elementSize);
-    if (end == null) throw "unreachable"; // TODO(38725)
     int byteLength = (end - start) * elementSize;
     if (byteLength % bytesPerElement != 0) {
       throw ArgumentError("The number of bytes to view must be a multiple of " +
@@ -2345,7 +2330,6 @@
     int elementSize = data.elementSizeInBytes;
     end = RangeError.checkValidRange(
         start, end, data.lengthInBytes ~/ elementSize);
-    if (end == null) throw "unreachable"; // TODO(38725)
     int byteLength = (end - start) * elementSize;
     if (byteLength % bytesPerElement != 0) {
       throw ArgumentError("The number of bytes to view must be a multiple of " +
diff --git a/tools/VERSION b/tools/VERSION
index 317622d..51c8ca9 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 14
 PATCH 0
-PRERELEASE 121
+PRERELEASE 122
 PRERELEASE_PATCH 0
\ No newline at end of file