Issue 23067. Report HintCode.DEPRECATED_EXPORT_USE
It found a few cases for `BytesBuilder` exported from `dart:io`.
I fixed most of them in a separate CL.
But package:flutter (itself only) is clean.
There are a few violations in google3.
I will ignore most of them, and fix a few.
Bug: https://github.com/dart-lang/sdk/issues/23067
Change-Id: Ic89370ad84caa60fd49326c2bc60ad5d927e2264
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/248343
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
index 3f8f4d7..5adb652 100644
--- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
+++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml
@@ -1223,6 +1223,8 @@
status: hasFix
HintCode.DEAD_CODE_ON_CATCH_SUBTYPE:
status: hasFix
+HintCode.DEPRECATED_EXPORT_USE:
+ status: needsEvaluation
HintCode.DEPRECATED_EXTENDS_FUNCTION:
status: needsFix
notes: |-
diff --git a/pkg/analyzer/lib/dart/element/scope.dart b/pkg/analyzer/lib/dart/element/scope.dart
index 0fdfa0c..1b2a745 100644
--- a/pkg/analyzer/lib/dart/element/scope.dart
+++ b/pkg/analyzer/lib/dart/element/scope.dart
@@ -5,6 +5,8 @@
import 'package:analyzer/dart/element/element.dart';
/// Scopes are used to resolve names to elements.
+///
+/// Clients may not extend, implement or mix-in this class.
abstract class Scope {
/// Return the result of lexical lookup for the given [id], not `null`.
///
@@ -14,9 +16,10 @@
ScopeLookupResult lookup(String id);
}
-class ScopeLookupResult {
- final Element? getter;
- final Element? setter;
-
- ScopeLookupResult(this.getter, this.setter);
+/// The result of a single name lookup.
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class ScopeLookupResult {
+ Element? get getter;
+ Element? get setter;
}
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 6d41685..df3709c 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -553,6 +553,7 @@
HintCode.DEAD_CODE,
HintCode.DEAD_CODE_CATCH_FOLLOWING_CATCH,
HintCode.DEAD_CODE_ON_CATCH_SUBTYPE,
+ HintCode.DEPRECATED_EXPORT_USE,
HintCode.DEPRECATED_EXTENDS_FUNCTION,
HintCode.DEPRECATED_FUNCTION_CLASS_DECLARATION,
HintCode.DEPRECATED_IMPLEMENTS_FUNCTION,
diff --git a/pkg/analyzer/lib/src/dart/element/scope.dart b/pkg/analyzer/lib/src/dart/element/scope.dart
index 7eb9526..259ad2c 100644
--- a/pkg/analyzer/lib/src/dart/element/scope.dart
+++ b/pkg/analyzer/lib/src/dart/element/scope.dart
@@ -6,6 +6,7 @@
import 'package:analyzer/dart/element/scope.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/summary2/combinator.dart';
+import 'package:analyzer/src/summary2/export.dart';
/// The scope defined by a class.
class ClassScope extends EnclosedScope {
@@ -37,7 +38,7 @@
var getter = _getters[id];
var setter = _setters[id];
if (getter != null || setter != null) {
- return ScopeLookupResult(getter, setter);
+ return ScopeLookupResultImpl(getter, setter);
}
return _parent.lookup(id);
@@ -92,6 +93,20 @@
}
}
+class ImportedElement {
+ final Element element;
+
+ /// This flag is set to `true` if [element] is available using import
+ /// directives where every imported library re-exports the element, and
+ /// every such `export` directive is marked as deprecated.
+ final bool isFromDeprecatedExport;
+
+ ImportedElement({
+ required this.element,
+ required this.isFromDeprecatedExport,
+ });
+}
+
class LibraryScope extends EnclosedScope {
final LibraryElement _element;
final List<ExtensionElement> extensions = [];
@@ -133,8 +148,8 @@
class PrefixScope implements Scope {
final LibraryOrAugmentationElement _library;
- final Map<String, Element> _getters = {};
- final Map<String, Element> _setters = {};
+ final Map<String, ImportedElement> _getters = {};
+ final Map<String, ImportedElement> _setters = {};
final Set<ExtensionElement> _extensions = {};
LibraryElement? _deferredLibrary;
@@ -150,7 +165,12 @@
final reference = exportedReference.reference;
if (combinators.allows(reference.name)) {
final element = elementFactory.elementOfReference(reference)!;
- _add(element);
+ final importedElement = ImportedElement(
+ element: element,
+ isFromDeprecatedExport:
+ _isFromDeprecatedExport(importedLibrary, exportedReference),
+ );
+ _add(importedElement);
}
}
if (import.isDeferred) {
@@ -165,19 +185,20 @@
ScopeLookupResult lookup(String id) {
var deferredLibrary = _deferredLibrary;
if (deferredLibrary != null && id == FunctionElement.LOAD_LIBRARY_NAME) {
- return ScopeLookupResult(deferredLibrary.loadLibraryFunction, null);
+ return ScopeLookupResultImpl(deferredLibrary.loadLibraryFunction, null);
}
var getter = _getters[id];
var setter = _setters[id];
- return ScopeLookupResult(getter, setter);
+ return PrefixScopeLookupResult(getter, setter);
}
- void _add(Element element) {
+ void _add(ImportedElement imported) {
+ final element = imported.element;
if (element is PropertyAccessorElement && element.isSetter) {
- _addTo(map: _setters, element: element);
+ _addTo(map: _setters, incoming: imported);
} else {
- _addTo(map: _getters, element: element);
+ _addTo(map: _getters, incoming: imported);
if (element is ExtensionElement) {
_extensions.add(element);
}
@@ -185,18 +206,30 @@
}
void _addTo({
- required Map<String, Element> map,
- required Element element,
+ required Map<String, ImportedElement> map,
+ required ImportedElement incoming,
}) {
- var id = element.displayName;
+ final id = incoming.element.displayName;
+ final existing = map[id];
- var existing = map[id];
- if (existing != null && existing != element) {
- map[id] = _merge(existing, element);
+ if (existing == null) {
+ map[id] = incoming;
return;
}
- map[id] = element;
+ if (existing.element == incoming.element) {
+ map[id] = ImportedElement(
+ element: incoming.element,
+ isFromDeprecatedExport:
+ existing.isFromDeprecatedExport && incoming.isFromDeprecatedExport,
+ );
+ return;
+ }
+
+ map[id] = ImportedElement(
+ element: _merge(existing.element, incoming.element),
+ isFromDeprecatedExport: false,
+ );
}
Element _merge(Element existing, Element other) {
@@ -233,6 +266,23 @@
}
}
+ /// Return `true` if [exportedReference] comes only from deprecated exports.
+ static bool _isFromDeprecatedExport(
+ LibraryElementImpl importedLibrary,
+ ExportedReference exportedReference,
+ ) {
+ if (exportedReference is ExportedReferenceExported) {
+ for (final exportIndex in exportedReference.indexes) {
+ final export = importedLibrary.exports[exportIndex];
+ if (!export.hasDeprecated) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
static bool _isSdkElement(Element element) {
if (element is DynamicElementImpl || element is NeverElementImpl) {
return true;
@@ -244,6 +294,32 @@
}
}
+class PrefixScopeLookupResult implements ScopeLookupResult {
+ final ImportedElement? importedGetter;
+ final ImportedElement? importedSetter;
+
+ PrefixScopeLookupResult(
+ this.importedGetter,
+ this.importedSetter,
+ );
+
+ @override
+ Element? get getter => importedGetter?.element;
+
+ @override
+ Element? get setter => importedSetter?.element;
+}
+
+class ScopeLookupResultImpl implements ScopeLookupResult {
+ @override
+ final Element? getter;
+
+ @override
+ final Element? setter;
+
+ ScopeLookupResultImpl(this.getter, this.setter);
+}
+
class TypeParameterScope extends EnclosedScope {
TypeParameterScope(
super.parent,
diff --git a/pkg/analyzer/lib/src/dart/error/hint_codes.g.dart b/pkg/analyzer/lib/src/dart/error/hint_codes.g.dart
index 4d4efcf..5536f34 100644
--- a/pkg/analyzer/lib/src/dart/error/hint_codes.g.dart
+++ b/pkg/analyzer/lib/src/dart/error/hint_codes.g.dart
@@ -94,6 +94,14 @@
hasPublishedDocs: true,
);
+ /// Parameters:
+ /// 0: the name of the element
+ static const HintCode DEPRECATED_EXPORT_USE = HintCode(
+ 'DEPRECATED_EXPORT_USE',
+ "The ability to import '{0}' indirectly has been deprecated.",
+ correctionMessage: "Try importing '{0}' directly.",
+ );
+
/// No parameters.
static const HintCode DEPRECATED_EXTENDS_FUNCTION = HintCode(
'DEPRECATED_SUBTYPE_OF_FUNCTION',
diff --git a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
index c7169aa..6d951b3 100644
--- a/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/method_invocation_resolver.dart
@@ -5,6 +5,7 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/ast_factory.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
@@ -19,10 +20,11 @@
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/migratable_ast_info_provider.dart';
import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/scope_helpers.dart';
import 'package:analyzer/src/generated/super_context.dart';
import 'package:analyzer/src/generated/variable_type_provider.dart';
-class MethodInvocationResolver {
+class MethodInvocationResolver with ScopeHelpers {
/// Resolver visitor is separated from the elements resolver, which calls
/// this method resolver. If we rewrite a [MethodInvocation] node, we put
/// the resulting [FunctionExpressionInvocation] into the original node
@@ -75,6 +77,9 @@
_extensionResolver = _resolver.extensionResolver,
_inferenceHelper = inferenceHelper;
+ @override
+ ErrorReporter get errorReporter => _resolver.errorReporter;
+
TypeSystemImpl get _typeSystem => _resolver.typeSystem;
void resolve(
@@ -584,7 +589,13 @@
String name,
List<WhyNotPromotedGetter> whyNotPromotedList,
{required DartType? contextType}) {
- var element = nameNode.scopeLookupResult!.getter;
+ final scopeLookupResult = nameNode.scopeLookupResult!;
+ reportDeprecatedExportUseGetter(
+ scopeLookupResult: scopeLookupResult,
+ node: nameNode,
+ );
+
+ var element = scopeLookupResult.getter;
if (element != null) {
element = _resolver.toLegacyElement(element);
nameNode.staticElement = element;
@@ -668,7 +679,13 @@
}
}
- var element = prefix.scope.lookup(name).getter;
+ final scopeLookupResult = prefix.scope.lookup(name);
+ reportDeprecatedExportUseGetter(
+ scopeLookupResult: scopeLookupResult,
+ node: nameNode,
+ );
+
+ var element = scopeLookupResult.getter;
element = _resolver.toLegacyElement(element);
nameNode.staticElement = element;
diff --git a/pkg/analyzer/lib/src/dart/resolver/named_type_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/named_type_resolver.dart
index 1d564ff..63e41ad 100644
--- a/pkg/analyzer/lib/src/dart/resolver/named_type_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/named_type_resolver.dart
@@ -18,15 +18,18 @@
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/diagnostic/diagnostic_factory.dart';
import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/scope_helpers.dart';
/// Helper for resolving types.
///
/// The client must set [nameScope] before calling [resolve].
-class NamedTypeResolver {
+class NamedTypeResolver with ScopeHelpers {
final LibraryElementImpl _libraryElement;
final TypeSystemImpl typeSystem;
final DartType dynamicType;
final bool isNonNullableByDefault;
+
+ @override
final ErrorReporter errorReporter;
late Scope nameScope;
@@ -96,10 +99,8 @@
}
if (prefixElement is PrefixElement) {
- var nameNode = typeIdentifier.identifier;
- var name = nameNode.name;
-
- var element = prefixElement.scope.lookup(name).getter;
+ final nameNode = typeIdentifier.identifier;
+ final element = _lookupGetter(prefixElement.scope, nameNode);
nameNode.staticElement = element;
_resolveToElement(node, element);
return;
@@ -113,14 +114,13 @@
node.type = dynamicType;
} else {
var nameNode = typeIdentifier as SimpleIdentifierImpl;
- var name = nameNode.name;
- if (name == 'void') {
+ if (nameNode.name == 'void') {
node.type = VoidTypeImpl.instance;
return;
}
- var element = nameScope.lookup(name).getter;
+ final element = _lookupGetter(nameScope, nameNode);
nameNode.staticElement = element;
_resolveToElement(node, element);
}
@@ -282,6 +282,15 @@
}
}
+ Element? _lookupGetter(Scope scope, SimpleIdentifier node) {
+ final scopeLookupResult = scope.lookup(node.name);
+ reportDeprecatedExportUseGetter(
+ scopeLookupResult: scopeLookupResult,
+ node: node,
+ );
+ return scopeLookupResult.getter;
+ }
+
void _resolveToElement(NamedTypeImpl node, Element? element) {
if (element == null) {
node.type = dynamicType;
diff --git a/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
index 28f5ec9..6ddcf05 100644
--- a/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/property_element_resolver.dart
@@ -18,16 +18,18 @@
import 'package:analyzer/src/error/assignment_verifier.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/scope_helpers.dart';
import 'package:analyzer/src/generated/super_context.dart';
-class PropertyElementResolver {
+class PropertyElementResolver with ScopeHelpers {
final ResolverVisitor _resolver;
PropertyElementResolver(this._resolver);
- LibraryElement get _definingLibrary => _resolver.definingLibrary;
+ @override
+ ErrorReporter get errorReporter => _resolver.errorReporter;
- ErrorReporter get _errorReporter => _resolver.errorReporter;
+ LibraryElement get _definingLibrary => _resolver.definingLibrary;
ExtensionMemberResolver get _extensionResolver => _resolver.extensionResolver;
@@ -81,7 +83,7 @@
if (identical(targetType, NeverTypeImpl.instance)) {
// TODO(scheglov) Report directly in TypePropertyResolver?
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
HintCode.RECEIVER_OF_TYPE_NEVER,
target,
);
@@ -214,10 +216,18 @@
);
}
+ final scopeLookupResult = node.scopeLookupResult!;
+ reportDeprecatedExportUse(
+ scopeLookupResult: scopeLookupResult,
+ node: node,
+ hasRead: hasRead,
+ hasWrite: hasWrite,
+ );
+
Element? readElementRequested;
Element? readElementRecovery;
if (hasRead) {
- var readLookup = LexicalLookup.resolveGetter(node.scopeLookupResult!) ??
+ var readLookup = LexicalLookup.resolveGetter(scopeLookupResult) ??
_resolver.thisLookupGetter(node);
readElementRequested = _resolver.toLegacyElement(readLookup?.requested);
if (readElementRequested is PropertyAccessorElement &&
@@ -231,12 +241,12 @@
Element? writeElementRequested;
Element? writeElementRecovery;
if (hasWrite) {
- var writeLookup = LexicalLookup.resolveSetter(node.scopeLookupResult!) ??
+ var writeLookup = LexicalLookup.resolveSetter(scopeLookupResult) ??
_resolver.thisLookupSetter(node);
writeElementRequested = _resolver.toLegacyElement(writeLookup?.requested);
writeElementRecovery = _resolver.toLegacyElement(writeLookup?.recovery);
- AssignmentVerifier(_resolver.definingLibrary, _errorReporter).verify(
+ AssignmentVerifier(_resolver.definingLibrary, errorReporter).verify(
node: node,
requested: writeElementRequested,
recovery: writeElementRecovery,
@@ -261,7 +271,7 @@
) {
if (element.isStatic) return false;
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.STATIC_ACCESS_TO_INSTANCE_MEMBER,
identifier,
[identifier.name],
@@ -276,7 +286,7 @@
) {
if (element != null && element.isStatic) {
if (target is ExtensionOverride) {
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.EXTENSION_OVERRIDE_ACCESS_TO_STATIC_MEMBER,
propertyName,
);
@@ -296,7 +306,7 @@
// It is safe to assume that `enclosingElement.name` is non-`null`
// because it can only be `null` for extensions, and we handle that
// case above.
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.INSTANCE_ACCESS_TO_STATIC_MEMBER,
propertyName, [
propertyName.name,
@@ -339,7 +349,7 @@
var offset = leftBracket.offset;
var length = rightBracket.end - offset;
- _errorReporter.reportErrorForOffset(errorCode, offset, length, arguments);
+ errorReporter.reportErrorForOffset(errorCode, offset, length, arguments);
}
PropertyElementResolverResult _resolve({
@@ -408,7 +418,7 @@
}
if (targetType.isVoid) {
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.USE_OF_VOID_RESULT,
propertyName,
);
@@ -424,13 +434,13 @@
// type literal (which can only be a type instantiation of a type alias
// of a function type).
if (hasRead) {
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_GETTER_ON_FUNCTION_TYPE,
propertyName,
[propertyName.name, target.type.name.name],
);
} else {
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_SETTER_ON_FUNCTION_TYPE,
propertyName,
[propertyName.name, target.type.name.name],
@@ -457,7 +467,7 @@
if (hasRead) {
_checkForStaticMember(target, propertyName, result.getter);
if (result.needsGetterError) {
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_GETTER,
propertyName,
[propertyName.name, targetType],
@@ -468,7 +478,7 @@
if (hasWrite) {
_checkForStaticMember(target, propertyName, result.setter);
if (result.needsSetterError) {
- AssignmentVerifier(_definingLibrary, _errorReporter).verify(
+ AssignmentVerifier(_definingLibrary, errorReporter).verify(
node: propertyName,
requested: null,
recovery: result.getter,
@@ -521,7 +531,7 @@
var code = typeReference.isEnum
? CompileTimeErrorCode.UNDEFINED_ENUM_CONSTANT
: CompileTimeErrorCode.UNDEFINED_GETTER;
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
code,
propertyName,
[propertyName.name, typeReference.name],
@@ -536,7 +546,7 @@
if (writeElement != null) {
writeElement = _resolver.toLegacyElement(writeElement);
if (!_isAccessible(writeElement)) {
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.PRIVATE_SETTER,
propertyName,
[propertyName.name],
@@ -549,7 +559,7 @@
} else {
// Recovery, try to use getter.
writeElementRecovery = typeReference.getGetter(propertyName.name);
- AssignmentVerifier(_definingLibrary, _errorReporter).verify(
+ AssignmentVerifier(_definingLibrary, errorReporter).verify(
node: propertyName,
requested: null,
recovery: writeElementRecovery,
@@ -584,7 +594,7 @@
// This method is only called for extension overrides, and extension
// overrides can only refer to named extensions. So it is safe to
// assume that `extension.name` is non-`null`.
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_EXTENSION_GETTER,
propertyName,
[memberName, extension.name!],
@@ -604,7 +614,7 @@
writeElement = extension.getSetter(memberName);
if (writeElement == null) {
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
// This method is only called for extension overrides, and extension
// overrides can only refer to named extensions. So it is safe to
// assume that `extension.name` is non-`null`.
@@ -637,7 +647,7 @@
}) {
if (target.parent is CascadeExpression) {
// Report this error and recover by treating it like a non-cascade.
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.EXTENSION_OVERRIDE_WITH_CASCADE,
target.extensionName,
);
@@ -655,7 +665,7 @@
// This method is only called for extension overrides, and extension
// overrides can only refer to named extensions. So it is safe to
// assume that `element.name` is non-`null`.
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_EXTENSION_GETTER,
propertyName,
[memberName, element.name!],
@@ -671,7 +681,7 @@
// This method is only called for extension overrides, and extension
// overrides can only refer to named extensions. So it is safe to
// assume that `element.name` is non-`null`.
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_EXTENSION_SETTER,
propertyName,
[memberName, element.name!],
@@ -694,6 +704,12 @@
required bool forAnnotation,
}) {
var lookupResult = target.scope.lookup(identifier.name);
+ reportDeprecatedExportUse(
+ scopeLookupResult: lookupResult,
+ node: identifier,
+ hasRead: hasRead,
+ hasWrite: hasWrite,
+ );
var readElement = _resolver.toLegacyElement(lookupResult.getter);
var writeElement = _resolver.toLegacyElement(lookupResult.setter);
@@ -704,7 +720,7 @@
prefix: target.name,
name: identifier.name,
)) {
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_PREFIXED_NAME,
identifier,
[identifier.name, target.name],
@@ -751,13 +767,13 @@
readElement =
_resolver.inheritance.getInherited2(targetType.element, name);
if (readElement != null) {
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.ABSTRACT_SUPER_MEMBER_REFERENCE,
propertyName,
[readElement.kind.displayName, propertyName.name],
);
} else {
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_SUPER_GETTER,
propertyName,
[propertyName.name, targetType],
@@ -793,13 +809,13 @@
inherited: true,
);
if (writeElement != null) {
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.ABSTRACT_SUPER_MEMBER_REFERENCE,
propertyName,
[writeElement.kind.displayName, propertyName.name],
);
} else {
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_SUPER_SETTER,
propertyName,
[propertyName.name, targetType],
diff --git a/pkg/analyzer/lib/src/dart/resolver/simple_identifier_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/simple_identifier_resolver.dart
index 35266a2..9846ada 100644
--- a/pkg/analyzer/lib/src/dart/resolver/simple_identifier_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/simple_identifier_resolver.dart
@@ -15,8 +15,9 @@
import 'package:analyzer/src/dart/resolver/property_element_resolver.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/resolver.dart';
+import 'package:analyzer/src/generated/scope_helpers.dart';
-class SimpleIdentifierResolver {
+class SimpleIdentifierResolver with ScopeHelpers {
final ResolverVisitor _resolver;
final InvocationInferenceHelper _inferenceHelper;
@@ -24,7 +25,8 @@
SimpleIdentifierResolver(this._resolver)
: _inferenceHelper = _resolver.inferenceHelper;
- ErrorReporter get _errorReporter => _resolver.errorReporter;
+ @override
+ ErrorReporter get errorReporter => _resolver.errorReporter;
TypeProviderImpl get _typeProvider => _resolver.typeProvider;
@@ -37,6 +39,8 @@
_resolver.checkReadOfNotAssignedLocalVariable(node, node.staticElement);
+ _reportDeprecatedExportUse(node);
+
_resolve1(node);
_resolve2(node, contextType: contextType);
}
@@ -103,6 +107,16 @@
return false;
}
+ void _reportDeprecatedExportUse(SimpleIdentifierImpl node) {
+ final scopeLookupResult = node.scopeLookupResult;
+ if (scopeLookupResult != null) {
+ reportDeprecatedExportUseGetter(
+ scopeLookupResult: scopeLookupResult,
+ node: node,
+ );
+ }
+ }
+
void _resolve1(SimpleIdentifierImpl node) {
//
// Synthetic identifiers have been already reported during parsing.
@@ -158,14 +172,14 @@
var enclosingClass = _resolver.enclosingClass;
if (_isFactoryConstructorReturnType(node) &&
!identical(element, enclosingClass)) {
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.INVALID_FACTORY_NAME_NOT_A_CLASS, node);
} else if (_isConstructorReturnType(node) &&
!identical(element, enclosingClass)) {
// This error is now reported by the parser.
element = null;
} else if (element is PrefixElement && !_isValidAsPrefix(node)) {
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.PREFIX_IDENTIFIER_NOT_FOLLOWED_BY_DOT,
node,
[element.name],
@@ -173,13 +187,13 @@
} else if (element == null) {
// TODO(brianwilkerson) Recover from this error.
if (node.name == "await" && _resolver.enclosingFunction != null) {
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_IDENTIFIER_AWAIT,
node,
);
} else if (!_resolver.definingLibrary
.shouldIgnoreUndefinedIdentifier(node)) {
- _errorReporter.reportErrorForNode(
+ errorReporter.reportErrorForNode(
CompileTimeErrorCode.UNDEFINED_IDENTIFIER,
node,
[node.name],
diff --git a/pkg/analyzer/lib/src/generated/scope_helpers.dart b/pkg/analyzer/lib/src/generated/scope_helpers.dart
new file mode 100644
index 0000000..a49da26
--- /dev/null
+++ b/pkg/analyzer/lib/src/generated/scope_helpers.dart
@@ -0,0 +1,74 @@
+// Copyright (c) 2022, 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/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/scope.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/src/dart/element/scope.dart';
+import 'package:analyzer/src/error/codes.dart';
+
+/// Methods useful for [Scope] for resolution, but not belonging to it. This
+/// mixin exists to allow code to be more easily shared between separate
+/// resolvers.
+mixin ScopeHelpers {
+ ErrorReporter get errorReporter;
+
+ void reportDeprecatedExportUse({
+ required ScopeLookupResult scopeLookupResult,
+ required SimpleIdentifier node,
+ required bool hasRead,
+ required bool hasWrite,
+ }) {
+ if (hasRead) {
+ reportDeprecatedExportUseGetter(
+ scopeLookupResult: scopeLookupResult,
+ node: node,
+ );
+ }
+
+ if (hasWrite) {
+ reportDeprecatedExportUseSetter(
+ scopeLookupResult: scopeLookupResult,
+ node: node,
+ );
+ }
+ }
+
+ void reportDeprecatedExportUseGetter({
+ required ScopeLookupResult scopeLookupResult,
+ required SimpleIdentifier node,
+ }) {
+ if (scopeLookupResult is PrefixScopeLookupResult) {
+ _reportDeprecatedExportUse(
+ node: node,
+ imported: scopeLookupResult.importedGetter,
+ );
+ }
+ }
+
+ void reportDeprecatedExportUseSetter({
+ required ScopeLookupResult scopeLookupResult,
+ required SimpleIdentifier node,
+ }) {
+ if (scopeLookupResult is PrefixScopeLookupResult) {
+ _reportDeprecatedExportUse(
+ node: node,
+ imported: scopeLookupResult.importedSetter,
+ );
+ }
+ }
+
+ void _reportDeprecatedExportUse({
+ required SimpleIdentifier node,
+ required final ImportedElement? imported,
+ }) {
+ if (imported != null && imported.isFromDeprecatedExport) {
+ errorReporter.reportErrorForNode(
+ HintCode.DEPRECATED_EXPORT_USE,
+ node,
+ [node.name],
+ );
+ }
+ }
+}
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index f0e456a..de28c26 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -17677,6 +17677,12 @@
problemMessage: "Declaring a class named 'Function' is deprecated."
correctionMessage: Try renaming the class.
comment: Users should not create a class named `Function` anymore.
+ DEPRECATED_EXPORT_USE:
+ problemMessage: "The ability to import '{0}' indirectly has been deprecated."
+ correctionMessage: "Try importing '{0}' directly."
+ comment: |-
+ Parameters:
+ 0: the name of the element
DEPRECATED_MEMBER_USE:
problemMessage: "'{0}' is deprecated and shouldn't be used."
correctionMessage: Try replacing the use of the deprecated member with the replacement.
diff --git a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
index 91d48d2a9..be50344 100644
--- a/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
+++ b/pkg/analyzer/test/src/dart/micro/simple_file_resolver_test.dart
@@ -1886,6 +1886,7 @@
put: [k07]
elementFactory
hasElement
+ dart:_internal
dart:async
dart:core
package:dart.test/test.dart
diff --git a/pkg/analyzer/test/src/diagnostics/deprecated_export_use_test.dart b/pkg/analyzer/test/src/diagnostics/deprecated_export_use_test.dart
new file mode 100644
index 0000000..9209fb5
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/deprecated_export_use_test.dart
@@ -0,0 +1,471 @@
+// Copyright (c) 2022, 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/hint_codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(DeprecatedExportUseTest);
+ });
+}
+
+@reflectiveTest
+class DeprecatedExportUseTest extends PubPackageResolutionTest {
+ test_deprecated_class_asExpression() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+class A {}
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+''');
+
+ await assertErrorsInCode('''
+import 'b.dart';
+
+void f() {
+ A;
+}
+''', [
+ error(HintCode.DEPRECATED_EXPORT_USE, 31, 1),
+ ]);
+ }
+
+ test_deprecated_class_asExpression_prefixed() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+class A {}
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+''');
+
+ await assertErrorsInCode('''
+import 'b.dart' as prefix;
+
+void f() {
+ prefix.A;
+}
+''', [
+ error(HintCode.DEPRECATED_EXPORT_USE, 48, 1),
+ ]);
+ }
+
+ test_deprecated_class_asType() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+class A {}
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+''');
+
+ await assertErrorsInCode('''
+import 'b.dart';
+
+void f(A a) {}
+''', [
+ error(HintCode.DEPRECATED_EXPORT_USE, 25, 1),
+ ]);
+ }
+
+ test_deprecated_class_asType_prefixed() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+class A {}
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+''');
+
+ await assertErrorsInCode('''
+import 'b.dart' as prefix;
+
+void f(prefix.A a) {}
+''', [
+ error(HintCode.DEPRECATED_EXPORT_USE, 42, 1),
+ ]);
+ }
+
+ test_deprecated_class_import_show() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+class A {}
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+''');
+
+ await assertErrorsInCode('''
+import 'b.dart' show A;
+
+void f(A a) {}
+''', [
+ error(HintCode.DEPRECATED_EXPORT_USE, 32, 1),
+ ]);
+ }
+
+ test_deprecated_function() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+void foo() {}
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+''');
+
+ await assertErrorsInCode('''
+import 'b.dart';
+
+void f() {
+ foo();
+}
+''', [
+ error(HintCode.DEPRECATED_EXPORT_USE, 31, 3),
+ ]);
+ }
+
+ test_deprecated_function_prefixed() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+void foo() {}
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+''');
+
+ await assertErrorsInCode('''
+import 'b.dart' as prefix;
+
+void f() {
+ prefix.foo();
+}
+''', [
+ error(HintCode.DEPRECATED_EXPORT_USE, 48, 3),
+ ]);
+ }
+
+ test_deprecated_getter() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+int get foo => 0;
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+
+/// Does not prevent the hint.
+set foo(int _) {}
+''');
+
+ await assertErrorsInCode('''
+import 'b.dart';
+
+void f() {
+ foo;
+}
+''', [
+ error(HintCode.DEPRECATED_EXPORT_USE, 31, 3),
+ ]);
+ }
+
+ test_deprecated_getter_prefixed() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+int get foo => 0;
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+
+/// Does not prevent the hint.
+set foo(int _) {}
+''');
+
+ await assertErrorsInCode('''
+import 'b.dart' as prefix;
+
+void f() {
+ prefix.foo;
+}
+''', [
+ error(HintCode.DEPRECATED_EXPORT_USE, 48, 3),
+ ]);
+ }
+
+ test_deprecated_setter() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+void set foo(int _) {}
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+
+/// Does not prevent the hint.
+int get foo => 0;
+''');
+
+ await assertErrorsInCode('''
+import 'b.dart';
+
+void f() {
+ foo = 0;
+}
+''', [
+ error(HintCode.DEPRECATED_EXPORT_USE, 31, 3),
+ ]);
+ }
+
+ test_deprecated_setter_prefixed() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+void set foo(int _) {}
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+
+/// Does not prevent the hint.
+int get foo => 0;
+''');
+
+ await assertErrorsInCode('''
+import 'b.dart' as prefix;
+
+void f() {
+ prefix.foo = 0;
+}
+''', [
+ error(HintCode.DEPRECATED_EXPORT_USE, 48, 3),
+ ]);
+ }
+
+ test_deprecated_variable() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+var foo = 0;
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+''');
+
+ await assertErrorsInCode('''
+import 'b.dart';
+
+void f() {
+ foo;
+}
+''', [
+ error(HintCode.DEPRECATED_EXPORT_USE, 31, 3),
+ ]);
+ }
+
+ test_notDeprecated_class_hasDirectImport() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+class A {}
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+''');
+
+ await assertNoErrorsInCode('''
+import 'a.dart';
+import 'b.dart';
+
+void f(A a) {}
+''');
+ }
+
+ test_notDeprecated_class_hasNotDeprecatedExport() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+class A {}
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+
+// Not deprecated.
+export 'a.dart';
+''');
+
+ await assertNoErrorsInCode('''
+import 'b.dart';
+
+void f(A a) {}
+''');
+ }
+
+ test_notDeprecated_class_import_hide() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+class A {}
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+
+class B {}
+''');
+
+ await assertNoErrorsInCode('''
+import 'a.dart';
+import 'b.dart' hide A;
+
+void f(A a, B b) {}
+''');
+ }
+
+ test_notDeprecated_class_onlyNotDeprecatedExport() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+class A {}
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+export 'a.dart';
+''');
+
+ await assertNoErrorsInCode('''
+import 'b.dart';
+
+void f(A a) {}
+''');
+ }
+
+ test_notDeprecated_getter_useSetter() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+int get foo => 0;
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+
+set foo(int _) {}
+''');
+
+ await assertNoErrorsInCode('''
+import 'b.dart';
+
+void f() {
+ foo = 0;
+}
+''');
+ }
+
+ test_notDeprecated_getter_useSetter_prefixed() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+int get foo => 0;
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+
+set foo(int _) {}
+''');
+
+ await assertNoErrorsInCode('''
+import 'b.dart' as prefix;
+
+void f() {
+ prefix.foo = 0;
+}
+''');
+ }
+
+ test_notDeprecated_setter_useGetter() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+set foo(int _) {}
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+
+int get foo => 0;
+''');
+
+ await assertNoErrorsInCode('''
+import 'b.dart';
+
+void f() {
+ foo;
+}
+''');
+ }
+
+ test_notDeprecated_setter_useGetter_prefixed() async {
+ newFile('$testPackageLibPath/a.dart', r'''
+set foo(int _) {}
+''');
+
+ newFile('$testPackageLibPath/b.dart', r'''
+library b;
+
+@deprecated
+export 'a.dart';
+
+int get foo => 0;
+''');
+
+ await assertNoErrorsInCode('''
+import 'b.dart' as prefix;
+
+void f() {
+ prefix.foo;
+}
+''');
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 2e99aee..3f25d0f 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -137,6 +137,7 @@
import 'deferred_import_of_extension_test.dart' as deferred_import_of_extension;
import 'definitely_unassigned_late_local_variable_test.dart'
as definitely_unassigned_late_local_variable;
+import 'deprecated_export_use_test.dart' as deprecated_export_use;
import 'deprecated_extends_function_test.dart' as deprecated_extends_function;
import 'deprecated_function_class_declaration_test.dart'
as deprecated_function_class_declaration;
@@ -888,6 +889,7 @@
default_value_on_required_parameter.main();
deferred_import_of_extension.main();
definitely_unassigned_late_local_variable.main();
+ deprecated_export_use.main();
deprecated_extends_function.main();
deprecated_function_class_declaration.main();
deprecated_implements_function.main();