Analyzer warnings: add support for @Deprecation.extend and .implement
Work towards https://github.com/dart-lang/sdk/issues/60504
For each annotation, we implement several features in the analyzer:
* The annotation causes certain usage to generate a warning (+ tests).
* Possible fixes for this new warning are offered (+ tests).
* Placing the annotation on an invalid element generates a different
warning (+ tests).
* Possible fixes for the invalid annotation are offered (+ tests).
Change-Id: I55f8663a4c0589bc8409dda879f28b072eca6197
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/442705
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@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 8a12b05..1f5b2f2 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
@@ -3462,10 +3462,14 @@
status: hasFix
notes: |-
A data driven fix using `replacedBy`.
+WarningCode.DEPRECATED_EXTEND:
+ status: hasFix
WarningCode.DEPRECATED_EXTENDS_FUNCTION:
status: needsFix
notes: |-
- The fix is to remove `Function` from where it's referenced.
+ The fix is to remove the class from the implements list.
+WarningCode.DEPRECATED_IMPLEMENT:
+ status: hasFix
WarningCode.DEPRECATED_IMPLEMENTS_FUNCTION:
status: hasFix
WarningCode.DEPRECATED_MIXIN_FUNCTION:
@@ -3551,6 +3555,12 @@
status: hasFix
WarningCode.INVALID_AWAIT_NOT_REQUIRED_ANNOTATION:
status: needsFix
+WarningCode.INVALID_DEPRECATED_EXTEND_ANNOTATION:
+ status: needsFix
+ notes: The fix is to remove the annotation.
+WarningCode.INVALID_DEPRECATED_IMPLEMENT_ANNOTATION:
+ status: needsFix
+ notes: The fix is to remove the annotation.
WarningCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT:
status: needsFix
notes: |-
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index b571416..fc4d65d 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -1197,6 +1197,8 @@
// a place where it can be reached (when possible).
RemoveDeadCode.new,
],
+ WarningCode.DEPRECATED_EXTEND: [RemoveExtendsClause.new],
+ WarningCode.DEPRECATED_IMPLEMENT: [RemoveNameFromDeclarationClause.new],
WarningCode.DEPRECATED_IMPLEMENTS_FUNCTION: [
RemoveNameFromDeclarationClause.new,
],
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_extends_clause_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_extends_clause_test.dart
index 2810262..1a87b3a 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_extends_clause_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_extends_clause_test.dart
@@ -43,6 +43,34 @@
@override
FixKind get kind => DartFixKind.REMOVE_EXTENDS_CLAUSE;
+ Future<void> test_deprecatedExtends() async {
+ newFile('$testPackageLibPath/a.dart', '''
+@Deprecated.extend()
+class A {}
+''');
+ await resolveTestCode('''
+import 'a.dart';
+class B extends A {}
+''');
+ await assertHasFix('''
+import 'a.dart';
+class B {}
+''');
+ }
+
+ Future<void> test_deprecatedExtends_classTypeAlias() async {
+ newFile('$testPackageLibPath/a.dart', '''
+@Deprecated.extend()
+class A {}
+''');
+ await resolveTestCode('''
+import 'a.dart';
+mixin M {}
+class B = A with M;
+''');
+ await assertNoFix();
+ }
+
Future<void> test_mixinClass_extends_class() async {
await resolveTestCode('''
class A {}
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_name_from_declaration_clause_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_name_from_declaration_clause_test.dart
index f299d72..4c1dba5 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_name_from_declaration_clause_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_name_from_declaration_clause_test.dart
@@ -15,6 +15,7 @@
defineReflectiveTests(ExtendsDisallowedClassTest);
defineReflectiveTests(ExtendsNonClassTest);
defineReflectiveTests(ExtendsTypeAliasExpandsToTypeParameterTest);
+ defineReflectiveTests(ImplementsDeprecatedImplementTest);
defineReflectiveTests(ImplementsDisallowedClassTest);
defineReflectiveTests(ImplementsRepeatedTest);
defineReflectiveTests(ImplementsSuperClassTest);
@@ -93,6 +94,27 @@
}
@reflectiveTest
+class ImplementsDeprecatedImplementTest extends FixProcessorTest {
+ @override
+ FixKind get kind => DartFixKind.REMOVE_NAME_FROM_DECLARATION_CLAUSE;
+
+ Future<void> test_deprecatedExtends() async {
+ newFile('$testPackageLibPath/a.dart', '''
+@Deprecated.implement()
+class A {}
+''');
+ await resolveTestCode('''
+import 'a.dart';
+class B implements A {}
+''');
+ await assertHasFix('''
+import 'a.dart';
+class B {}
+''');
+ }
+}
+
+@reflectiveTest
class ImplementsDisallowedClassTest extends FixProcessorTest {
@override
FixKind get kind => DartFixKind.REMOVE_NAME_FROM_DECLARATION_CLAUSE;
diff --git a/pkg/analyzer/lib/src/dart/element/extensions.dart b/pkg/analyzer/lib/src/dart/element/extensions.dart
index 7b9f516..dad9779 100644
--- a/pkg/analyzer/lib/src/dart/element/extensions.dart
+++ b/pkg/analyzer/lib/src/dart/element/extensions.dart
@@ -74,6 +74,22 @@
return false;
}
+ /// Whether the use of this element is deprecated.
+ bool get isUseDeprecated {
+ var element = this;
+
+ var metadata =
+ (element is PropertyAccessorElement && element.isSynthetic)
+ ? element.variable.metadata
+ : element.metadata;
+
+ var annotations = metadata.annotations.where((e) => e.isDeprecated);
+ return annotations.any((annotation) {
+ var value = annotation.computeConstantValue();
+ return value?.getField('_isUse')?.toBoolValue() ?? true;
+ });
+ }
+
/// Whether this element is a wildcard variable.
bool get isWildcardVariable {
return name == '_' &&
diff --git a/pkg/analyzer/lib/src/diagnostic/diagnostic_code_values.g.dart b/pkg/analyzer/lib/src/diagnostic/diagnostic_code_values.g.dart
index eafef2a..9147cac 100644
--- a/pkg/analyzer/lib/src/diagnostic/diagnostic_code_values.g.dart
+++ b/pkg/analyzer/lib/src/diagnostic/diagnostic_code_values.g.dart
@@ -988,7 +988,9 @@
WarningCode.DEAD_CODE_LATE_WILDCARD_VARIABLE_INITIALIZER,
WarningCode.DEAD_CODE_ON_CATCH_SUBTYPE,
WarningCode.DEPRECATED_EXPORT_USE,
+ WarningCode.DEPRECATED_EXTEND,
WarningCode.DEPRECATED_EXTENDS_FUNCTION,
+ WarningCode.DEPRECATED_IMPLEMENT,
WarningCode.DEPRECATED_IMPLEMENTS_FUNCTION,
WarningCode.DEPRECATED_MIXIN_FUNCTION,
WarningCode.DEPRECATED_NEW_IN_COMMENT_REFERENCE,
@@ -1022,6 +1024,8 @@
WarningCode.INFERENCE_FAILURE_ON_UNTYPED_PARAMETER,
WarningCode.INVALID_ANNOTATION_TARGET,
WarningCode.INVALID_AWAIT_NOT_REQUIRED_ANNOTATION,
+ WarningCode.INVALID_DEPRECATED_EXTEND_ANNOTATION,
+ WarningCode.INVALID_DEPRECATED_IMPLEMENT_ANNOTATION,
WarningCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT,
WarningCode.INVALID_EXPORT_OF_INTERNAL_ELEMENT_INDIRECTLY,
WarningCode.INVALID_FACTORY_METHOD_DECL,
diff --git a/pkg/analyzer/lib/src/error/annotation_verifier.dart b/pkg/analyzer/lib/src/error/annotation_verifier.dart
index 9803161..4c49cd8 100644
--- a/pkg/analyzer/lib/src/error/annotation_verifier.dart
+++ b/pkg/analyzer/lib/src/error/annotation_verifier.dart
@@ -45,6 +45,8 @@
var parent = node.parent;
if (element.isAwaitNotRequired) {
_checkAwaitNotRequired(node);
+ } else if (element.isDeprecated) {
+ _checkDeprecated(node);
} else if (element.isFactory) {
_checkFactory(node);
} else if (element.isInternal) {
@@ -113,6 +115,88 @@
}
}
+ void _checkDeprecated(Annotation node) {
+ var element = node.elementAnnotation;
+ var value = element?.computeConstantValue();
+ if (value == null) return;
+
+ // The vast majority of deprecated annotations use the default constructor.
+ // Check this case first.
+ if (value.getField('_isUse')?.toBoolValue() ?? true) return;
+
+ var isExtend = value.getField('_isExtend')?.toBoolValue() ?? false;
+ if (isExtend) {
+ _checkDeprecatedExtend(node, node.parent);
+ return;
+ }
+
+ var isImplement = value.getField('_isImplement')?.toBoolValue() ?? false;
+ if (isImplement) {
+ _checkDeprecatedImplement(node, node.parent);
+ return;
+ }
+ }
+
+ void _checkDeprecatedExtend(Annotation node, AstNode parent) {
+ if (parent
+ case ClassDeclaration(:var declaredFragment) ||
+ ClassTypeAlias(:var declaredFragment)) {
+ var classElement = declaredFragment?.element;
+ if (classElement == null) return;
+ var hasGenerativeConstructor = classElement.constructors.any(
+ (c) => c.isPublic && c.isGenerative,
+ );
+ if (classElement.isExtendableOutside && hasGenerativeConstructor) return;
+ }
+
+ if (parent is GenericTypeAlias) {
+ var classElement = parent.type.type?.element;
+ if (classElement is ClassElement) {
+ var hasGenerativeConstructor = classElement.constructors.any(
+ (c) => c.isPublic && c.isGenerative,
+ );
+ if (classElement.isExtendableOutside && hasGenerativeConstructor) {
+ return;
+ }
+ }
+ }
+
+ _diagnosticReporter.atNode(
+ node.name,
+ WarningCode.INVALID_DEPRECATED_EXTEND_ANNOTATION,
+ );
+ }
+
+ void _checkDeprecatedImplement(Annotation node, AstNode parent) {
+ if (parent
+ case ClassDeclaration(:var declaredFragment) ||
+ ClassTypeAlias(:var declaredFragment)) {
+ var classElement = declaredFragment?.element;
+ if (classElement == null) return;
+ if (classElement.isImplementableOutside) return;
+ }
+
+ if (parent is MixinDeclaration) {
+ var mixinElement = parent.declaredFragment?.element;
+ if (mixinElement == null) return;
+ if (mixinElement.isImplementableOutside) return;
+ }
+
+ if (parent is GenericTypeAlias) {
+ var element = parent.type.type?.element;
+ if (element is ClassElement && element.isImplementableOutside) {
+ return;
+ } else if (element is MixinElement && element.isImplementableOutside) {
+ return;
+ }
+ }
+
+ _diagnosticReporter.atNode(
+ node.name,
+ WarningCode.INVALID_DEPRECATED_IMPLEMENT_ANNOTATION,
+ );
+ }
+
/// Reports a warning at [node] if its parent is not a valid target for a
/// `@factory` annotation.
void _checkFactory(Annotation node) {
diff --git a/pkg/analyzer/lib/src/error/best_practices_verifier.dart b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
index 728c455..ba46426 100644
--- a/pkg/analyzer/lib/src/error/best_practices_verifier.dart
+++ b/pkg/analyzer/lib/src/error/best_practices_verifier.dart
@@ -28,6 +28,7 @@
import 'package:analyzer/src/dart/resolver/scope.dart';
import 'package:analyzer/src/error/annotation_verifier.dart';
import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/error/deprecated_functionality_verifier.dart';
import 'package:analyzer/src/error/deprecated_member_use_verifier.dart';
import 'package:analyzer/src/error/doc_comment_verifier.dart';
import 'package:analyzer/src/error/error_handler_verifier.dart';
@@ -64,7 +65,9 @@
final AnnotationVerifier _annotationVerifier;
- final DeprecatedMemberUseVerifier _deprecatedVerifier;
+ final DeprecatedFunctionalityVerifier _deprecatedFunctionalityVerifier;
+
+ final DeprecatedMemberUseVerifier _deprecatedMemberUseVerifier;
final ErrorHandlerVerifier _errorHandlerVerifier;
@@ -107,10 +110,13 @@
_currentLibrary,
workspacePackage,
),
- _deprecatedVerifier = DeprecatedMemberUseVerifier(
+ _deprecatedFunctionalityVerifier = DeprecatedFunctionalityVerifier(
+ _diagnosticReporter,
+ _currentLibrary,
+ ),
+ _deprecatedMemberUseVerifier = DeprecatedMemberUseVerifier(
workspacePackage,
_diagnosticReporter,
- strictCasts: analysisOptions.strictCasts,
),
_errorHandlerVerifier = ErrorHandlerVerifier(
_diagnosticReporter,
@@ -131,8 +137,8 @@
),
_widgetPreviewVerifier = WidgetPreviewVerifier(_diagnosticReporter),
_workspacePackage = workspacePackage {
- _deprecatedVerifier.pushInDeprecatedValue(
- _currentLibrary.metadata.hasDeprecated,
+ _deprecatedMemberUseVerifier.pushInDeprecatedValue(
+ _currentLibrary.isUseDeprecated,
);
_inDoNotStoreMember = _currentLibrary.metadata.hasDoNotStore;
}
@@ -166,13 +172,13 @@
@override
void visitAssignmentExpression(AssignmentExpression node) {
- _deprecatedVerifier.assignmentExpression(node);
+ _deprecatedMemberUseVerifier.assignmentExpression(node);
super.visitAssignmentExpression(node);
}
@override
void visitBinaryExpression(BinaryExpression node) {
- _deprecatedVerifier.binaryExpression(node);
+ _deprecatedMemberUseVerifier.binaryExpression(node);
_checkForInvariantNanComparison(node);
_checkForInvariantNullComparison(node);
_invalidAccessVerifier.verifyBinary(node);
@@ -206,7 +212,8 @@
_invalidAccessVerifier._enclosingClass = element;
bool wasInDoNotStoreMember = _inDoNotStoreMember;
- _deprecatedVerifier.pushInDeprecatedValue(element.metadata.hasDeprecated);
+ _deprecatedFunctionalityVerifier.classDeclaration(node);
+ _deprecatedMemberUseVerifier.pushInDeprecatedValue(element.isUseDeprecated);
if (element.metadata.hasDoNotStore) {
_inDoNotStoreMember = true;
}
@@ -218,7 +225,7 @@
} finally {
_enclosingClass = null;
_invalidAccessVerifier._enclosingClass = null;
- _deprecatedVerifier.popInDeprecated();
+ _deprecatedMemberUseVerifier.popInDeprecated();
_inDoNotStoreMember = wasInDoNotStoreMember;
}
}
@@ -227,14 +234,15 @@
void visitClassTypeAlias(ClassTypeAlias node) {
_checkForImmutable(node);
_checkForInvalidSealedSuperclass(node);
- _deprecatedVerifier.pushInDeprecatedValue(
- node.declaredFragment!.element.metadata.hasDeprecated,
+ _deprecatedFunctionalityVerifier.classTypeAlias(node);
+ _deprecatedMemberUseVerifier.pushInDeprecatedValue(
+ node.declaredFragment!.element.isUseDeprecated,
);
try {
super.visitClassTypeAlias(node);
} finally {
- _deprecatedVerifier.popInDeprecated();
+ _deprecatedMemberUseVerifier.popInDeprecated();
}
}
@@ -281,17 +289,17 @@
body: node.body,
initializers: node.initializers,
);
- _deprecatedVerifier.pushInDeprecatedValue(element.metadata.hasDeprecated);
+ _deprecatedMemberUseVerifier.pushInDeprecatedValue(element.isUseDeprecated);
try {
super.visitConstructorDeclaration(node);
} finally {
- _deprecatedVerifier.popInDeprecated();
+ _deprecatedMemberUseVerifier.popInDeprecated();
}
}
@override
void visitConstructorName(ConstructorName node) {
- _deprecatedVerifier.constructorName(node);
+ _deprecatedMemberUseVerifier.constructorName(node);
super.visitConstructorName(node);
}
@@ -315,14 +323,14 @@
);
}
}
- _deprecatedVerifier.pushInDeprecatedValue(
- node.declaredFragment!.element.metadata.hasDeprecated,
+ _deprecatedMemberUseVerifier.pushInDeprecatedValue(
+ node.declaredFragment!.element.isUseDeprecated,
);
try {
super.visitDefaultFormalParameter(node);
} finally {
- _deprecatedVerifier.popInDeprecated();
+ _deprecatedMemberUseVerifier.popInDeprecated();
}
}
@@ -330,27 +338,28 @@
void visitDotShorthandConstructorInvocation(
DotShorthandConstructorInvocation node,
) {
- _deprecatedVerifier.dotShorthandConstructorInvocation(node);
+ _deprecatedMemberUseVerifier.dotShorthandConstructorInvocation(node);
_checkForLiteralConstructorUseInDotShorthand(node);
super.visitDotShorthandConstructorInvocation(node);
}
@override
void visitEnumDeclaration(EnumDeclaration node) {
- _deprecatedVerifier.pushInDeprecatedValue(
- node.declaredFragment!.element.metadata.hasDeprecated,
+ _deprecatedMemberUseVerifier.pushInDeprecatedValue(
+ node.declaredFragment!.element.isUseDeprecated,
);
+ _deprecatedFunctionalityVerifier.enumDeclaration(node);
try {
super.visitEnumDeclaration(node);
} finally {
- _deprecatedVerifier.popInDeprecated();
+ _deprecatedMemberUseVerifier.popInDeprecated();
}
}
@override
void visitExportDirective(covariant ExportDirectiveImpl node) {
- _deprecatedVerifier.exportDirective(node);
+ _deprecatedMemberUseVerifier.exportDirective(node);
_checkForInternalExport(node);
super.visitExportDirective(node);
}
@@ -365,39 +374,39 @@
@override
void visitExtensionDeclaration(ExtensionDeclaration node) {
- _deprecatedVerifier.pushInDeprecatedValue(
- node.declaredFragment!.element.metadata.hasDeprecated,
+ _deprecatedMemberUseVerifier.pushInDeprecatedValue(
+ node.declaredFragment!.element.isUseDeprecated,
);
try {
super.visitExtensionDeclaration(node);
} finally {
- _deprecatedVerifier.popInDeprecated();
+ _deprecatedMemberUseVerifier.popInDeprecated();
}
}
@override
void visitExtensionOverride(ExtensionOverride node) {
- _deprecatedVerifier.extensionOverride(node);
+ _deprecatedMemberUseVerifier.extensionOverride(node);
super.visitExtensionOverride(node);
}
@override
void visitExtensionTypeDeclaration(ExtensionTypeDeclaration node) {
- _deprecatedVerifier.pushInDeprecatedValue(
- node.declaredFragment!.element.metadata.hasDeprecated,
+ _deprecatedMemberUseVerifier.pushInDeprecatedValue(
+ node.declaredFragment!.element.isUseDeprecated,
);
try {
super.visitExtensionTypeDeclaration(node);
} finally {
- _deprecatedVerifier.popInDeprecated();
+ _deprecatedMemberUseVerifier.popInDeprecated();
}
}
@override
void visitFieldDeclaration(FieldDeclaration node) {
- _deprecatedVerifier.pushInDeprecatedMetadata(node.metadata);
+ _deprecatedMemberUseVerifier.pushInDeprecatedMetadata(node.metadata);
try {
super.visitFieldDeclaration(node);
@@ -438,7 +447,7 @@
}
}
} finally {
- _deprecatedVerifier.popInDeprecated();
+ _deprecatedMemberUseVerifier.popInDeprecated();
}
}
@@ -458,7 +467,7 @@
void visitFunctionDeclaration(FunctionDeclaration node) {
bool wasInDoNotStoreMember = _inDoNotStoreMember;
var element = node.declaredFragment!.element;
- _deprecatedVerifier.pushInDeprecatedValue(element.metadata.hasDeprecated);
+ _deprecatedMemberUseVerifier.pushInDeprecatedValue(element.isUseDeprecated);
if (element.metadata.hasDoNotStore) {
_inDoNotStoreMember = true;
}
@@ -477,7 +486,7 @@
);
super.visitFunctionDeclaration(node);
} finally {
- _deprecatedVerifier.popInDeprecated();
+ _deprecatedMemberUseVerifier.popInDeprecated();
_inDoNotStoreMember = wasInDoNotStoreMember;
}
}
@@ -501,7 +510,7 @@
@override
void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
- _deprecatedVerifier.functionExpressionInvocation(node);
+ _deprecatedMemberUseVerifier.functionExpressionInvocation(node);
super.visitFunctionExpressionInvocation(node);
}
@@ -509,14 +518,14 @@
void visitFunctionTypeAlias(FunctionTypeAlias node) {
_checkStrictInferenceReturnType(node.returnType, node, node.name.lexeme);
_checkStrictInferenceInParameters(node.parameters);
- _deprecatedVerifier.pushInDeprecatedValue(
- node.declaredFragment!.element.metadata.hasDeprecated,
+ _deprecatedMemberUseVerifier.pushInDeprecatedValue(
+ node.declaredFragment!.element.isUseDeprecated,
);
try {
super.visitFunctionTypeAlias(node);
} finally {
- _deprecatedVerifier.popInDeprecated();
+ _deprecatedMemberUseVerifier.popInDeprecated();
}
}
@@ -546,20 +555,20 @@
node.name.lexeme,
);
}
- _deprecatedVerifier.pushInDeprecatedValue(
- node.declaredFragment!.element.metadata.hasDeprecated,
+ _deprecatedMemberUseVerifier.pushInDeprecatedValue(
+ node.declaredFragment!.element.isUseDeprecated,
);
try {
super.visitGenericTypeAlias(node);
} finally {
- _deprecatedVerifier.popInDeprecated();
+ _deprecatedMemberUseVerifier.popInDeprecated();
}
}
@override
void visitImportDirective(ImportDirective node) {
- _deprecatedVerifier.importDirective(node);
+ _deprecatedMemberUseVerifier.importDirective(node);
var import = node.libraryImport;
if (import != null && import.prefix?.isDeferred == true) {
_checkForLoadLibraryFunction(node, import);
@@ -570,7 +579,7 @@
@override
void visitIndexExpression(IndexExpression node) {
- _deprecatedVerifier.indexExpression(node);
+ _deprecatedMemberUseVerifier.indexExpression(node);
super.visitIndexExpression(node);
}
@@ -578,7 +587,7 @@
void visitInstanceCreationExpression(
covariant InstanceCreationExpressionImpl node,
) {
- _deprecatedVerifier.instanceCreationExpression(node);
+ _deprecatedMemberUseVerifier.instanceCreationExpression(node);
_nullSafeApiVerifier.instanceCreation(node);
_checkForLiteralConstructorUse(node);
super.visitInstanceCreationExpression(node);
@@ -596,7 +605,7 @@
var element = node.declaredFragment!.element;
var enclosingElement = element.enclosingElement;
- _deprecatedVerifier.pushInDeprecatedValue(element.metadata.hasDeprecated);
+ _deprecatedMemberUseVerifier.pushInDeprecatedValue(element.isUseDeprecated);
if (element.metadata.hasDoNotStore) {
_inDoNotStoreMember = true;
}
@@ -650,14 +659,14 @@
super.visitMethodDeclaration(node);
} finally {
- _deprecatedVerifier.popInDeprecated();
+ _deprecatedMemberUseVerifier.popInDeprecated();
_inDoNotStoreMember = wasInDoNotStoreMember;
}
}
@override
void visitMethodInvocation(covariant MethodInvocationImpl node) {
- _deprecatedVerifier.methodInvocation(node);
+ _deprecatedMemberUseVerifier.methodInvocation(node);
_errorHandlerVerifier.verifyMethodInvocation(node);
_nullSafeApiVerifier.methodInvocation(node);
super.visitMethodInvocation(node);
@@ -670,7 +679,7 @@
_enclosingClass = element;
_invalidAccessVerifier._enclosingClass = _enclosingClass;
- _deprecatedVerifier.pushInDeprecatedValue(element.metadata.hasDeprecated);
+ _deprecatedMemberUseVerifier.pushInDeprecatedValue(element.isUseDeprecated);
try {
_checkForImmutable(node);
@@ -679,13 +688,13 @@
} finally {
_enclosingClass = null;
_invalidAccessVerifier._enclosingClass = null;
- _deprecatedVerifier.popInDeprecated();
+ _deprecatedMemberUseVerifier.popInDeprecated();
}
}
@override
void visitNamedType(NamedType node) {
- _deprecatedVerifier.namedType(node);
+ _deprecatedMemberUseVerifier.namedType(node);
_invalidAccessVerifier.verifyNamedType(node);
var question = node.question;
if (question != null) {
@@ -707,14 +716,14 @@
@override
void visitPatternField(PatternField node) {
- _deprecatedVerifier.patternField(node);
+ _deprecatedMemberUseVerifier.patternField(node);
_invalidAccessVerifier.verifyPatternField(node as PatternFieldImpl);
super.visitPatternField(node);
}
@override
void visitPostfixExpression(PostfixExpression node) {
- _deprecatedVerifier.postfixExpression(node);
+ _deprecatedMemberUseVerifier.postfixExpression(node);
if (node.operator.type == TokenType.BANG &&
node.operand.typeOrThrow.isDartCoreNull) {
_diagnosticReporter.atNode(node, WarningCode.NULL_CHECK_ALWAYS_FAILS);
@@ -724,7 +733,7 @@
@override
void visitPrefixExpression(PrefixExpression node) {
- _deprecatedVerifier.prefixExpression(node);
+ _deprecatedMemberUseVerifier.prefixExpression(node);
super.visitPrefixExpression(node);
}
@@ -732,7 +741,7 @@
void visitRedirectingConstructorInvocation(
RedirectingConstructorInvocation node,
) {
- _deprecatedVerifier.redirectingConstructorInvocation(node);
+ _deprecatedMemberUseVerifier.redirectingConstructorInvocation(node);
super.visitRedirectingConstructorInvocation(node);
}
@@ -752,14 +761,14 @@
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
- _deprecatedVerifier.simpleIdentifier(node);
+ _deprecatedMemberUseVerifier.simpleIdentifier(node);
_invalidAccessVerifier.verify(node);
super.visitSimpleIdentifier(node);
}
@override
void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
- _deprecatedVerifier.superConstructorInvocation(node);
+ _deprecatedMemberUseVerifier.superConstructorInvocation(node);
_invalidAccessVerifier.verifySuperConstructorInvocation(node);
super.visitSuperConstructorInvocation(node);
}
@@ -772,7 +781,7 @@
@override
void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
- _deprecatedVerifier.pushInDeprecatedMetadata(node.metadata);
+ _deprecatedMemberUseVerifier.pushInDeprecatedMetadata(node.metadata);
if (!_invalidAccessVerifier._inTestDirectory) {
for (var decl in node.variables.variables) {
@@ -783,7 +792,7 @@
try {
super.visitTopLevelVariableDeclaration(node);
} finally {
- _deprecatedVerifier.popInDeprecated();
+ _deprecatedMemberUseVerifier.popInDeprecated();
}
}
diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart
index 0da04bb..25da688 100644
--- a/pkg/analyzer/lib/src/error/codes.g.dart
+++ b/pkg/analyzer/lib/src/error/codes.g.dart
@@ -6372,6 +6372,14 @@
hasPublishedDocs: true,
);
+ /// Parameters:
+ /// 0: the name of the member
+ static const WarningCode DEPRECATED_EXTEND = WarningCode(
+ 'DEPRECATED_EXTEND',
+ "Extending '{0}' is deprecated.",
+ correctionMessage: "Try removing the 'extends' clause.",
+ );
+
/// No parameters.
static const WarningCode DEPRECATED_EXTENDS_FUNCTION = WarningCode(
'DEPRECATED_SUBTYPE_OF_FUNCTION',
@@ -6381,6 +6389,14 @@
uniqueName: 'DEPRECATED_EXTENDS_FUNCTION',
);
+ /// Parameters:
+ /// 0: the name of the member
+ static const WarningCode DEPRECATED_IMPLEMENT = WarningCode(
+ 'DEPRECATED_IMPLEMENT',
+ "Implementing '{0}' is deprecated.",
+ correctionMessage: "Try removing '{0}' from the 'implements' clause.",
+ );
+
/// No parameters.
static const WarningCode DEPRECATED_IMPLEMENTS_FUNCTION = WarningCode(
'DEPRECATED_SUBTYPE_OF_FUNCTION',
@@ -6700,6 +6716,24 @@
"Future-returning function, or a Future-typed field.",
);
+ /// No parameters.
+ static const WarningCode INVALID_DEPRECATED_EXTEND_ANNOTATION = WarningCode(
+ 'INVALID_DEPRECATED_EXTEND_ANNOTATION',
+ "The annotation '@Deprecated.extend' can only be applied to extendable "
+ "classes.",
+ correctionMessage: "Try removing the '@Deprecated.extend' annotation.",
+ );
+
+ /// No parameters.
+ static const WarningCode INVALID_DEPRECATED_IMPLEMENT_ANNOTATION =
+ WarningCode(
+ 'INVALID_DEPRECATED_IMPLEMENT_ANNOTATION',
+ "The annotation '@Deprecated.implement' can only be applied to "
+ "implementable classes.",
+ correctionMessage:
+ "Try removing the '@Deprecated.implement' annotation.",
+ );
+
/// Parameters:
/// 0: the name of the element
static const WarningCode INVALID_EXPORT_OF_INTERNAL_ELEMENT = WarningCode(
diff --git a/pkg/analyzer/lib/src/error/deprecated_functionality_verifier.dart b/pkg/analyzer/lib/src/error/deprecated_functionality_verifier.dart
new file mode 100644
index 0000000..58aa4e5
--- /dev/null
+++ b/pkg/analyzer/lib/src/error/deprecated_functionality_verifier.dart
@@ -0,0 +1,79 @@
+// Copyright (c) 2025, 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/element/element.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/error/codes.dart';
+
+class DeprecatedFunctionalityVerifier {
+ final DiagnosticReporter _diagnosticReporter;
+
+ final LibraryElement _currentLibrary;
+
+ DeprecatedFunctionalityVerifier(
+ this._diagnosticReporter,
+ this._currentLibrary,
+ );
+
+ void classDeclaration(ClassDeclaration node) {
+ _checkForDeprecatedExtend(node.extendsClause?.superclass);
+ _checkForDeprecatedImplement(node.implementsClause);
+ }
+
+ void classTypeAlias(ClassTypeAlias node) {
+ _checkForDeprecatedExtend(node.superclass);
+ _checkForDeprecatedImplement(node.implementsClause);
+ }
+
+ void enumDeclaration(EnumDeclaration node) {
+ _checkForDeprecatedImplement(node.implementsClause);
+ }
+
+ void _checkForDeprecatedExtend(NamedType? node) {
+ if (node == null) return;
+ var element = node.element;
+ if (element == null) return;
+ if (node.type?.element is InterfaceElement) {
+ if (element.library == _currentLibrary) return;
+ if (element.hasDeprecatedWithField('_isExtend')) {
+ _diagnosticReporter.atNode(
+ node,
+ WarningCode.DEPRECATED_EXTEND,
+ arguments: [element.name!],
+ );
+ }
+ }
+ }
+
+ void _checkForDeprecatedImplement(ImplementsClause? node) {
+ if (node == null) return;
+ var interfaces = node.interfaces;
+ for (var interface in interfaces) {
+ var element = interface.element;
+ if (element == null) continue;
+ if (interface.type?.element is InterfaceElement) {
+ if (element.library == _currentLibrary) continue;
+ if (element.hasDeprecatedWithField('_isImplement')) {
+ _diagnosticReporter.atNode(
+ interface,
+ WarningCode.DEPRECATED_IMPLEMENT,
+ arguments: [element.name!],
+ );
+ }
+ }
+ }
+ }
+}
+
+extension on Element {
+ /// Whether this Element is annotated with a `Deprecated` annotation with a
+ /// `true` value for [fieldName].
+ bool hasDeprecatedWithField(String fieldName) {
+ return metadata.annotations.where((e) => e.isDeprecated).any((annotation) {
+ var value = annotation.computeConstantValue();
+ return value?.getField(fieldName)?.toBoolValue() ?? false;
+ });
+ }
+}
diff --git a/pkg/analyzer/lib/src/error/deprecated_member_use_verifier.dart b/pkg/analyzer/lib/src/error/deprecated_member_use_verifier.dart
index 351ecc7e..3876ec7 100644
--- a/pkg/analyzer/lib/src/error/deprecated_member_use_verifier.dart
+++ b/pkg/analyzer/lib/src/error/deprecated_member_use_verifier.dart
@@ -7,6 +7,7 @@
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/element/extensions.dart';
import 'package:analyzer/src/dart/error/hint_codes.dart';
import 'package:analyzer/src/workspace/workspace.dart';
import 'package:collection/collection.dart';
@@ -16,10 +17,7 @@
/// can be marked as deprecated - a class, a method, fields (multiple).
final List<bool> _inDeprecatedMemberStack = [false];
- final bool _strictCasts;
-
- BaseDeprecatedMemberUseVerifier({bool strictCasts = false})
- : _strictCasts = strictCasts;
+ BaseDeprecatedMemberUseVerifier();
void assignmentExpression(AssignmentExpression node) {
_checkForDeprecated(node.readElement, node.leftHandSide);
@@ -98,7 +96,7 @@
}
void pushInDeprecatedMetadata(List<Annotation> metadata) {
- var hasDeprecated = _hasDeprecatedAnnotation(metadata);
+ var hasDeprecated = _hasDeprecatedUseAnnotation(metadata);
pushInDeprecatedValue(hasDeprecated);
}
@@ -161,11 +159,9 @@
_invocationArguments(node.element, node.argumentList);
}
- /// Given some [element], look at the associated metadata and report the use
- /// of the member if it is declared as deprecated. If a diagnostic is reported
- /// it should be reported at the given [node].
+ /// Reports the use of [element] at [node] if its use is deprecated.
void _checkForDeprecated(Element? element, AstNode node) {
- if (!_isDeprecated(element)) {
+ if (element == null || !element.isUseDeprecated) {
return;
}
@@ -212,7 +208,7 @@
}
}
- String displayName = element!.displayName;
+ String displayName = element.displayName;
if (element is ConstructorElement) {
// TODO(jwren): We should modify ConstructorElement.displayName,
// or have the logic centralized elsewhere, instead of doing this logic
@@ -227,9 +223,9 @@
displayName == MethodElement.CALL_METHOD_NAME) {
var invokeType = node.staticInvokeType as InterfaceType;
var invokeClass = invokeType.element;
- displayName = "${invokeClass.name}.${element.displayName}";
+ displayName = '${invokeClass.name}.${element.displayName}';
}
- var message = _deprecatedMessage(element, strictCasts: _strictCasts);
+ var message = _deprecatedMessage(element);
reportError(errorEntity, element, displayName, message);
}
@@ -248,13 +244,10 @@
_checkForDeprecated(identifier.element, identifier);
}
- /// Return the message in the deprecated annotation on the given [element], or
+ /// The message in the deprecated annotation on the given [element], or
/// `null` if the element doesn't have a deprecated annotation or if the
/// annotation does not have a message.
- static String? _deprecatedMessage(
- Element element, {
- required bool strictCasts,
- }) {
+ static String? _deprecatedMessage(Element element) {
// Implicit getters/setters.
if (element.isSynthetic && element is PropertyAccessorElement) {
element = element.variable;
@@ -270,24 +263,19 @@
constantValue?.getField('expires')?.toStringValue();
}
- static bool _hasDeprecatedAnnotation(List<Annotation> annotations) {
- for (var i = 0; i < annotations.length; i++) {
- if (annotations[i].elementAnnotation!.isDeprecated) {
- return true;
- }
- }
- return false;
- }
-
- static bool _isDeprecated(Element? element) {
- if (element == null) {
- return false;
- }
-
- if (element is PropertyAccessorElement && element.isSynthetic) {
- return element.variable.metadata.hasDeprecated;
- }
- return element.metadata.hasDeprecated;
+ /// Whether [annotations] includes an annotation indicating "deprecated use."
+ ///
+ /// This amounts to any annotation using `deprecated` or `Deprecated()` (the
+ /// default constructor) from `dart:core`.
+ static bool _hasDeprecatedUseAnnotation(List<Annotation> annotations) {
+ var deprecatedAnnotations = annotations
+ .map((a) => a.elementAnnotation)
+ .nonNulls
+ .where((a) => a.isDeprecated);
+ return deprecatedAnnotations.any((deprecated) {
+ var value = deprecated.computeConstantValue();
+ return value?.getField('_isUse')?.toBoolValue() ?? true;
+ });
}
/// Returns whether [element] is a [FormalParameterElement] declared in
@@ -357,11 +345,7 @@
final WorkspacePackageImpl? _workspacePackage;
final DiagnosticReporter _diagnosticReporter;
- DeprecatedMemberUseVerifier(
- this._workspacePackage,
- this._diagnosticReporter, {
- required super.strictCasts,
- });
+ DeprecatedMemberUseVerifier(this._workspacePackage, this._diagnosticReporter);
@override
void reportError(
diff --git a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
index a7a4bf9..4ce79b8 100644
--- a/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
+++ b/pkg/analyzer/lib/src/test_utilities/mock_sdk.dart
@@ -314,7 +314,21 @@
class Deprecated extends Object {
final String message;
- const Deprecated(this.message);
+ final bool _isUse;
+ final bool _isImplement;
+ final bool _isExtend;
+ const Deprecated(this.message)
+ : _isUse = true,
+ _isImplement = false,
+ _isExtend = false;
+ const Deprecated.implement([this.message = "next release"])
+ : _isUse = false,
+ _isImplement = true,
+ _isExtend = false;
+ const Deprecated.extend([this.message = "next release"])
+ : _isUse = false,
+ _isImplement = false,
+ _isExtend = true;
}
class pragma {
diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml
index 200f8a6..4b75e8f 100644
--- a/pkg/analyzer/messages.yaml
+++ b/pkg/analyzer/messages.yaml
@@ -24009,6 +24009,47 @@
If the name isn't available, then look for instructions from the library
author or contact them directly to find out how to update your code.
+ DEPRECATED_EXTEND:
+ problemMessage: "Extending '{0}' is deprecated."
+ correctionMessage: Try removing the 'extends' clause.
+ hasPublishedDocs: false
+ comment: |-
+ Parameters:
+ 0: the name of the member
+ documentation: |-
+ #### Description
+
+ The analyzer produces this diagnostic when a class which is annotated with
+ `@Deprecated.extend` is used in the `extends` clause of a class
+ declaration.
+
+ This annotation indicates that the ability for classes to extend the
+ annotated class is deprecated, and will soon be removed, perhaps by
+ marking the annotated class with `interface`, `final`, or `sealed`.
+
+ #### Example
+
+ If the library `p` defines a class annotated with `@Deprecated.extend`:
+
+ ```dart
+ %uri="package:p/p.dart"
+ @Deprecated.extend()
+ class C {}
+ ```
+
+ Then, the following code, when in a library other than `p`, produces this
+ diagnostic:
+
+ ```dart
+ import 'package:p/p.dart';
+
+ class D extends [!C!] {}
+ ```
+
+ #### Common fixes
+
+ Follow any directions found in the `Deprecation.extend` annotation, or
+ just remove the class name from the `extends` clause.
DEPRECATED_EXTENDS_FUNCTION:
sharedName: DEPRECATED_SUBTYPE_OF_FUNCTION
problemMessage: "Extending 'Function' is deprecated."
@@ -24040,6 +24081,46 @@
```dart
class F {}
```
+ DEPRECATED_IMPLEMENT:
+ problemMessage: "Implementing '{0}' is deprecated."
+ correctionMessage: Try removing '{0}' from the 'implements' clause.
+ hasPublishedDocs: false
+ comment: |-
+ Parameters:
+ 0: the name of the member
+ documentation: |-
+ #### Description
+
+ The analyzer produces this diagnostic when a class which is annotated with
+ `@Deprecated.implement` is used in the `implements` clause of a class or
+ enum declaration. This annotation indicates that the ability for classes
+ to implement the annotated class is deprecated, and will soon be removed,
+ perhaps
+ by marking the annotated class with `interface`, `final`, or `sealed`.
+
+ #### Example
+
+ If the library `p` defines a class annotated with `@Deprecated.implement`:
+
+ ```dart
+ %uri="package:p/p.dart"
+ @Deprecated.implement()
+ class C {}
+ ```
+
+ Then, the following code, when in a library other than `p`, produces this
+ diagnostic:
+
+ ```dart
+ import 'package:p/p.dart';
+
+ class D implements [!C!] {}
+ ```
+
+ #### Common fixes
+
+ Follow any directions found in the `Deprecation.implement` annotation, or
+ just remove the class name from the `implements` clause.
DEPRECATED_IMPLEMENTS_FUNCTION:
sharedName: DEPRECATED_SUBTYPE_OF_FUNCTION
problemMessage: "Implementing 'Function' has no effect."
@@ -24730,6 +24811,60 @@
```dart
void f() {}
```
+ INVALID_DEPRECATED_EXTEND_ANNOTATION:
+ problemMessage: "The annotation '@Deprecated.extend' can only be applied to extendable classes."
+ correctionMessage: Try removing the '@Deprecated.extend' annotation.
+ hasPublishedDocs: false
+ comment: No parameters.
+ documentation: |-
+ #### Description
+
+ The analyzer produces this diagnostic when anything other than an
+ extendable class is annotated with
+ [`Deprecated.extend`][meta-deprecated-extend]. An extendable class is a
+ class not declared with the `interface`, `final`, or `sealed` keywords,
+ and with at least one public generative constructor.
+
+ #### Example
+
+ The following code produces this diagnostic because the annotation is on a
+ sealed class:
+
+ ```dart
+ @[!Deprecated.extend!]()
+ sealed class C {}
+ ```
+
+ #### Common fixes
+
+ Remove the annotation.
+ INVALID_DEPRECATED_IMPLEMENT_ANNOTATION:
+ problemMessage: "The annotation '@Deprecated.implement' can only be applied to implementable classes."
+ correctionMessage: Try removing the '@Deprecated.implement' annotation.
+ hasPublishedDocs: false
+ comment: No parameters.
+ documentation: |-
+ #### Description
+
+ The analyzer produces this diagnostic when anything other than an
+ implementable class or mixin is annotated with
+ [`Deprecated.implement`][meta-deprecated-implement]. An implementable
+ class or mixin is one not declared with the `base`, `final`, or `sealed`
+ keywords.
+
+ #### Example
+
+ The following code produces this diagnostic because the annotation is on a
+ sealed class:
+
+ ```dart
+ @[!Deprecated.implement!]()
+ sealed class C {}
+ ```
+
+ #### Common fixes
+
+ Remove the annotation.
INVALID_EXPORT_OF_INTERNAL_ELEMENT:
problemMessage: "The member '{0}' can't be exported as a part of a package's public API."
correctionMessage: "Try using a hide clause to hide '{0}'."
diff --git a/pkg/analyzer/test/src/diagnostics/deprecated_extend_test.dart b/pkg/analyzer/test/src/diagnostics/deprecated_extend_test.dart
new file mode 100644
index 0000000..2b252d7
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/deprecated_extend_test.dart
@@ -0,0 +1,109 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(DeprecatedExtendTest);
+ });
+}
+
+@reflectiveTest
+class DeprecatedExtendTest extends PubPackageResolutionTest {
+ test_annotatedClass() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+@Deprecated.extend()
+class Foo {}
+''');
+
+ await assertErrorsInCode(
+ r'''
+import 'foo.dart';
+class Bar extends Foo {}
+''',
+ [error(WarningCode.DEPRECATED_EXTEND, 37, 3)],
+ );
+ }
+
+ test_annotatedClass_indirect() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+@Deprecated.extend()
+class Foo {}
+class Bar extends Foo {}
+''');
+
+ await assertNoErrorsInCode(r'''
+import 'foo.dart';
+class Baz extends Bar {}
+''');
+ }
+
+ test_annotatedClass_typedef() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+@Deprecated.extend()
+class Foo {}
+typedef Foo2 = Foo;
+''');
+
+ await assertNoErrorsInCode(r'''
+import 'foo.dart';
+class Bar extends Foo2 {}
+''');
+ }
+
+ test_annotatedClassTypeAlias() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+@Deprecated.extend()
+class Foo = Object with M;
+mixin M {}
+''');
+
+ await assertErrorsInCode(
+ r'''
+import 'foo.dart';
+class Bar extends Foo {}
+''',
+ [error(WarningCode.DEPRECATED_EXTEND, 37, 3)],
+ );
+ }
+
+ test_classTypeAlias() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+@Deprecated.extend()
+class Foo {}
+''');
+
+ await assertErrorsInCode(
+ r'''
+import 'foo.dart';
+mixin M {}
+class Bar = Foo with M;
+''',
+ [error(WarningCode.DEPRECATED_EXTEND, 42, 3)],
+ );
+ }
+
+ test_insideLibrary() async {
+ await assertNoErrorsInCode(r'''
+@Deprecated.extend()
+class Foo {}
+class Bar extends Foo {}
+''');
+ }
+
+ test_noAnnotation() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+class Foo {}
+''');
+
+ await assertNoErrorsInCode(r'''
+import 'foo.dart';
+class Bar extends Foo {}
+''');
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/deprecated_implement_test.dart b/pkg/analyzer/test/src/diagnostics/deprecated_implement_test.dart
new file mode 100644
index 0000000..0213299
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/deprecated_implement_test.dart
@@ -0,0 +1,124 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(DeprecatedImplementTest);
+ });
+}
+
+@reflectiveTest
+class DeprecatedImplementTest extends PubPackageResolutionTest {
+ test_annotatedClass() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+@Deprecated.implement()
+class Foo {}
+''');
+
+ await assertErrorsInCode(
+ r'''
+import 'foo.dart';
+class Bar implements Foo {}
+''',
+ [error(WarningCode.DEPRECATED_IMPLEMENT, 40, 3)],
+ );
+ }
+
+ test_annotatedClass_indirect() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+@Deprecated.implement()
+class Foo {}
+class Bar extends Foo {}
+''');
+
+ await assertNoErrorsInCode(r'''
+import 'foo.dart';
+class Baz implements Bar {}
+''');
+ }
+
+ test_annotatedClass_typedef() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+@Deprecated.implement()
+class Foo {}
+typedef Foo2 = Foo;
+''');
+
+ await assertNoErrorsInCode(r'''
+import 'foo.dart';
+class Bar implements Foo2 {}
+''');
+ }
+
+ test_annotatedClassTypeAlias() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+@Deprecated.implement()
+class Foo = Object with M;
+mixin M {}
+''');
+
+ await assertErrorsInCode(
+ r'''
+import 'foo.dart';
+class Bar implements Foo {}
+''',
+ [error(WarningCode.DEPRECATED_IMPLEMENT, 40, 3)],
+ );
+ }
+
+ test_classTypeAlias() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+@Deprecated.implement()
+class Foo {}
+''');
+
+ await assertErrorsInCode(
+ r'''
+import 'foo.dart';
+mixin M {}
+class Bar = Object with M implements Foo;
+''',
+ [error(WarningCode.DEPRECATED_IMPLEMENT, 67, 3)],
+ );
+ }
+
+ test_enum() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+@Deprecated.implement()
+class Foo {}
+''');
+
+ await assertErrorsInCode(
+ r'''
+import 'foo.dart';
+enum Bar implements Foo { one; }
+''',
+ [error(WarningCode.DEPRECATED_IMPLEMENT, 39, 3)],
+ );
+ }
+
+ test_insideLibrary() async {
+ await assertNoErrorsInCode(r'''
+@Deprecated.implement()
+class Foo {}
+class Bar implements Foo {}
+''');
+ }
+
+ test_noAnnotation() async {
+ newFile('$testPackageLibPath/foo.dart', r'''
+class Foo {}
+''');
+
+ await assertNoErrorsInCode(r'''
+import 'foo.dart';
+class Bar implements Foo {}
+''');
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_deprecated_extend_annotation_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_deprecated_extend_annotation_test.dart
new file mode 100644
index 0000000..d81fb6e
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/invalid_deprecated_extend_annotation_test.dart
@@ -0,0 +1,103 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(InvalidDeprecatedExtendAnnotationTest);
+ });
+}
+
+@reflectiveTest
+class InvalidDeprecatedExtendAnnotationTest extends PubPackageResolutionTest {
+ test_class() async {
+ await assertNoErrorsInCode(r'''
+@Deprecated.extend()
+class C {}
+''');
+ }
+
+ test_class_final() async {
+ await assertErrorsInCode(
+ r'''
+@Deprecated.extend()
+final class C {}
+''',
+ [error(WarningCode.INVALID_DEPRECATED_EXTEND_ANNOTATION, 1, 17)],
+ );
+ }
+
+ test_class_interface() async {
+ await assertErrorsInCode(
+ r'''
+@Deprecated.extend()
+interface class C {}
+''',
+ [error(WarningCode.INVALID_DEPRECATED_EXTEND_ANNOTATION, 1, 17)],
+ );
+ }
+
+ test_class_noPublicGenerativeConstructor() async {
+ await assertErrorsInCode(
+ r'''
+@Deprecated.extend()
+class C {
+ C._();
+}
+''',
+ [error(WarningCode.INVALID_DEPRECATED_EXTEND_ANNOTATION, 1, 17)],
+ );
+ }
+
+ test_class_sealed() async {
+ await assertErrorsInCode(
+ r'''
+@Deprecated.extend()
+sealed class C {}
+''',
+ [error(WarningCode.INVALID_DEPRECATED_EXTEND_ANNOTATION, 1, 17)],
+ );
+ }
+
+ test_classTypeAlias() async {
+ await assertNoErrorsInCode(r'''
+mixin M {}
+@Deprecated.extend()
+class C = Object with M;
+''');
+ }
+
+ test_mixin() async {
+ await assertErrorsInCode(
+ r'''
+@Deprecated.extend()
+mixin M {}
+''',
+ [error(WarningCode.INVALID_DEPRECATED_EXTEND_ANNOTATION, 1, 17)],
+ );
+ }
+
+ test_typeAlias_forClass() async {
+ await assertNoErrorsInCode(r'''
+class C {}
+@Deprecated.extend()
+typedef D = C;
+''');
+ }
+
+ test_typeAlias_forEnum() async {
+ await assertErrorsInCode(
+ r'''
+enum E { one; }
+@Deprecated.extend()
+typedef F = E;
+''',
+ [error(WarningCode.INVALID_DEPRECATED_EXTEND_ANNOTATION, 17, 17)],
+ );
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/invalid_deprecated_implement_annotation_test.dart b/pkg/analyzer/test/src/diagnostics/invalid_deprecated_implement_annotation_test.dart
new file mode 100644
index 0000000..b556be3
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/invalid_deprecated_implement_annotation_test.dart
@@ -0,0 +1,109 @@
+// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(InvalidDeprecatedImplementAnnotationTest);
+ });
+}
+
+@reflectiveTest
+class InvalidDeprecatedImplementAnnotationTest
+ extends PubPackageResolutionTest {
+ test_class() async {
+ await assertNoErrorsInCode(r'''
+@Deprecated.implement()
+class C {}
+''');
+ }
+
+ test_class_base() async {
+ await assertErrorsInCode(
+ r'''
+@Deprecated.implement()
+base class C {}
+''',
+ [error(WarningCode.INVALID_DEPRECATED_IMPLEMENT_ANNOTATION, 1, 20)],
+ );
+ }
+
+ test_class_final() async {
+ await assertErrorsInCode(
+ r'''
+@Deprecated.implement()
+final class C {}
+''',
+ [error(WarningCode.INVALID_DEPRECATED_IMPLEMENT_ANNOTATION, 1, 20)],
+ );
+ }
+
+ test_class_sealed() async {
+ await assertErrorsInCode(
+ r'''
+@Deprecated.implement()
+sealed class C {}
+''',
+ [error(WarningCode.INVALID_DEPRECATED_IMPLEMENT_ANNOTATION, 1, 20)],
+ );
+ }
+
+ test_classTypeAlias() async {
+ await assertNoErrorsInCode(r'''
+mixin M {}
+@Deprecated.implement()
+class C = Object with M;
+''');
+ }
+
+ test_function() async {
+ await assertErrorsInCode(
+ r'''
+@Deprecated.implement()
+void f() {}
+''',
+ [error(WarningCode.INVALID_DEPRECATED_IMPLEMENT_ANNOTATION, 1, 20)],
+ );
+ }
+
+ test_mixin() async {
+ await assertNoErrorsInCode(r'''
+@Deprecated.implement()
+mixin M {}
+''');
+ }
+
+ test_mixin_base() async {
+ await assertErrorsInCode(
+ r'''
+@Deprecated.implement()
+base mixin M {}
+''',
+ [error(WarningCode.INVALID_DEPRECATED_IMPLEMENT_ANNOTATION, 1, 20)],
+ );
+ }
+
+ test_typeAlias_forClass() async {
+ await assertNoErrorsInCode(r'''
+class C {}
+@Deprecated.implement()
+typedef D = C;
+''');
+ }
+
+ test_typeAlias_forEnum() async {
+ await assertErrorsInCode(
+ r'''
+enum E { one; }
+@Deprecated.implement()
+typedef F = E;
+''',
+ [error(WarningCode.INVALID_DEPRECATED_IMPLEMENT_ANNOTATION, 17, 20)],
+ );
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 3304af3..da053cc 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -180,7 +180,9 @@
import 'deprecated_colon_for_default_value_test.dart'
as deprecated_colon_for_default_value;
import 'deprecated_export_use_test.dart' as deprecated_export_use;
+import 'deprecated_extend_test.dart' as deprecated_extend;
import 'deprecated_extends_function_test.dart' as deprecated_extends_function;
+import 'deprecated_implement_test.dart' as deprecated_implement;
import 'deprecated_implements_function_test.dart'
as deprecated_implements_function;
import 'deprecated_member_use_test.dart' as deprecated_member_use;
@@ -434,6 +436,10 @@
as await_not_required_annotation;
import 'invalid_constant_test.dart' as invalid_constant;
import 'invalid_constructor_name_test.dart' as invalid_constructor_name;
+import 'invalid_deprecated_extend_annotation_test.dart'
+ as invalid_deprecated_extend_annotation;
+import 'invalid_deprecated_implement_annotation_test.dart'
+ as invalid_deprecated_implement_annotation;
import 'invalid_do_not_submit_test.dart' as invalid_do_not_submit;
import 'invalid_exception_value_test.dart' as invalid_exception_value;
import 'invalid_export_of_internal_element_test.dart'
@@ -690,7 +696,8 @@
as nullable_type_in_implements_clause;
import 'nullable_type_in_on_clause_test.dart' as nullable_type_in_on_clause;
import 'nullable_type_in_with_clause_test.dart' as nullable_type_in_with_clause;
-import 'number_literals_with_separators_test.dart' as number_literals_with_separators;
+import 'number_literals_with_separators_test.dart'
+ as number_literals_with_separators;
import 'object_cannot_extend_another_class_test.dart'
as object_cannot_extend_another_class;
import 'obsolete_colon_for_default_value_test.dart'
@@ -1056,7 +1063,9 @@
definitely_unassigned_late_local_variable.main();
deprecated_colon_for_default_value.main();
deprecated_export_use.main();
+ deprecated_extend.main();
deprecated_extends_function.main();
+ deprecated_implement.main();
deprecated_implements_function.main();
deprecated_member_use.main();
deprecated_mixin_function.main();
@@ -1214,6 +1223,8 @@
await_not_required_annotation.main();
invalid_constant.main();
invalid_constructor_name.main();
+ invalid_deprecated_extend_annotation.main();
+ invalid_deprecated_implement_annotation.main();
invalid_do_not_submit.main();
invalid_exception_value.main();
invalid_export_of_internal_element.main();
diff --git a/pkg/linter/lib/src/rules/deprecated_member_use_from_same_package.dart b/pkg/linter/lib/src/rules/deprecated_member_use_from_same_package.dart
index d294e03..4e75d4e 100644
--- a/pkg/linter/lib/src/rules/deprecated_member_use_from_same_package.dart
+++ b/pkg/linter/lib/src/rules/deprecated_member_use_from_same_package.dart
@@ -11,6 +11,7 @@
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/error/error.dart';
+import 'package:analyzer/src/dart/element/extensions.dart'; // ignore: implementation_imports
import 'package:analyzer/src/error/deprecated_member_use_verifier.dart' // ignore: implementation_imports
show BaseDeprecatedMemberUseVerifier;
import 'package:analyzer/workspace/workspace.dart';
@@ -343,11 +344,7 @@
}
void _withDeprecatedFragment(Fragment? fragment, void Function() recurse) {
- var isDeprecated = false;
- if (fragment?.element case var element?) {
- isDeprecated = element.metadata.hasDeprecated;
- }
-
+ var isDeprecated = fragment?.element.isUseDeprecated ?? false;
_deprecatedVerifier.pushInDeprecatedValue(isDeprecated);
try {
recurse();