blob: fdc373fcc3f618b21f038489055af16aeda40044 [file]
// Copyright (c) 2023, 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/ast/syntactic_entity.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
// ignore: implementation_imports
import 'package:analyzer/src/error/deprecated_member_use_verifier.dart';
// ignore: implementation_imports
import 'package:analyzer/src/workspace/workspace.dart';
import '../analyzer.dart';
const _desc =
'Avoid using deprecated elements from within the package in which they are '
'declared.';
const _details = r'''
Elements that are annotated with `@Deprecated` should not be referenced from
within the package in which they are declared.
**AVOID** using deprecated elements.
...
**BAD:**
```dart
// Declared in one library:
class Foo {
@Deprecated("Use 'm2' instead")
void m1() {}
void m2({
@Deprecated('This is an old parameter') int? p,
})
}
@Deprecated('Do not use')
int x = 0;
// In the same or another library, but within the same package:
void m(Foo foo) {
foo.m1();
foo.m2(p: 7);
x = 1;
}
```
Deprecated elements can be used from within _other_ deprecated elements, in
order to allow for the deprecation of a collection of APIs together as one unit.
**GOOD:**
```dart
// Declared in one library:
class Foo {
@Deprecated("Use 'm2' instead")
void m1() {}
void m2({
@Deprecated('This is an old parameter') int? p,
})
}
@Deprecated('Do not use')
int x = 0;
// In the same or another library, but within the same package:
@Deprecated('Do not use')
void m(Foo foo) {
foo.m1();
foo.m2(p: 7);
x = 1;
}
```
''';
class DeprecatedMemberUseFromSamePackage extends LintRule {
static const LintCode code = LintCode(
'deprecated_member_use_from_same_package',
"'{0}' is deprecated and shouldn't be used.",
correctionMessage:
'Try replacing the use of the deprecated member with the replacement, '
'if a replacement is specified.',
);
static const LintCode codeWithMessage = LintCode(
'deprecated_member_use_from_same_package',
"'{0}' is deprecated and shouldn't be used. {1}",
correctionMessage:
'Try replacing the use of the deprecated member with the replacement, '
'if a replacement is specified.',
uniqueName: 'LintCode.deprecated_member_use_from_same_package_with_message',
);
DeprecatedMemberUseFromSamePackage()
: super(
name: 'deprecated_member_use_from_same_package',
description: _desc,
details: _details,
group: Group.errors);
@override
List<LintCode> get lintCodes => [code, codeWithMessage];
@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
var visitor = _Visitor(this, context);
registry.addCompilationUnit(this, visitor);
}
}
class _DeprecatedMemberUseVerifier extends BaseDeprecatedMemberUseVerifier {
final LintRule _rule;
final WorkspacePackage _workspacePackage;
_DeprecatedMemberUseVerifier(this._rule, this._workspacePackage);
@override
void reportError(SyntacticEntity errorEntity, Element element,
String displayName, String? message) {
var library = element is LibraryElement ? element : element.library;
if (library == null || !_workspacePackage.contains(library.source)) {
// In this case, `DEPRECATED_MEMBER_USE` is reported by the analyzer.
return;
}
var normalizedMessage = message?.trim();
if (normalizedMessage == null ||
normalizedMessage.isEmpty ||
normalizedMessage == '.') {
_rule.reportLintForOffset(
errorEntity.offset,
errorEntity.length,
arguments: [displayName],
errorCode: DeprecatedMemberUseFromSamePackage.code,
);
} else {
if (!normalizedMessage.endsWith('.') &&
!normalizedMessage.endsWith('?') &&
!normalizedMessage.endsWith('!')) {
normalizedMessage = '$message.';
}
_rule.reportLintForOffset(
errorEntity.offset,
errorEntity.length,
arguments: [displayName, normalizedMessage],
errorCode: DeprecatedMemberUseFromSamePackage.codeWithMessage,
);
}
}
}
/// This visitor uses a [DeprecatedMemberUseVerifier] to both report uses of
/// deprecated elements, and to track the deprecated-ness of ancestor
/// declaration nodes.
class _RecursiveVisitor extends RecursiveAstVisitor<void> {
final _DeprecatedMemberUseVerifier _deprecatedVerifier;
_RecursiveVisitor(LintRule rule, WorkspacePackage package)
: _deprecatedVerifier = _DeprecatedMemberUseVerifier(rule, package);
@override
void visitAssignmentExpression(AssignmentExpression node) {
_deprecatedVerifier.assignmentExpression(node);
super.visitAssignmentExpression(node);
}
@override
void visitBinaryExpression(BinaryExpression node) {
_deprecatedVerifier.binaryExpression(node);
super.visitBinaryExpression(node);
}
@override
void visitClassDeclaration(ClassDeclaration node) {
_deprecatedVerifier
.pushInDeprecatedValue(node.declaredElement?.hasDeprecated ?? false);
try {
super.visitClassDeclaration(node);
} finally {
_deprecatedVerifier.popInDeprecated();
}
}
@override
void visitClassTypeAlias(ClassTypeAlias node) {
_deprecatedVerifier
.pushInDeprecatedValue(node.declaredElement?.hasDeprecated ?? false);
try {
super.visitClassTypeAlias(node);
} finally {
_deprecatedVerifier.popInDeprecated();
}
}
@override
void visitCompilationUnit(CompilationUnit node) {
var library = node.declaredElement?.library;
if (library == null) {
return;
}
_deprecatedVerifier.pushInDeprecatedValue(library.hasDeprecated);
super.visitCompilationUnit(node);
}
@override
void visitConstructorDeclaration(ConstructorDeclaration node) {
_deprecatedVerifier
.pushInDeprecatedValue(node.declaredElement?.hasDeprecated ?? false);
try {
super.visitConstructorDeclaration(node);
} finally {
_deprecatedVerifier.popInDeprecated();
}
}
@override
void visitConstructorName(ConstructorName node) {
_deprecatedVerifier.constructorName(node);
super.visitConstructorName(node);
}
@override
void visitDefaultFormalParameter(DefaultFormalParameter node) {
_deprecatedVerifier
.pushInDeprecatedValue(node.declaredElement?.hasDeprecated ?? false);
try {
super.visitDefaultFormalParameter(node);
} finally {
_deprecatedVerifier.popInDeprecated();
}
}
@override
void visitEnumDeclaration(EnumDeclaration node) {
_deprecatedVerifier
.pushInDeprecatedValue(node.declaredElement?.hasDeprecated ?? false);
try {
super.visitEnumDeclaration(node);
} finally {
_deprecatedVerifier.popInDeprecated();
}
}
@override
void visitExportDirective(ExportDirective node) {
_deprecatedVerifier.exportDirective(node);
super.visitExportDirective(node);
}
@override
void visitExtensionDeclaration(ExtensionDeclaration node) {
_deprecatedVerifier
.pushInDeprecatedValue(node.declaredElement?.hasDeprecated ?? false);
try {
super.visitExtensionDeclaration(node);
} finally {
_deprecatedVerifier.popInDeprecated();
}
}
@override
void visitExtensionOverride(ExtensionOverride node) {
_deprecatedVerifier.extensionOverride(node);
super.visitExtensionOverride(node);
}
@override
void visitFieldDeclaration(FieldDeclaration node) {
_deprecatedVerifier.pushInDeprecatedMetadata(node.metadata);
try {
super.visitFieldDeclaration(node);
} finally {
_deprecatedVerifier.popInDeprecated();
}
}
@override
void visitFieldFormalParameter(FieldFormalParameter node) {
_deprecatedVerifier
.pushInDeprecatedValue(node.declaredElement?.hasDeprecated ?? false);
try {
super.visitFieldFormalParameter(node);
} finally {
_deprecatedVerifier.popInDeprecated();
}
}
@override
void visitFunctionDeclaration(FunctionDeclaration node) {
_deprecatedVerifier
.pushInDeprecatedValue(node.declaredElement?.hasDeprecated ?? false);
try {
super.visitFunctionDeclaration(node);
} finally {
_deprecatedVerifier.popInDeprecated();
}
}
@override
void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
_deprecatedVerifier.functionExpressionInvocation(node);
super.visitFunctionExpressionInvocation(node);
}
@override
void visitFunctionTypeAlias(FunctionTypeAlias node) {
_deprecatedVerifier
.pushInDeprecatedValue(node.declaredElement?.hasDeprecated ?? false);
try {
super.visitFunctionTypeAlias(node);
} finally {
_deprecatedVerifier.popInDeprecated();
}
}
@override
void visitGenericTypeAlias(GenericTypeAlias node) {
_deprecatedVerifier
.pushInDeprecatedValue(node.declaredElement?.hasDeprecated ?? false);
try {
super.visitGenericTypeAlias(node);
} finally {
_deprecatedVerifier.popInDeprecated();
}
}
@override
void visitImportDirective(ImportDirective node) {
_deprecatedVerifier.importDirective(node);
super.visitImportDirective(node);
}
@override
void visitIndexExpression(IndexExpression node) {
_deprecatedVerifier.indexExpression(node);
super.visitIndexExpression(node);
}
@override
void visitInstanceCreationExpression(InstanceCreationExpression node) {
_deprecatedVerifier.instanceCreationExpression(node);
super.visitInstanceCreationExpression(node);
}
@override
void visitMethodDeclaration(MethodDeclaration node) {
_deprecatedVerifier
.pushInDeprecatedValue(node.declaredElement?.hasDeprecated ?? false);
try {
super.visitMethodDeclaration(node);
} finally {
_deprecatedVerifier.popInDeprecated();
}
}
@override
void visitMethodInvocation(MethodInvocation node) {
_deprecatedVerifier.methodInvocation(node);
super.visitMethodInvocation(node);
}
@override
void visitMixinDeclaration(MixinDeclaration node) {
_deprecatedVerifier
.pushInDeprecatedValue(node.declaredElement?.hasDeprecated ?? false);
try {
super.visitMixinDeclaration(node);
} finally {
_deprecatedVerifier.popInDeprecated();
}
}
@override
void visitNamedType(NamedType node) {
_deprecatedVerifier.namedType(node);
super.visitNamedType(node);
}
@override
void visitPostfixExpression(PostfixExpression node) {
_deprecatedVerifier.postfixExpression(node);
super.visitPostfixExpression(node);
}
@override
void visitPrefixExpression(PrefixExpression node) {
_deprecatedVerifier.prefixExpression(node);
super.visitPrefixExpression(node);
}
@override
void visitRedirectingConstructorInvocation(
RedirectingConstructorInvocation node) {
_deprecatedVerifier.redirectingConstructorInvocation(node);
super.visitRedirectingConstructorInvocation(node);
}
@override
void visitSimpleFormalParameter(SimpleFormalParameter node) {
_deprecatedVerifier
.pushInDeprecatedValue(node.declaredElement?.hasDeprecated ?? false);
try {
super.visitSimpleFormalParameter(node);
} finally {
_deprecatedVerifier.popInDeprecated();
}
}
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
_deprecatedVerifier.simpleIdentifier(node);
super.visitSimpleIdentifier(node);
}
@override
void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
_deprecatedVerifier.superConstructorInvocation(node);
super.visitSuperConstructorInvocation(node);
}
@override
void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
_deprecatedVerifier.pushInDeprecatedMetadata(node.metadata);
try {
super.visitTopLevelVariableDeclaration(node);
} finally {
_deprecatedVerifier.popInDeprecated();
}
}
}
/// This [SimpleAstVisitor] visits the [CompilationUnit], and forwards the
/// remainder of visitations to [_RecursiveVisitor], which keeps track of
/// the deprecated-ness of ancestor declaration nodes.
class _Visitor extends SimpleAstVisitor<void> {
final LintRule _rule;
final LinterContext _context;
_Visitor(this._rule, this._context);
@override
void visitCompilationUnit(CompilationUnit node) {
var package = _context.package;
if (package == null) {
// If we don't appear to be in any known package structure, then we can
// never report that a deprecated use is from the same package as the
// declaration.
return;
}
var visitor = _RecursiveVisitor(_rule, package);
node.accept(visitor);
}
}