diff --git a/example/all.yaml b/example/all.yaml index 8e0ea071d..3212fef10 100644 --- a/example/all.yaml +++ b/example/all.yaml @@ -59,6 +59,7 @@ linter: - constant_identifier_names - control_flow_in_finally - curly_braces_in_flow_control_structures + - deprecated_consistency - diagnostic_describe_all_properties - directives_ordering - do_not_use_environment diff --git a/lib/src/rules.dart b/lib/src/rules.dart index 6b7938cc1..f9df94131 100644 --- a/lib/src/rules.dart +++ b/lib/src/rules.dart @@ -60,6 +60,7 @@ import 'rules/comment_references.dart'; import 'rules/constant_identifier_names.dart'; import 'rules/control_flow_in_finally.dart'; import 'rules/curly_braces_in_flow_control_structures.dart'; +import 'rules/deprecated_consistency.dart'; import 'rules/diagnostic_describe_all_properties.dart'; import 'rules/directives_ordering.dart'; import 'rules/do_not_use_environment.dart'; @@ -246,6 +247,7 @@ void registerLintRules() { ..register(ConstantIdentifierNames()) ..register(ControlFlowInFinally()) ..register(CurlyBracesInFlowControlStructures()) + ..register(DeprecatedConsistency()) ..register(DiagnosticsDescribeAllProperties()) ..register(DirectivesOrdering()) ..register(DoNotUseEnvironment()) diff --git a/lib/src/rules/deprecated_consistency.dart b/lib/src/rules/deprecated_consistency.dart new file mode 100644 index 000000000..6523c9e6e --- /dev/null +++ b/lib/src/rules/deprecated_consistency.dart @@ -0,0 +1,97 @@ +// Copyright (c) 2021, 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/visitor.dart'; +import 'package:analyzer/dart/element/element.dart'; + +import '../analyzer.dart'; + +const _desc = r'Missing deprecated annotation.'; + +const _details = r''' + +Do apply `@Deprecated()` consistently: + +- if a class is deprecated, its constructors should also be deprecated. +- if a field is deprecated, the constructor parameter pointing to it should also be deprecated. +- if a constructor parameter pointing to a field is deprecated, the field should also be deprecated. + +**BAD:** +``` +@deprecated +class A { + A(); +} + +class B { + B({this.field}); + @deprecated + Object field; +} +``` + +**GOOD:** +``` +@deprecated +class A { + @deprecated + A(); +} + +class B { + B({@deprecated this.field}); + @deprecated + Object field; +} +``` + +'''; + +class DeprecatedConsistency extends LintRule implements NodeLintRule { + DeprecatedConsistency() + : super( + name: 'deprecated_consistency', + description: _desc, + details: _details, + group: Group.style, + ); + + @override + void registerNodeProcessors( + NodeLintRegistry registry, LinterContext context) { + final visitor = _Visitor(this); + registry.addConstructorDeclaration(this, visitor); + registry.addFieldFormalParameter(this, visitor); + } +} + +class _Visitor extends SimpleAstVisitor { + final LintRule rule; + + _Visitor(this.rule); + + @override + void visitConstructorDeclaration(ConstructorDeclaration node) { + var constructorElement = node.declaredElement; + if (constructorElement.enclosingElement.hasDeprecated && + !constructorElement.hasDeprecated) { + rule.reportLint(node); + } + } + + @override + void visitFieldFormalParameter(FieldFormalParameter node) { + var declaredElement = node.declaredElement as FieldFormalParameterElement; + var field = declaredElement.field; + if (field == null) return; + + if (field.hasDeprecated && !declaredElement.hasDeprecated) { + rule.reportLint(node); + } + if (!field.hasDeprecated && declaredElement.hasDeprecated) { + rule.reportLintForOffset(field.nameOffset, field.nameLength); + } + } +} diff --git a/test/rules/deprecated_consistency.dart b/test/rules/deprecated_consistency.dart new file mode 100644 index 000000000..b39f95fc1 --- /dev/null +++ b/test/rules/deprecated_consistency.dart @@ -0,0 +1,26 @@ +// Copyright (c) 2021, 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. + +// test w/ `pub run test -N deprecated_consistency` + +@deprecated +class A { + A.ko(); // LINT + @deprecated + A.ok(); // OK +} + +class B { + B.ko({this.field = 0}); // LINT + B.ok({@deprecated this.field = 0}); // OK + + @deprecated + Object field; +} + +class C { + C({@deprecated this.field = 0}); // OK + + Object field; // LINT +}