Parts. Remove GatherUsedImportedElementsVisitor.
Change-Id: Ice56db3284631bf7cd3fe6e9c519f577c3486303
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/384120
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
index 714e2dfb..c3a437c 100644
--- a/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/library_analyzer.dart
@@ -313,7 +313,6 @@
_libraryVerificationContext.constructorFieldsVerifier.report();
if (_analysisOptions.warning) {
- var usedImportedElements = <UsedImportedElements>[];
var usedLocalElements = <UsedLocalElements>[];
for (var fileAnalysis in _libraryFiles.values) {
{
@@ -321,17 +320,11 @@
fileAnalysis.unit.accept(visitor);
usedLocalElements.add(visitor.usedElements);
}
- {
- var visitor = GatherUsedImportedElementsVisitor(_libraryElement);
- fileAnalysis.unit.accept(visitor);
- usedImportedElements.add(visitor.usedElements);
- }
}
var usedElements = UsedLocalElements.merge(usedLocalElements);
for (var fileAnalysis in _libraryFiles.values) {
_computeWarnings(
fileAnalysis,
- usedImportedElements: usedImportedElements,
usedElements: usedElements,
);
}
@@ -465,7 +458,6 @@
void _computeWarnings(
FileAnalysis fileAnalysis, {
- required List<UsedImportedElements> usedImportedElements,
required UsedLocalElements usedElements,
}) {
var errorReporter = fileAnalysis.errorReporter;
@@ -509,14 +501,12 @@
fileAnalysis: fileAnalysis,
);
verifier.addImports(unit);
- usedImportedElements.forEach(verifier.removeUsedElements);
verifier.generateDuplicateExportWarnings(errorReporter);
verifier.generateDuplicateImportWarnings(errorReporter);
verifier.generateDuplicateShownHiddenNameWarnings(errorReporter);
verifier.generateUnusedImportHints(errorReporter);
verifier.generateUnusedShownNameHints(errorReporter);
- verifier.generateUnnecessaryImportHints(
- errorReporter, usedImportedElements);
+ verifier.generateUnnecessaryImportHints(errorReporter);
}
// Unused local elements.
diff --git a/pkg/analyzer/lib/src/error/imports_verifier.dart b/pkg/analyzer/lib/src/error/imports_verifier.dart
index b1ce0d4f..1e4374c 100644
--- a/pkg/analyzer/lib/src/error/imports_verifier.dart
+++ b/pkg/analyzer/lib/src/error/imports_verifier.dart
@@ -2,234 +2,13 @@
// 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/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';
-/// A visitor that visits ASTs and fills [UsedImportedElements].
-class GatherUsedImportedElementsVisitor extends RecursiveAstVisitor<void> {
- final LibraryElement library;
- final UsedImportedElements usedElements = UsedImportedElements();
-
- GatherUsedImportedElementsVisitor(this.library);
-
- @override
- void visitAssignmentExpression(AssignmentExpression node) {
- _recordAssignmentTarget(node, node.leftHandSide);
- return super.visitAssignmentExpression(node);
- }
-
- @override
- void visitBinaryExpression(BinaryExpression node) {
- _recordIfExtensionMember(node.staticElement);
- return super.visitBinaryExpression(node);
- }
-
- @override
- void visitExportDirective(ExportDirective node) {
- _visitDirective(node);
- }
-
- @override
- void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
- _recordIfExtensionMember(node.staticElement);
- return super.visitFunctionExpressionInvocation(node);
- }
-
- @override
- void visitImportDirective(ImportDirective node) {
- _visitDirective(node);
- }
-
- @override
- void visitIndexExpression(IndexExpression node) {
- _recordIfExtensionMember(node.staticElement);
- return super.visitIndexExpression(node);
- }
-
- @override
- void visitLibraryDirective(LibraryDirective node) {
- _visitDirective(node);
- }
-
- @override
- void visitNamedType(NamedType node) {
- _recordPrefixedElement(node.importPrefix, node.element);
- super.visitNamedType(node);
- }
-
- @override
- void visitPatternField(PatternField node) {
- _recordIfExtensionMember(node.element);
- super.visitPatternField(node);
- }
-
- @override
- void visitPostfixExpression(PostfixExpression node) {
- _recordAssignmentTarget(node, node.operand);
- return super.visitPostfixExpression(node);
- }
-
- @override
- void visitPrefixExpression(PrefixExpression node) {
- _recordAssignmentTarget(node, node.operand);
- _recordIfExtensionMember(node.staticElement);
- return super.visitPrefixExpression(node);
- }
-
- @override
- void visitSimpleIdentifier(SimpleIdentifier node) {
- _visitIdentifier(node, node.staticElement);
- }
-
- void _recordAssignmentTarget(
- CompoundAssignmentExpression node,
- Expression target,
- ) {
- if (target is IndexExpression) {
- _recordIfExtensionMember(node.readElement);
- _recordIfExtensionMember(node.writeElement);
- } else if (target is PrefixedIdentifier) {
- _visitIdentifier(target.identifier, node.readElement);
- _visitIdentifier(target.identifier, node.writeElement);
- } else if (target is PropertyAccess) {
- _visitIdentifier(target.propertyName, node.readElement);
- _visitIdentifier(target.propertyName, node.writeElement);
- } else if (target is SimpleIdentifier) {
- _visitIdentifier(target, node.readElement);
- _visitIdentifier(target, node.writeElement);
- }
- }
-
- void _recordIfExtensionMember(Element? element) {
- if (element != null) {
- var enclosingElement = element.enclosingElement3;
- if (enclosingElement is ExtensionElement) {
- _recordUsedExtension(enclosingElement);
- }
- }
- }
-
- void _recordPrefixedElement(
- ImportPrefixReference? importPrefix,
- Element? element,
- ) {
- if (element is MultiplyDefinedElement) {
- for (var component in element.conflictingElements) {
- _recordPrefixedElement(importPrefix, component);
- return;
- }
- }
-
- // Invalid code can use `importPrefix` as a named type;
- if (element is PrefixElement) {
- usedElements.prefixMap[element] ??= [];
- return;
- }
-
- if (importPrefix != null) {
- var prefixElement = importPrefix.element;
- if (prefixElement is PrefixElement) {
- var map = usedElements.prefixMap[prefixElement] ??= [];
- if (element != null) {
- map.add(element);
- }
- }
- } else if (element != null) {
- _recordUsedElement(element);
- }
- }
-
- /// If the given [identifier] is prefixed with a [PrefixElement], fill the
- /// corresponding `UsedImportedElements.prefixMap` entry and return `true`.
- bool _recordPrefixMap(SimpleIdentifier identifier, Element element) {
- bool recordIfTargetIsPrefixElement(Expression? target) {
- if (target is SimpleIdentifier) {
- var targetElement = target.staticElement;
- if (targetElement is PrefixElement) {
- List<Element> prefixedElements = usedElements.prefixMap
- .putIfAbsent(targetElement, () => <Element>[]);
- prefixedElements.add(element);
- return true;
- }
- }
- return false;
- }
-
- var parent = identifier.parent;
- if (parent is MethodInvocation && parent.methodName == identifier) {
- return recordIfTargetIsPrefixElement(parent.target);
- }
- if (parent is PrefixedIdentifier && parent.identifier == identifier) {
- return recordIfTargetIsPrefixElement(parent.prefix);
- }
- return false;
- }
-
- /// Records use of an unprefixed [element].
- void _recordUsedElement(Element element) {
- // Ignore if an unknown library.
- var containingLibrary = element.library;
- if (containingLibrary == null) {
- return;
- }
- // Ignore if a local element.
- if (library == containingLibrary) {
- return;
- }
- // Remember the element.
- usedElements.elements.add(element);
- }
-
- void _recordUsedExtension(ExtensionElement extension) {
- // Ignore if a local element.
- if (library == extension.library) {
- return;
- }
- // Remember the element.
- usedElements.usedExtensions.add(extension);
- }
-
- /// Visit identifiers used by the given [directive].
- void _visitDirective(Directive directive) {
- directive.documentationComment?.accept(this);
- directive.metadata.accept(this);
- }
-
- void _visitIdentifier(SimpleIdentifier identifier, Element? element) {
- if (element == null) {
- return;
- }
- // Record `importPrefix.identifier` into 'prefixMap'.
- if (_recordPrefixMap(identifier, element)) {
- return;
- }
- var enclosingElement = element.enclosingElement3;
- if (element is PrefixElement) {
- usedElements.prefixMap.putIfAbsent(element, () => <Element>[]);
- } else if (enclosingElement is CompilationUnitElement) {
- _recordUsedElement(element);
- } else if (enclosingElement is ExtensionElement) {
- _recordUsedExtension(enclosingElement);
- return;
- } else if (element is MultiplyDefinedElement) {
- // If the element is multiply defined then call this method recursively
- // for each of the conflicting elements.
- List<Element> conflictingElements = element.conflictingElements;
- int length = conflictingElements.length;
- for (int i = 0; i < length; i++) {
- Element elt = conflictingElements[i];
- _visitIdentifier(identifier, elt);
- }
- }
- }
-}
-
/// Instances of the class `ImportsVerifier` visit all of the referenced
/// libraries in the source code verifying that all of the imports are used,
/// otherwise a [HintCode.UNUSED_IMPORT] hint is generated with
@@ -256,7 +35,7 @@
/// this list represents the set of unused imports.
///
/// See [ImportsVerifier.generateUnusedImportErrors].
- final List<ImportDirective> _unusedImports = [];
+ final Set<ImportDirective> _unusedImports = {};
/// After the list of [unusedImports] has been computed, this list is a proper
/// subset of the unused imports that are listed more than once.
@@ -266,31 +45,6 @@
/// than once.
final List<ExportDirective> _duplicateExports = [];
- /// The cache of [Namespace]s for [ImportDirective]s.
- final Map<ImportDirective, Namespace> _namespaceMap = {};
-
- /// This is a map between prefix elements and the import directives from which
- /// they are derived. In cases where a type is referenced via a prefix
- /// element, the import directive can be marked as used (removed from the
- /// unusedImports) by looking at the resolved `lib` in `lib.X`, instead of
- /// looking at which library the `lib.X` resolves.
- final Map<PrefixElement, List<ImportDirective>> _prefixElementMap = {};
-
- /// A map of identifiers that the current library's imports show, but that the
- /// library does not use.
- ///
- /// Each import directive maps to a list of the identifiers that are imported
- /// via the "show" keyword.
- ///
- /// As each identifier is visited by this visitor, it is identified as being
- /// used by the library, and the identifier is removed from this map (under
- /// the import that imported it). After all the sources in the library have
- /// been evaluated, each list in this map's values present the set of unused
- /// shown elements.
- ///
- /// See [ImportsVerifier.generateUnusedShownNameHints].
- final Map<ImportDirective, List<SimpleIdentifier>> _unusedShownNamesMap = {};
-
/// A map of names that are hidden more than once.
final Map<NamespaceDirective, List<SimpleIdentifier>>
_duplicateHiddenNamesMap = {};
@@ -312,33 +66,16 @@
if (libraryElement == null) {
continue;
}
+ if (libraryElement.isSynthetic) {
+ continue;
+ }
_allImports.add(directive);
- _unusedImports.add(directive);
importsWithLibraries.add(
_NamespaceDirective(
node: directive,
library: libraryElement,
),
);
- //
- // Initialize prefixElementMap
- //
- if (directive.asKeyword != null) {
- var prefixIdentifier = directive.prefix;
- if (prefixIdentifier != null) {
- var element = prefixIdentifier.staticElement;
- if (element is PrefixElement) {
- var list = _prefixElementMap[element];
- if (list == null) {
- list = <ImportDirective>[];
- _prefixElementMap[element] = list;
- }
- list.add(directive);
- }
- // TODO(jwren): Can the element ever not be a PrefixElement?
- }
- }
- _addShownNames(directive);
} else if (directive is ExportDirective) {
var libraryElement = directive.element?.exportedLibrary;
if (libraryElement == null) {
@@ -433,8 +170,7 @@
/// 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) {
+ void generateUnnecessaryImportHints(ErrorReporter errorReporter) {
var importsTracking = fileAnalysis.importsTracking;
var usedImports = {..._allImports}..removeAll(_unusedImports);
@@ -442,6 +178,17 @@
var firstElement = firstDirective.element!;
var tracker = importsTracking.trackerOf(firstElement);
+ // Ignore unresolved imports.
+ var importedLibrary = firstElement.importedLibrary;
+ if (importedLibrary == null) {
+ continue;
+ }
+
+ // Ignore explicit dart:core import.
+ if (importedLibrary.isDartCore) {
+ continue;
+ }
+
for (var secondDirective in usedImports) {
if (secondDirective == firstDirective) {
continue;
@@ -514,6 +261,7 @@
var isUsed = tracking.importToUsedElements.containsKey(importElement);
if (!isUsed) {
+ _unusedImports.add(importDirective);
errorReporter.atNode(
importDirective.uri,
WarningCode.UNUSED_IMPORT,
@@ -525,117 +273,57 @@
}
}
- /// Use the error [reporter] to report an [HintCode.UNUSED_SHOWN_NAME] hint
+ /// Use the error [reporter] to report an [WarningCode.UNUSED_SHOWN_NAME]
/// for each unused shown name.
///
- /// This method should only be invoked after all of the compilation units have
- /// been visited by this visitor.
+ /// This method should be invoked after [generateUnusedImportHints].
void generateUnusedShownNameHints(ErrorReporter reporter) {
- _unusedShownNamesMap.forEach(
- (ImportDirective importDirective, List<SimpleIdentifier> identifiers) {
- if (_unusedImports.contains(importDirective)) {
- // The whole import is unused, not just one or more shown names from it,
- // so an "unused_import" hint will be generated, making it unnecessary
- // to generate hints for the individual names.
- return;
- }
- int length = identifiers.length;
- for (int i = 0; i < length; i++) {
- Identifier identifier = identifiers[i];
- var duplicateNames = _duplicateShownNamesMap[importDirective];
- if (duplicateNames == null || !duplicateNames.contains(identifier)) {
- // Only generate a hint if we won't also generate a
- // "duplicate_shown_name" hint for the same identifier.
- reporter.atNode(
- identifier,
- WarningCode.UNUSED_SHOWN_NAME,
- arguments: [identifier.name],
- );
- }
- }
- });
- }
-
- /// Remove elements from [_unusedImports] using the given [usedElements].
- void removeUsedElements(UsedImportedElements usedElements) {
- bool everythingIsKnownToBeUsed() =>
- _unusedImports.isEmpty && _unusedShownNamesMap.isEmpty;
-
- // Process import prefixes.
- for (var entry in usedElements.prefixMap.entries) {
- if (everythingIsKnownToBeUsed()) {
- return;
- }
- var prefix = entry.key;
- var importDirectives = _prefixElementMap[prefix];
- if (importDirectives == null) {
+ var importsTracking = fileAnalysis.importsTracking;
+ for (var importDirective in fileAnalysis.unit.directives) {
+ if (importDirective is! ImportDirectiveImpl) {
continue;
}
- var elements = entry.value;
- // Find import directives using namespaces.
- for (var importDirective in importDirectives) {
- if (elements.isEmpty) {
- // [prefix] and [elements] were added to [usedElements.prefixMap] but
- // [elements] is empty, so the prefix was referenced incorrectly.
- // Another diagnostic about the prefix reference is reported, and we
- // shouldn't confuse by also reporting an unused prefix.
- _unusedImports.remove(importDirective);
- }
- var namespace = _namespaceMap.computeNamespace(importDirective);
- if (namespace == null) {
- continue;
- }
- for (var element in elements) {
- if (namespace.providesPrefixed(prefix.name, element)) {
- _unusedImports.remove(importDirective);
- _removeFromUnusedShownNamesMap(element, importDirective);
- }
- }
- }
- }
- // Process top-level elements.
- for (Element element in usedElements.elements) {
- if (everythingIsKnownToBeUsed()) {
- return;
+ // The whole import is unused, not just one or more shown names from it,
+ // so an "unused_import" hint will be generated, making it unnecessary
+ // to generate hints for the individual names.
+ if (_unusedImports.contains(importDirective)) {
+ continue;
}
- // Find import directives using namespaces.
- for (ImportDirective importDirective in _allImports) {
- var namespace = _namespaceMap.computeNamespace(importDirective);
- if (namespace == null) {
- continue;
- }
- if (namespace.provides(element)) {
- _unusedImports.remove(importDirective);
- _removeFromUnusedShownNamesMap(element, importDirective);
- }
+
+ // Ignore unresolved imports.
+ var importElement = importDirective.element!;
+ var importedLibrary = importElement.importedLibrary;
+ if (importedLibrary == null) {
+ continue;
}
- }
- // Process extension elements.
- for (ExtensionElement extensionElement in usedElements.usedExtensions) {
- if (everythingIsKnownToBeUsed()) {
- return;
+
+ // Ignore explicit dart:core import.
+ if (importedLibrary.isDartCore) {
+ continue;
}
- var elementName = extensionElement.name!;
- // 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) {
- _unusedImports.remove(importDirective);
- _removeFromUnusedShownNamesMap(extensionElement, importDirective);
- }
- } 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) {
- _unusedImports.remove(importDirective);
- _removeFromUnusedShownNamesMap(extensionElement, importDirective);
+
+ for (var combinator in importDirective.combinators) {
+ if (combinator is ShowCombinatorImpl) {
+ for (var identifier in combinator.shownNames) {
+ var element = identifier.staticElement;
+ if (element != null) {
+ var importElements = importsTracking.elementsOf(importElement);
+
+ var isUsed = importElements.contains(element);
+ if (element is PropertyInducingElement) {
+ isUsed = importElements.contains(element.getter) ||
+ importElements.contains(element.setter);
+ }
+
+ if (!isUsed) {
+ reporter.atNode(
+ identifier,
+ WarningCode.UNUSED_SHOWN_NAME,
+ arguments: [identifier.name],
+ );
+ }
+ }
}
}
}
@@ -676,21 +364,6 @@
}
}
- /// Add every shown name from [importDirective] into [_unusedShownNamesMap].
- void _addShownNames(ImportDirective importDirective) {
- List<SimpleIdentifier> identifiers = <SimpleIdentifier>[];
- _unusedShownNamesMap[importDirective] = identifiers;
- for (var combinator in importDirective.combinators) {
- if (combinator is ShowCombinator) {
- for (SimpleIdentifier name in combinator.shownNames) {
- if (name.staticElement != null) {
- identifiers.add(name);
- }
- }
- }
- }
- }
-
/// Return the duplicates in [directives].
List<NamespaceDirective> _duplicates(List<_NamespaceDirective> directives) {
var duplicates = <NamespaceDirective>[];
@@ -722,57 +395,6 @@
}
return duplicates;
}
-
- /// Remove [element] from the list of names shown by [importDirective].
- void _removeFromUnusedShownNamesMap(
- Element element, ImportDirective importDirective) {
- var identifiers = _unusedShownNamesMap[importDirective];
- if (identifiers == null) {
- return;
- }
-
- /// When an element is used, it might be converted into a `Member`,
- /// to apply substitution, or turn it into legacy. But using something
- /// is purely declaration based.
- bool hasElement(SimpleIdentifier identifier, Element element) {
- return identifier.staticElement?.declaration == element.declaration;
- }
-
- int length = identifiers.length;
- for (int i = 0; i < length; i++) {
- var identifier = identifiers[i];
- if (element is PropertyAccessorElement) {
- // If the getter or setter of a variable is used, then the variable (the
- // shown name) is used.
- var variable = element.variable2;
- if (variable != null && hasElement(identifier, variable)) {
- identifiers.remove(identifier);
- break;
- }
- } else {
- if (hasElement(identifier, element)) {
- identifiers.remove(identifier);
- break;
- }
- }
- }
- if (identifiers.isEmpty) {
- _unusedShownNamesMap.remove(importDirective);
- }
- }
-}
-
-/// A container with information about used imports prefixes and used imported
-/// elements.
-class UsedImportedElements {
- /// The map of referenced prefix elements and the elements that they prefix.
- final Map<PrefixElement, List<Element>> prefixMap = {};
-
- /// The set of referenced top-level elements.
- final Set<Element> elements = {};
-
- /// The set of extensions defining members that are referenced.
- final Set<ExtensionElement> usedExtensions = {};
}
/// [NamespaceDirective] with non-null imported or exported [LibraryElement].
@@ -788,56 +410,3 @@
/// Returns the absolute URI of the library.
String get libraryUriStr => '${library.source.uri}';
}
-
-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.uri.isScheme('dart') && source2.uri.isScheme('dart');
- }
-}
diff --git a/pkg/analyzer/test/src/diagnostics/unnecessary_import_test.dart b/pkg/analyzer/test/src/diagnostics/unnecessary_import_test.dart
index 8d771aa..a244524a 100644
--- a/pkg/analyzer/test/src/diagnostics/unnecessary_import_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unnecessary_import_test.dart
@@ -415,4 +415,18 @@
error(HintCode.UNNECESSARY_IMPORT, 28, 12),
]);
}
+
+ test_uriDoesNotExist() async {
+ newFile('$testPackageLibPath/a.dart', '''
+class A {}
+''');
+
+ await assertErrorsInCode('''
+import 'a.dart';
+import 'b.dart';
+void f(A _) {}
+''', [
+ error(CompileTimeErrorCode.URI_DOES_NOT_EXIST, 24, 8),
+ ]);
+ }
}
diff --git a/pkg/analyzer/test/src/diagnostics/unused_shown_name_test.dart b/pkg/analyzer/test/src/diagnostics/unused_shown_name_test.dart
index 843cc6f..a833dcd 100644
--- a/pkg/analyzer/test/src/diagnostics/unused_shown_name_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/unused_shown_name_test.dart
@@ -15,6 +15,12 @@
@reflectiveTest
class UnusedShownNameTest extends PubPackageResolutionTest {
+ test_dartCore_unused() async {
+ await assertNoErrorsInCode(r'''
+import 'dart:core' as core show int;
+''');
+ }
+
test_extension_instance_method_unused() async {
newFile('$testPackageLibPath/lib1.dart', r'''
extension E on String {