Parts. Report UNNECESSARY_IMPORT using the new tracking.

Change-Id: Ide8f003bdfb1c80f1e342d3742b538edfda6ad8b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/382901
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/end_to_end_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/end_to_end_test.dart
index c75f6cd..02ecc4e 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/end_to_end_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/end_to_end_test.dart
@@ -161,7 +161,7 @@
 import '$importUri';
 
 void f(New o) {}
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 
   Future<void> test_replacedBy() async {
@@ -193,6 +193,6 @@
 int f() {
   return FileMode.read;
 }
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 }
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/flutter_use_case_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/flutter_use_case_test.dart
index 5b32f16..3755483 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/flutter_use_case_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/flutter_use_case_test.dart
@@ -95,7 +95,7 @@
 void f() {
   CupertinoAlertDialog(content: 'x');
 }
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 
   Future<void>
@@ -171,7 +171,7 @@
 void f() {
   CupertinoPopupSurface(child: 'x');
 }
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 
   Future<void>
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_test.dart
index 0b9e999..d7cde53 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/rename_test.dart
@@ -125,7 +125,7 @@
 void f() {
   New.c();
 }
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 
   Future<void> test_constructor_unnamed_deprecated() async {
@@ -175,7 +175,7 @@
 void f() {
   New();
 }
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 
   Future<void> test_constructor_unnamed_removed_prefixed() async {
@@ -234,7 +234,7 @@
 import '$importUri';
 
 class C extends New {}
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 
   Future<void> test_inImplements_deprecated() async {
@@ -270,7 +270,7 @@
 import '$importUri';
 
 class C implements New {}
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 
   Future<void> test_inOn_deprecated() async {
@@ -306,7 +306,7 @@
 import '$importUri';
 
 extension E on New {}
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 
   Future<void> test_inTypeAnnotation_deprecated() async {
@@ -342,7 +342,7 @@
 import '$importUri';
 
 void f(New o) {}
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 
   Future<void> test_inTypeArgument_deprecated() async {
@@ -430,7 +430,7 @@
 import '$importUri';
 
 class C with New {}
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 
   Future<void> test_staticField_deprecated() async {
@@ -472,7 +472,7 @@
 import '$importUri';
 
 var s = New.empty;
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 }
 
@@ -693,7 +693,7 @@
 import '$importUri';
 
 var l = New('a').double;
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 
   Future<void> test_staticField_deprecated() async {
@@ -735,7 +735,7 @@
 import '$importUri';
 
 var s = New.empty;
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 
   Future<void> test_staticField_removed_prefixed() async {
@@ -1176,7 +1176,7 @@
 void f() {
   b;
 }
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 
   Future<void> test_topLevel_reference_removed_prefixed() async {
@@ -1417,7 +1417,7 @@
 import '$importUri';
 
 class C with New {}
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 
   Future<void> test_inWith_removed_prefixed() async {
@@ -1515,7 +1515,7 @@
 void f() {
   b();
 }
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 
   Future<void> test_removed_prefixed() async {
@@ -1661,7 +1661,7 @@
 import '$importUri';
 
 void f(New o) {}
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 
   Future<void> test_removed_prefixed() async {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/replaced_by_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/replaced_by_test.dart
index 1c3f3be..9ff7300 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/replaced_by_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/replaced_by_test.dart
@@ -1068,7 +1068,7 @@
 f() {
   expect(true, true);
 }
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 
   Future<void> test_new_element_uris_single() async {
@@ -1102,7 +1102,7 @@
 main() {
   expect(true, true);
 }
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 }
 
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/sdk_fix_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/sdk_fix_test.dart
index fb75558..76c6fdb 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/sdk_fix_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/sdk_fix_test.dart
@@ -49,7 +49,7 @@
 import '$importUri';
 
 void f(Bar o) {}
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 }
 
diff --git a/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_use_case_test.dart b/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_use_case_test.dart
index 506bab3..11e3f25 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_use_case_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/data_driven/test_use_case_test.dart
@@ -96,6 +96,6 @@
 main() {
   expect(true, true);
 }
-''', errorFilter: ignoreUnusedImport);
+''');
   }
 }
diff --git a/pkg/analyzer/lib/src/dart/analysis/file_analysis.dart b/pkg/analyzer/lib/src/dart/analysis/file_analysis.dart
index ec94826..3f82f5f 100644
--- a/pkg/analyzer/lib/src/dart/analysis/file_analysis.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/file_analysis.dart
@@ -6,6 +6,7 @@
 import 'package:analyzer/src/dart/analysis/file_state.dart';
 import 'package:analyzer/src/dart/ast/ast.dart';
 import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer/src/dart/element/scope.dart';
 import 'package:analyzer/src/ignore_comments/ignore_info.dart';
 
 /// Information about a file being analyzed.
@@ -16,6 +17,7 @@
   final CompilationUnitImpl unit;
   final CompilationUnitElementImpl element;
   final IgnoreInfo ignoreInfo;
+  late ImportsTracking importsTracking;
 
   FileAnalysis({
     required this.file,
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index 7ffa613..83ba5e6 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -182,11 +182,18 @@
       _testingData?.recordFlowAnalysisDataForTesting(
           file.uri, flowAnalysisHelper.dataForTesting!);
 
-      var resolverVisitor = ResolverVisitor(_inheritance, _libraryElement,
-          libraryResolutionContext, file.source, _typeProvider, errorListener,
-          featureSet: _libraryElement.featureSet,
-          analysisOptions: _library.file.analysisOptions,
-          flowAnalysisHelper: flowAnalysisHelper);
+      var resolverVisitor = ResolverVisitor(
+        _inheritance,
+        _libraryElement,
+        libraryResolutionContext,
+        file.source,
+        _typeProvider,
+        errorListener,
+        featureSet: _libraryElement.featureSet,
+        analysisOptions: _library.file.analysisOptions,
+        flowAnalysisHelper: flowAnalysisHelper,
+        libraryFragment: unitElement,
+      );
       _testingData?.recordTypeConstraintGenerationDataForTesting(
           file.uri, resolverVisitor.inferenceHelper.dataForTesting!);
 
@@ -497,8 +504,10 @@
     LanguageVersionOverrideVerifier(errorReporter).verify(unit);
 
     // Verify imports.
-    {
-      ImportsVerifier verifier = ImportsVerifier();
+    if (!_hasDiagnosticReportedThatPreventsImportWarnings()) {
+      var verifier = ImportsVerifier(
+        fileAnalysis: fileAnalysis,
+      );
       verifier.addImports(unit);
       usedImportedElements.forEach(verifier.removeUsedElements);
       verifier.generateDuplicateExportWarnings(errorReporter);
@@ -586,6 +595,35 @@
     ];
   }
 
+  bool _hasDiagnosticReportedThatPreventsImportWarnings() {
+    var errorCodes = _libraryFiles.values.map((analysis) {
+      return analysis.errorListener.errors.map((e) => e.errorCode);
+    }).flattenedToSet;
+
+    for (var errorCode in errorCodes) {
+      if (const {
+        CompileTimeErrorCode.AMBIGUOUS_IMPORT,
+        CompileTimeErrorCode.CONST_WITH_NON_TYPE,
+        CompileTimeErrorCode.EXTENDS_NON_CLASS,
+        CompileTimeErrorCode.IMPLEMENTS_NON_CLASS,
+        CompileTimeErrorCode.MIXIN_OF_NON_CLASS,
+        CompileTimeErrorCode.NEW_WITH_NON_TYPE,
+        CompileTimeErrorCode.NOT_A_TYPE,
+        CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT,
+        CompileTimeErrorCode.UNDEFINED_ANNOTATION,
+        CompileTimeErrorCode.UNDEFINED_CLASS,
+        CompileTimeErrorCode.UNDEFINED_FUNCTION,
+        CompileTimeErrorCode.UNDEFINED_IDENTIFIER,
+        CompileTimeErrorCode.UNDEFINED_PREFIXED_NAME,
+        WarningCode.DEPRECATED_EXPORT_USE,
+      }.contains(errorCode)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
   /// Return a new parsed unresolved [CompilationUnit].
   FileAnalysis _parse({
     required FileState file,
@@ -618,10 +656,24 @@
       fileElement: _libraryElement.definingCompilationUnit,
     );
 
+    // Configure scopes for all files to track imports usages.
+    // Associate tracking objects with file objects.
+    for (var fileAnalysis in _libraryFiles.values) {
+      var scope = fileAnalysis.element.scope;
+      var tracking = scope.importsTrackingInit();
+      fileAnalysis.importsTracking = tracking;
+    }
+
     for (var fileAnalysis in _libraryFiles.values) {
       _resolveFile(fileAnalysis);
     }
 
+    // Stop tracking usages by scopes.
+    for (var fileAnalysis in _libraryFiles.values) {
+      var scope = fileAnalysis.element.scope;
+      scope.importsTrackingDestroy();
+    }
+
     _computeConstants();
   }
 
@@ -911,11 +963,18 @@
     _testingData?.recordFlowAnalysisDataForTesting(
         fileAnalysis.file.uri, flowAnalysisHelper.dataForTesting!);
 
-    var resolver = ResolverVisitor(_inheritance, _libraryElement,
-        libraryResolutionContext, source, _typeProvider, errorListener,
-        analysisOptions: _library.file.analysisOptions,
-        featureSet: unit.featureSet,
-        flowAnalysisHelper: flowAnalysisHelper);
+    var resolver = ResolverVisitor(
+      _inheritance,
+      _libraryElement,
+      libraryResolutionContext,
+      source,
+      _typeProvider,
+      errorListener,
+      analysisOptions: _library.file.analysisOptions,
+      featureSet: unit.featureSet,
+      flowAnalysisHelper: flowAnalysisHelper,
+      libraryFragment: unitElement,
+    );
     unit.accept(resolver);
     _testingData?.recordTypeConstraintGenerationDataForTesting(
         fileAnalysis.file.uri, resolver.inferenceHelper.dataForTesting!);
diff --git a/pkg/analyzer/lib/src/dart/element/scope.dart b/pkg/analyzer/lib/src/dart/element/scope.dart
index c9d4229..c668cc1 100644
--- a/pkg/analyzer/lib/src/dart/element/scope.dart
+++ b/pkg/analyzer/lib/src/dart/element/scope.dart
@@ -109,6 +109,141 @@
   }
 }
 
+/// Tracking information for all import in [CompilationUnitElementImpl].
+class ImportsTracking {
+  /// Tracking information for each import prefix.
+  final Map<PrefixElementImpl?, ImportsTrackingOfPrefix> map;
+
+  ImportsTracking({
+    required this.map,
+  });
+
+  /// The elements that are used from [import].
+  Set<Element> elementsOf(LibraryImportElementImpl import) {
+    return trackerOf(import).importToUsedElements[import] ?? {};
+  }
+
+  void notifyExtensionUsed(ExtensionElement element) {
+    for (var tracking in map.values) {
+      tracking.notifyExtensionUsed(element);
+    }
+  }
+
+  ImportsTrackingOfPrefix trackerOf(LibraryImportElementImpl import) {
+    var prefix = import.prefix?.element;
+    return map[prefix]!;
+  }
+}
+
+class ImportsTrackingOfPrefix {
+  final PrefixScope scope;
+  final Map<Element, List<LibraryImportElementImpl>> elementImports;
+
+  /// Key: an import.
+  /// Value: used elements imported from the import.
+  final Map<LibraryImportElementImpl, Set<Element>> importToUsedElements = {};
+
+  /// Key: an import.
+  /// Value: used elements imported from the import.
+  /// Excludes elements from deprecated exports.
+  final Map<LibraryImportElementImpl, Set<Element>> importToAccessedElements2 =
+      {};
+
+  /// Usually it is an error to use an import prefix without `.identifier`
+  /// after it, but we allow this in comment references. This makes the
+  /// corresponding group of imports "used".
+  bool hasPrefixUsedInCommentReference = false;
+
+  /// We set it temporarily to `false` while resolving combinators.
+  bool active = true;
+
+  ImportsTrackingOfPrefix({
+    required this.scope,
+    required this.elementImports,
+  });
+
+  /// The elements that are used from [import].
+  Set<Element> elementsOf(LibraryImportElementImpl import) {
+    return importToUsedElements[import] ?? {};
+  }
+
+  /// The subset of [elementsOf], excludes elements that are from deprecated
+  /// exports inside the imported library.
+  Set<Element> elementsOf2(LibraryImportElementImpl import) {
+    var result = importToAccessedElements2[import];
+    if (result != null) {
+      return result;
+    }
+
+    var accessedElements = elementsOf(import);
+
+    // SAFETY: the scope adds only imports with libraries.
+    var importedLibrary = import.importedLibrary!;
+    var elementFactory = importedLibrary.session.elementFactory;
+
+    for (var exportedReference in importedLibrary.exportedReferences) {
+      var reference = exportedReference.reference;
+      var element = elementFactory.elementOfReference(reference)!;
+
+      // Check only accessed elements.
+      if (!accessedElements.contains(element)) {
+        continue;
+      }
+
+      // We want to exclude only deprecated exports.
+      if (!importedLibrary.isFromDeprecatedExport(exportedReference)) {
+        continue;
+      }
+
+      // OK, we have to clone the set, and remove the element.
+      result ??= accessedElements.toSet();
+      result.remove(element);
+    }
+
+    result ??= accessedElements;
+    return importToAccessedElements2[import] = result;
+  }
+
+  void lookupResult(Element? element) {
+    if (!active) {
+      return;
+    }
+
+    if (element == null) {
+      return;
+    }
+
+    if (element is MultiplyDefinedElement) {
+      return;
+    }
+
+    // SAFETY: if we have `element`, it is from a local import.
+    var imports = elementImports[element]!;
+    for (var import in imports) {
+      (importToUsedElements[import] ??= {}).add(element);
+    }
+  }
+
+  void notifyExtensionUsed(ExtensionElement element) {
+    var imports = elementImports[element];
+    if (imports != null) {
+      for (var import in imports) {
+        (importToUsedElements[import] ??= {}).add(element);
+      }
+    } else {
+      // We include into `accessibleExtensions` elements from parents.
+      // So, it is possible that the element is not from this scope.
+      // In this case we notify the parent tracker.
+      var parentTracking = scope.parent?._importsTracking;
+      parentTracking?.notifyExtensionUsed(element);
+    }
+  }
+
+  void notifyPrefixUsedInCommentReference() {
+    hasPrefixUsedInCommentReference = true;
+  }
+}
+
 /// The scope defined by an instance element.
 class InstanceScope extends EnclosedScope {
   InstanceScope(super.parent, InstanceElement element) {
@@ -177,13 +312,22 @@
   /// The cached result for [accessibleExtensions].
   List<ExtensionElement>? _extensions;
 
+  /// This field is set temporarily while resolving all files of a library.
+  /// So, we can track which elements were actually returned, and which imports
+  /// in which file (including enclosing files) provided these elements.
+  ///
+  /// When we are done, we remove the tracker, so that it does not use memory
+  /// when we are not resolving files of this library.
+  ImportsTracking? _importsTracking;
+
   factory LibraryFragmentScope(CompilationUnitElementImpl fragment) {
+    var parent = fragment.enclosingElement3?.scope;
     return LibraryFragmentScope._(
-      parent: fragment.enclosingElement3?.scope,
+      parent: parent,
       fragment: fragment,
       noPrefixScope: PrefixScope(
         libraryElement: fragment.library,
-        parent: null,
+        parent: parent?.noPrefixScope,
         libraryImports: fragment.libraryImports,
         prefix: null,
       ),
@@ -217,6 +361,34 @@
     }.toFixedList();
   }
 
+  // TODO(scheglov): this is kludge.
+  // We should not use the fragment scope for resolving combinators.
+  // We should use the export scope of the imported library.
+  void importsTrackingActive(bool value) {
+    if (_importsTracking case var importsTracking?) {
+      for (var tracking in importsTracking.map.values) {
+        tracking.active = value;
+      }
+    }
+  }
+
+  void importsTrackingDestroy() {
+    noPrefixScope.importsTrackingDestroy();
+    for (var prefixElement in _prefixElements.values) {
+      prefixElement.scope.importsTrackingDestroy();
+    }
+  }
+
+  ImportsTracking importsTrackingInit() {
+    return _importsTracking = ImportsTracking(
+      map: {
+        null: noPrefixScope.importsTrackingInit(),
+        for (var prefixElement in _prefixElements.values)
+          prefixElement: prefixElement.scope.importsTrackingInit(),
+      },
+    );
+  }
+
   @override
   ScopeLookupResult lookup(String id) {
     // Try declarations of the whole library.
@@ -230,10 +402,14 @@
       return importResult;
     }
 
-    // No parent, no result.
+    // No result.
     return ScopeLookupResultImpl(null, null);
   }
 
+  void notifyExtensionUsed(ExtensionElement element) {
+    _importsTracking?.notifyExtensionUsed(element);
+  }
+
   PrefixScope? _getParentPrefixScope(PrefixElementImpl prefix) {
     var isDeferred = prefix.imports.any((import) {
       return import.prefix is DeferredImportElementPrefix;
@@ -266,12 +442,7 @@
     }
 
     // Try the parent's combined import scope.
-    var parentResult = parent?._lookupCombined(id);
-    if (parentResult != null) {
-      return parentResult;
-    }
-
-    return null;
+    return parent?._lookupCombined(id);
   }
 
   ScopeLookupResult? _lookupLibrary(String id) {
@@ -310,6 +481,9 @@
   final LibraryElementImpl libraryElement;
   final PrefixScope? parent;
 
+  final List<LibraryImportElementImpl> _importElements = [];
+  final Map<Element, List<LibraryImportElementImpl>> _elementImports = {};
+
   final Map<String, Element> _getters = {};
   final Map<String, Element> _setters = {};
   Set<String>? _settersFromDeprecatedExport;
@@ -317,40 +491,53 @@
   final Set<ExtensionElement> _extensions = {};
   LibraryElement? _deferredLibrary;
 
+  ImportsTrackingOfPrefix? _importsTracking;
+
   PrefixScope({
     required this.libraryElement,
     required this.parent,
-    required List<LibraryImportElement> libraryImports,
+    required List<LibraryImportElementImpl> libraryImports,
     required PrefixElement? prefix,
   }) {
     var elementFactory = libraryElement.session.elementFactory;
     for (var import in libraryImports) {
       var importedUri = import.uri;
-      if (importedUri is DirectiveUriWithLibrary &&
+      if (importedUri is DirectiveUriWithLibraryImpl &&
           import.prefix?.element == prefix) {
+        _importElements.add(import);
         var importedLibrary = importedUri.library;
-        if (importedLibrary is LibraryElementImpl) {
-          var combinators = import.combinators.build();
-          for (var exportedReference in importedLibrary.exportedReferences) {
-            var reference = exportedReference.reference;
-            if (combinators.allows(reference.name)) {
-              var element = elementFactory.elementOfReference(reference)!;
-              if (_shouldAdd(importedLibrary, element)) {
-                _add(
-                  element,
-                  importedLibrary.isFromDeprecatedExport(exportedReference),
-                );
-              }
+        var combinators = import.combinators.build();
+        for (var exportedReference in importedLibrary.exportedReferences) {
+          var reference = exportedReference.reference;
+          if (combinators.allows(reference.name)) {
+            var element = elementFactory.elementOfReference(reference)!;
+            if (_shouldAdd(importedLibrary, element)) {
+              (_elementImports[element] ??= []).add(import);
+              _add(
+                element,
+                importedLibrary.isFromDeprecatedExport(exportedReference),
+              );
             }
           }
-          if (import.prefix is DeferredImportElementPrefix) {
-            _deferredLibrary ??= importedLibrary;
-          }
+        }
+        if (import.prefix is DeferredImportElementPrefix) {
+          _deferredLibrary ??= importedLibrary;
         }
       }
     }
   }
 
+  void importsTrackingDestroy() {
+    _importsTracking = null;
+  }
+
+  ImportsTrackingOfPrefix importsTrackingInit() {
+    return _importsTracking = ImportsTrackingOfPrefix(
+      scope: this,
+      elementImports: _elementImports,
+    );
+  }
+
   @override
   ScopeLookupResult lookup(String id) {
     var deferredLibrary = _deferredLibrary;
@@ -361,6 +548,8 @@
     var getter = _getters[id];
     var setter = _setters[id];
     if (getter != null || setter != null) {
+      _importsTracking?.lookupResult(getter);
+      _importsTracking?.lookupResult(setter);
       return PrefixScopeLookupResult(
         getter,
         setter,
@@ -376,6 +565,11 @@
     return ScopeLookupResultImpl(null, null);
   }
 
+  /// Usually this is an error, but we allow it in comment references.
+  void notifyPrefixUsedInCommentReference() {
+    _importsTracking?.notifyPrefixUsedInCommentReference();
+  }
+
   void _add(Element element, bool isFromDeprecatedExport) {
     if (element is PropertyAccessorElement && element.isSetter) {
       _addTo(
diff --git a/pkg/analyzer/lib/src/dart/resolver/comment_reference_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/comment_reference_resolver.dart
index 5092d92..cdb1c50 100644
--- a/pkg/analyzer/lib/src/dart/resolver/comment_reference_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/comment_reference_resolver.dart
@@ -5,6 +5,7 @@
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/dart/element/type.dart';
 import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
 import 'package:analyzer/src/dart/element/type_provider.dart';
 import 'package:analyzer/src/dart/element/type_system.dart';
@@ -143,6 +144,12 @@
     var lookupResult = identifier.scopeLookupResult!;
     var element = lookupResult.getter ?? lookupResult.setter;
 
+    // Usually referencing just an import prefix is an error.
+    // But we allow this in documentation comments.
+    if (element is PrefixElementImpl) {
+      element.scope.notifyPrefixUsedInCommentReference();
+    }
+
     if (element == null) {
       InterfaceType enclosingType;
       var enclosingClass = _resolver.enclosingClass;
diff --git a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
index bcf7695..124c1be 100644
--- a/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/extension_member_resolver.dart
@@ -96,12 +96,20 @@
     }
 
     if (extensions.length == 1) {
-      return extensions[0].asResolutionResult;
+      var instantiated = extensions[0];
+      _resolver.libraryFragment.scope.notifyExtensionUsed(
+        instantiated.extension,
+      );
+      return instantiated.asResolutionResult;
     }
 
     var mostSpecific = _chooseMostSpecific(extensions);
     if (mostSpecific.length == 1) {
-      return mostSpecific.first.asResolutionResult;
+      var instantiated = mostSpecific.first;
+      _resolver.libraryFragment.scope.notifyExtensionUsed(
+        instantiated.extension,
+      );
+      return instantiated.asResolutionResult;
     }
 
     // The most specific extension is ambiguous.
diff --git a/pkg/analyzer/lib/src/error/imports_verifier.dart b/pkg/analyzer/lib/src/error/imports_verifier.dart
index 80d63b7..b1ce0d4f 100644
--- a/pkg/analyzer/lib/src/error/imports_verifier.dart
+++ b/pkg/analyzer/lib/src/error/imports_verifier.dart
@@ -5,10 +5,11 @@
 import 'package:analyzer/dart/ast/visitor.dart';
 import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/src/dart/analysis/file_analysis.dart';
 import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/resolver/scope.dart';
 import 'package:analyzer/src/error/codes.dart';
-import 'package:analyzer/src/summary2/combinator.dart';
 
 /// A visitor that visits ASTs and fills [UsedImportedElements].
 class GatherUsedImportedElementsVisitor extends RecursiveAstVisitor<void> {
@@ -241,6 +242,8 @@
 /// this logic built up in this class could be used for such an action in the
 /// future.
 class ImportsVerifier {
+  final FileAnalysis fileAnalysis;
+
   /// All [ImportDirective]s of the current library.
   final List<ImportDirectiveImpl> _allImports = [];
 
@@ -296,6 +299,10 @@
   final Map<NamespaceDirective, List<SimpleIdentifier>>
       _duplicateShownNamesMap = {};
 
+  ImportsVerifier({
+    required this.fileAnalysis,
+  });
+
   void addImports(CompilationUnit node) {
     var importsWithLibraries = <_NamespaceDirective>[];
     var exportsWithLibraries = <_NamespaceDirective>[];
@@ -418,50 +425,103 @@
     });
   }
 
-  /// Report an [HintCode.UNNECESSARY_IMPORT] hint for each unnecessary import.
+  /// Report import directives that are unnecessary.
   ///
-  /// Only call this method after unused imports have been determined by
-  /// [removeUsedElements].
+  /// 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 first import directive, and a "used elements" set which is a
+  /// proper superset of the first import directive's "used elements" set.
   void generateUnnecessaryImportHints(ErrorReporter errorReporter,
       List<UsedImportedElements> usedImportedElementsList) {
+    var importsTracking = fileAnalysis.importsTracking;
     var usedImports = {..._allImports}..removeAll(_unusedImports);
 
-    var verifier = _UnnecessaryImportsVerifier(usedImports);
-    verifier.processUsedElements(usedImportedElementsList);
-    verifier.reportImports(errorReporter);
-  }
+    for (var firstDirective in usedImports) {
+      var firstElement = firstDirective.element!;
+      var tracker = importsTracking.trackerOf(firstElement);
 
-  /// Report an [HintCode.UNUSED_IMPORT] hint for each unused import.
-  ///
-  /// Only call this method after all of the compilation units have been visited
-  /// by this visitor.
-  ///
-  /// @param errorReporter the error reporter used to report the set of
-  ///        [HintCode.UNUSED_IMPORT] hints
-  void generateUnusedImportHints(ErrorReporter errorReporter) {
-    int length = _unusedImports.length;
-    for (int i = 0; i < length; i++) {
-      ImportDirective unusedImport = _unusedImports[i];
-      // Check that the imported URI exists and isn't dart:core
-      var importElement = unusedImport.element;
-      if (importElement != null) {
-        var libraryElement = importElement.importedLibrary;
-        if (libraryElement == null ||
-            libraryElement.isDartCore ||
-            libraryElement.isSynthetic) {
+      for (var secondDirective in usedImports) {
+        if (secondDirective == firstDirective) {
           continue;
         }
+
+        var secondElement = secondDirective.element!;
+
+        // Must be the same import prefix, so the same tracker.
+        var secondTracker = importsTracking.trackerOf(secondElement);
+        if (secondTracker != tracker) {
+          continue;
+        }
+
+        var firstSet = tracker.elementsOf2(firstElement);
+        var secondSet = tracker.elementsOf2(secondElement);
+
+        // The second must provide all elements of the first.
+        if (!secondSet.containsAll(firstSet)) {
+          continue;
+        }
+
+        // The second must provide strictly more than the first.
+        if (!(secondSet.length > firstSet.length)) {
+          continue;
+        }
+
+        var firstElementUri = firstElement.uri;
+        var secondElementUri = secondElement.uri;
+        if (firstElementUri is DirectiveUriWithLibraryImpl &&
+            secondElementUri is DirectiveUriWithLibraryImpl) {
+          errorReporter.atNode(
+            firstDirective.uri,
+            HintCode.UNNECESSARY_IMPORT,
+            arguments: [
+              firstElementUri.relativeUriString,
+              secondElementUri.relativeUriString,
+            ],
+          );
+          // Now that we reported on the first, so we are done.
+          break;
+        }
       }
-      StringLiteral uri = unusedImport.uri;
-      // We can safely assume that `uri.stringValue` is non-`null`, because the
-      // only way for it to be `null` is if the import contains a string
-      // interpolation, in which case the import wouldn't have resolved and
-      // would not have been included in [_unusedImports].
-      errorReporter.atNode(
-        uri,
-        WarningCode.UNUSED_IMPORT,
-        arguments: [uri.stringValue!],
-      );
+    }
+  }
+
+  /// Report [WarningCode.UNUSED_IMPORT] for each unused import.
+  void generateUnusedImportHints(ErrorReporter errorReporter) {
+    var importsTracking = fileAnalysis.importsTracking;
+    for (var importDirective in fileAnalysis.unit.directives) {
+      if (importDirective is ImportDirectiveImpl) {
+        var importElement = importDirective.element!;
+        var prefixElement = importElement.prefix?.element;
+        var tracking = importsTracking.map[prefixElement]!;
+
+        // Ignore the group of imports with a prefix in a comment reference.
+        if (tracking.hasPrefixUsedInCommentReference) {
+          continue;
+        }
+
+        if (importElement.uri case DirectiveUriWithLibraryImpl uri) {
+          // Ignore explicit dart:core import.
+          if (uri.library.isDartCore) {
+            continue;
+          }
+
+          // The URI target does not exist, reported this elsewhere.
+          if (uri.library.isSynthetic) {
+            continue;
+          }
+
+          var isUsed = tracking.importToUsedElements.containsKey(importElement);
+          if (!isUsed) {
+            errorReporter.atNode(
+              importDirective.uri,
+              WarningCode.UNUSED_IMPORT,
+              arguments: [uri.relativeUriString],
+            );
+          }
+        }
+      }
     }
   }
 
@@ -729,97 +789,6 @@
   String get libraryUriStr => '${library.source.uri}';
 }
 
-/// 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 set of imports which provide at least one element used in the library.
-  final Set<ImportDirectiveImpl> _usedImports;
-
-  /// The mapping of each import to its "used elements" set.
-  ///
-  /// This is computed in [processUsedElements].
-  final Map<ImportDirective, Set<Element>> _usedElementSets = {};
-
-  _UnnecessaryImportsVerifier(this._usedImports);
-
-  /// Determines the "used elements" set for each import directive in
-  /// [_usedImports].
-  void processUsedElements(
-    List<UsedImportedElements> usedImportedElementsList,
-  ) {
-    assert(_usedElementSets.isEmpty);
-
-    var allUsedElements = <Element>{};
-    for (var usedElements in usedImportedElementsList) {
-      allUsedElements.addAll(usedElements.elements);
-      allUsedElements.addAll(usedElements.usedExtensions);
-      for (var elements in usedElements.prefixMap.values) {
-        allUsedElements.addAll(elements);
-      }
-    }
-
-    for (var importDirective in _usedImports) {
-      var importElement = importDirective.element;
-      if (importElement == null) continue;
-
-      var importedLibrary = importElement.importedLibrary;
-      if (importedLibrary == null) continue;
-
-      var combinators = importElement.combinators.build();
-      for (var exportedReference in importedLibrary.exportedReferences) {
-        var reference = exportedReference.reference;
-        var element = reference.element;
-        if (combinators.allows(reference.name) && element != null) {
-          if (allUsedElements.contains(element)) {
-            if (!importedLibrary.isFromDeprecatedExport(exportedReference)) {
-              (_usedElementSets[importDirective] ??= {}).add(element);
-            }
-          }
-        }
-      }
-    }
-  }
-
-  /// 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;
-            // The only way an import URI's `stringValue` can be `null` is if
-            // the string contained interpolations, in which case the import
-            // would have failed to resolve, and we would never reach here.  So
-            // it is safe to assume that `uri.stringValue` and
-            // `otherImport.uri.stringValue` are both non-`null`.
-            errorReporter.atNode(
-              uri,
-              HintCode.UNNECESSARY_IMPORT,
-              arguments: [uri.stringValue!, otherImport.uri.stringValue!],
-            );
-            // Break out of the loop of "other imports" to prevent reporting
-            // UNNECESSARY_IMPORT on [importDirective] multiple times.
-            break;
-          }
-        }
-      }
-    }
-  }
-}
-
 extension on Map<ImportDirective, Namespace> {
   /// Lookup and return the [Namespace] in this Map.
   ///
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 581e9dd..b67dc19 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -86,6 +86,7 @@
 import 'package:analyzer/src/generated/variable_type_provider.dart';
 import 'package:analyzer/src/task/inference_error.dart';
 import 'package:analyzer/src/util/ast_data_extractor.dart';
+import 'package:analyzer/src/utilities/extensions/object.dart';
 
 /// Function determining which source files should have inference logging
 /// enabled.
@@ -134,6 +135,9 @@
   /// The element for the library containing the compilation unit being visited.
   final LibraryElementImpl definingLibrary;
 
+  /// The library fragment being visited.
+  final CompilationUnitElementImpl libraryFragment;
+
   /// The context shared between different units of the same library.
   final LibraryResolutionContext libraryResolutionContext;
 
@@ -304,39 +308,43 @@
   // TODO(paulberry): make [featureSet] a required parameter (this will be a
   // breaking change).
   ResolverVisitor(
-      InheritanceManager3 inheritanceManager,
-      LibraryElementImpl definingLibrary,
-      LibraryResolutionContext libraryResolutionContext,
-      Source source,
-      TypeProvider typeProvider,
-      AnalysisErrorListener errorListener,
-      {required FeatureSet featureSet,
-      required AnalysisOptionsImpl analysisOptions,
-      required FlowAnalysisHelper flowAnalysisHelper})
-      : this._(
-            inheritanceManager,
-            definingLibrary,
-            libraryResolutionContext,
-            source,
-            definingLibrary.typeSystem,
-            typeProvider as TypeProviderImpl,
-            errorListener,
-            featureSet,
-            analysisOptions,
-            flowAnalysisHelper);
+    InheritanceManager3 inheritanceManager,
+    LibraryElementImpl definingLibrary,
+    LibraryResolutionContext libraryResolutionContext,
+    Source source,
+    TypeProvider typeProvider,
+    AnalysisErrorListener errorListener, {
+    required CompilationUnitElementImpl libraryFragment,
+    required FeatureSet featureSet,
+    required AnalysisOptionsImpl analysisOptions,
+    required FlowAnalysisHelper flowAnalysisHelper,
+  }) : this._(
+          inheritanceManager,
+          definingLibrary,
+          libraryResolutionContext,
+          source,
+          definingLibrary.typeSystem,
+          typeProvider as TypeProviderImpl,
+          errorListener,
+          featureSet,
+          analysisOptions,
+          flowAnalysisHelper,
+          libraryFragment: libraryFragment,
+        );
 
   ResolverVisitor._(
-      this.inheritance,
-      this.definingLibrary,
-      this.libraryResolutionContext,
-      this.source,
-      this.typeSystem,
-      this.typeProvider,
-      AnalysisErrorListener errorListener,
-      FeatureSet featureSet,
-      this.analysisOptions,
-      this.flowAnalysis)
-      : errorReporter = ErrorReporter(errorListener, source),
+    this.inheritance,
+    this.definingLibrary,
+    this.libraryResolutionContext,
+    this.source,
+    this.typeSystem,
+    this.typeProvider,
+    AnalysisErrorListener errorListener,
+    FeatureSet featureSet,
+    this.analysisOptions,
+    this.flowAnalysis, {
+    required this.libraryFragment,
+  })  : errorReporter = ErrorReporter(errorListener, source),
         _featureSet = featureSet,
         genericMetadataIsEnabled =
             definingLibrary.featureSet.isEnabled(Feature.generic_metadata),
@@ -4984,6 +4992,17 @@
   }
 
   @override
+  void visitHideCombinator(HideCombinator node) {
+    var scope = nameScope.ifTypeOrNull<LibraryFragmentScope>();
+    scope?.importsTrackingActive(false);
+    try {
+      super.visitHideCombinator(node);
+    } finally {
+      scope?.importsTrackingActive(true);
+    }
+  }
+
+  @override
   void visitIfElement(covariant IfElementImpl node) {
     _visitIf(node);
   }
@@ -5010,6 +5029,9 @@
   }
 
   @override
+  void visitLibraryIdentifier(LibraryIdentifier node) {}
+
+  @override
   void visitMethodDeclaration(covariant MethodDeclarationImpl node) {
     node.body.localVariableInfo = _localVariableInfo;
     node.metadata.accept(this);
@@ -5102,6 +5124,17 @@
   }
 
   @override
+  void visitShowCombinator(ShowCombinator node) {
+    var scope = nameScope.ifTypeOrNull<LibraryFragmentScope>();
+    scope?.importsTrackingActive(false);
+    try {
+      super.visitShowCombinator(node);
+    } finally {
+      scope?.importsTrackingActive(true);
+    }
+  }
+
+  @override
   void visitSimpleIdentifier(covariant SimpleIdentifierImpl node) {
     // Ignore if already resolved - declaration or type.
     if (node.inDeclarationContext()) {
@@ -5109,6 +5142,12 @@
     }
     // Ignore if qualified.
     var parent = node.parent;
+    if (parent is ConstructorName && parent.name == node) {
+      return;
+    }
+    if (parent is Label && parent.parent is NamedExpression) {
+      return;
+    }
     var scopeLookupResult = nameScope.lookup(node.name);
     node.scopeLookupResult = scopeLookupResult;
     // Ignore if it cannot be a reference to a local variable.
@@ -5120,9 +5159,6 @@
         parent.fieldName == node) {
       return;
     }
-    if (parent is ConstructorName) {
-      return;
-    }
     if (parent is Label) {
       return;
     }
diff --git a/pkg/analyzer/lib/src/summary2/ast_resolver.dart b/pkg/analyzer/lib/src/summary2/ast_resolver.dart
index cc8dbbf..aab408f 100644
--- a/pkg/analyzer/lib/src/summary2/ast_resolver.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_resolver.dart
@@ -58,6 +58,7 @@
     featureSet: _featureSet,
     analysisOptions: analysisOptions,
     flowAnalysisHelper: _flowAnalysis,
+    libraryFragment: _unitElement,
   );
 
   AstResolver(
diff --git a/pkg/analyzer/test/src/dart/analysis/driver_test.dart b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
index c4089ba..a4af3fa 100644
--- a/pkg/analyzer/test/src/dart/analysis/driver_test.dart
+++ b/pkg/analyzer/test/src/dart/analysis/driver_test.dart
@@ -2237,7 +2237,6 @@
     flags: exists isLibrary
     errors
       25 +1 UNDEFINED_CLASS
-      7 +8 UNUSED_IMPORT
 [stream]
   ResolvedUnitResult #1
 [status] idle
diff --git a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
index 175ba7b..b2cc427 100644
--- a/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/method_invocation_test.dart
@@ -2112,7 +2112,6 @@
   math?.loadLibrary();
 }
 ''', [
-      error(WarningCode.UNUSED_IMPORT, 7, 11),
       error(CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT, 49, 4),
     ]);
 
diff --git a/pkg/analyzer/test/src/diagnostics/deprecated_export_use_test.dart b/pkg/analyzer/test/src/diagnostics/deprecated_export_use_test.dart
index e7e84cd..b45f790 100644
--- a/pkg/analyzer/test/src/diagnostics/deprecated_export_use_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/deprecated_export_use_test.dart
@@ -336,17 +336,19 @@
 ''');
 
     newFile('$testPackageLibPath/b.dart', r'''
-library b;
+library;
 
 @deprecated
 export 'a.dart';
+
+class B {}
 ''');
 
     await assertNoErrorsInCode('''
 import 'a.dart';
 import 'b.dart';
 
-void f(A a) {}
+void f(A a, B b) {}
 ''');
   }
 
diff --git a/pkg/analyzer/test/src/diagnostics/prefix_collides_with_top_level_member_test.dart b/pkg/analyzer/test/src/diagnostics/prefix_collides_with_top_level_member_test.dart
index 20b55fd..f8c9324 100644
--- a/pkg/analyzer/test/src/diagnostics/prefix_collides_with_top_level_member_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/prefix_collides_with_top_level_member_test.dart
@@ -25,7 +25,6 @@
 typedef p();
 p.A a = p.A();
 ''', [
-      error(WarningCode.UNUSED_IMPORT, 7, 10),
       error(CompileTimeErrorCode.PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER, 32, 1,
           contextMessages: [message(testFile, 21, 1)]),
       error(CompileTimeErrorCode.NOT_A_TYPE, 37, 3),
@@ -92,7 +91,6 @@
 class p {}
 p.A a = p.A();
 ''', [
-      error(WarningCode.UNUSED_IMPORT, 7, 10),
       error(CompileTimeErrorCode.PREFIX_COLLIDES_WITH_TOP_LEVEL_MEMBER, 30, 1,
           contextMessages: [message(testFile, 21, 1)]),
       error(CompileTimeErrorCode.NOT_A_TYPE, 35, 3),
diff --git a/pkg/analyzer/test/src/diagnostics/prefix_identifier_not_followed_by_dot_test.dart b/pkg/analyzer/test/src/diagnostics/prefix_identifier_not_followed_by_dot_test.dart
index 40bcb1b..68813dd 100644
--- a/pkg/analyzer/test/src/diagnostics/prefix_identifier_not_followed_by_dot_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/prefix_identifier_not_followed_by_dot_test.dart
@@ -132,7 +132,6 @@
   p?.loadLibrary();
 }
 ''', [
-      error(WarningCode.UNUSED_IMPORT, 7, 10),
       error(CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT, 41, 1),
     ]);
   }
diff --git a/pkg/analyzer/test/src/diagnostics/unnecessary_import_test.dart b/pkg/analyzer/test/src/diagnostics/unnecessary_import_test.dart
index 427aa8d..8d771aa 100644
--- a/pkg/analyzer/test/src/diagnostics/unnecessary_import_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unnecessary_import_test.dart
@@ -171,25 +171,6 @@
     ]);
   }
 
-  test_class_deprecatedExport() async {
-    newFile('$testPackageLibPath/a.dart', '''
-class A {}
-''');
-
-    newFile('$testPackageLibPath/b.dart', '''
-library;
-@deprecated
-export 'a.dart';
-class B {}
-''');
-
-    await assertNoErrorsInCode('''
-import 'a.dart';
-import 'b.dart';
-void f(A a, B b) {}
-''');
-  }
-
   test_duplicateImport_differentPrefix() async {
     newFile('$testPackageLibPath/lib1.dart', '''
 class A {}
@@ -271,6 +252,91 @@
     ]);
   }
 
+  test_hasDeprecatedExport_hasNotDeprecatedImport_hasOtherClass() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+class A {}
+''');
+
+    newFile('$testPackageLibPath/b.dart', r'''
+library;
+
+@deprecated
+export 'a.dart';
+
+class B {}
+''');
+
+    // `import b` is not reported because provides used `B`.
+    // `A` is from both `a.dart` and `b.dart`, so not reported.
+    await assertNoErrorsInCode('''
+import 'a.dart';
+import 'b.dart';
+
+void f(A _, B _) {}
+''');
+  }
+
+  test_hasDeprecatedExport_hasNotDeprecatedImport_noOtherClass() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+class A {}
+''');
+
+    newFile('$testPackageLibPath/b.dart', r'''
+class B {}
+''');
+
+    newFile('$testPackageLibPath/c.dart', r'''
+library;
+
+@deprecated
+export 'b.dart';
+
+class C {}
+''');
+
+    // `import c` is unnecessary because we use only `B` from it.
+    // But the export of `B` from `c.dart` is deprecated.
+    // We can get `B` from `import b`, in a not deprecated way.
+    // It also declares `C`, but we don't use it.
+    await assertErrorsInCode('''
+import 'a.dart';
+import 'b.dart';
+import 'c.dart';
+
+void f(A _, B _) {}
+''', [
+      error(HintCode.UNNECESSARY_IMPORT, 41, 8),
+    ]);
+  }
+
+  test_hasDeprecatedExport_noNotDeprecatedImport() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+class A {}
+''');
+
+    newFile('$testPackageLibPath/b.dart', r'''
+class B {}
+''');
+
+    newFile('$testPackageLibPath/c.dart', r'''
+library;
+
+@deprecated
+export 'b.dart';
+''');
+
+    // `import c` is not marked as unnecessary because of there is
+    // `DEPRECATED_EXPORT_USE` already reported.
+    await assertErrorsInCode('''
+import 'a.dart';
+import 'c.dart';
+
+void f(A _, B _) {}
+''', [
+      error(WarningCode.DEPRECATED_EXPORT_USE, 47, 1),
+    ]);
+  }
+
   test_hide() async {
     newFile('$testPackageLibPath/lib1.dart', '''
 class A {}
@@ -297,6 +363,25 @@
 ''');
   }
 
+  test_unnecessary_hasError() async {
+    newFile('$testPackageLibPath/a.dart', '''
+class A {}
+''');
+
+    newFile('$testPackageLibPath/b.dart', '''
+export 'a.dart';
+class B {}
+''');
+
+    await assertErrorsInCode('''
+import 'a.dart';
+import 'b.dart';
+void f(A _, B _, C _) {}
+''', [
+      error(CompileTimeErrorCode.UNDEFINED_CLASS, 51, 1),
+    ]);
+  }
+
   test_unnecessaryImport() async {
     newFile('$testPackageLibPath/lib1.dart', '''
 class A {}
diff --git a/pkg/analyzer/test/src/diagnostics/unused_import_test.dart b/pkg/analyzer/test/src/diagnostics/unused_import_test.dart
index 9e45b31..dc1e091 100644
--- a/pkg/analyzer/test/src/diagnostics/unused_import_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unused_import_test.dart
@@ -315,6 +315,30 @@
 ''');
   }
 
+  test_extension_instance_method_inPart() async {
+    newFile('$testPackageLibPath/a.dart', r'''
+extension E on int {
+  void foo() {}
+}
+''');
+
+    var b = newFile('$testPackageLibPath/b.dart', r'''
+import 'a.dart';
+part 'c.dart';
+''');
+
+    var c = newFile('$testPackageLibPath/c.dart', r'''
+part of 'b.dart';
+
+void f() {
+  0.foo();
+}
+''');
+
+    await assertErrorsInFile2(b, []);
+    await assertErrorsInFile2(c, []);
+  }
+
   test_extension_instance_operator_binary() async {
     newFile('$testPackageLibPath/lib1.dart', r'''
 extension E on String {
@@ -475,6 +499,52 @@
     ]);
   }
 
+  test_noPrefix_constructorName_name() async {
+    await assertErrorsInCode(r'''
+import 'dart:async';
+
+class A {
+  A.foo();
+}
+
+void f() {
+  A.foo();
+}
+''', [
+      error(WarningCode.UNUSED_IMPORT, 7, 12),
+    ]);
+  }
+
+  test_noPrefix_named_argument() async {
+    await assertErrorsInCode(r'''
+import 'dart:math';
+
+void f() {
+  Duration(seconds: 0);
+}
+''', [
+      error(WarningCode.UNUSED_IMPORT, 7, 11),
+    ]);
+  }
+
+  test_prefixed_commentReference_prefix() async {
+    await assertNoErrorsInCode(r'''
+import 'dart:math' as math;
+
+/// [math]
+void f() {}
+''');
+  }
+
+  test_prefixed_commentReference_prefixClass() async {
+    await assertNoErrorsInCode(r'''
+import 'dart:math' as math;
+
+/// [math.Random]
+void f() {}
+''');
+  }
+
   test_show() async {
     newFile('$testPackageLibPath/lib1.dart', r'''
 class A {}
diff --git a/pkg/linter/test/rules/use_key_in_widget_constructors_test.dart b/pkg/linter/test/rules/use_key_in_widget_constructors_test.dart
index cd6446b..44ee669 100644
--- a/pkg/linter/test/rules/use_key_in_widget_constructors_test.dart
+++ b/pkg/linter/test/rules/use_key_in_widget_constructors_test.dart
@@ -36,8 +36,6 @@
     await assertNoDiagnostics(r'''
 part of 'a.dart';
 
-import 'package:flutter/widgets.dart';
-
 augment class W {
   augment const W();
 }
@@ -96,13 +94,11 @@
     await assertDiagnostics(r'''
 part of 'a.dart';
 
-import 'package:flutter/widgets.dart';
-
 augment class W {
   const W();
 }
 ''', [
-      lint(85, 1),
+      lint(45, 1),
     ]);
   }