diff --git a/CHANGELOG.md b/CHANGELOG.md index c0275fa729..351cfaa247 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased +* feat: ignore tear-off methods for [`avoid-unused-parameters`](https://dartcodemetrics.dev/docs/rules/common/avoid-unused-parameters). * feat: show warning for rules without config that require config to work. * fix: correctly handle FunctionExpressions for [`avoid-redundant-async`](https://dartcodemetrics.dev/docs/rules/common/avoid-redundant-async). * feat: support ignoring nesting for [`prefer-conditional-expressions`](https://dartcodemetrics.dev/docs/rules/common/prefer-conditional-expressions). diff --git a/lib/src/analyzers/lint_analyzer/rules/rules_list/avoid_redundant_async/visitor.dart b/lib/src/analyzers/lint_analyzer/rules/rules_list/avoid_redundant_async/visitor.dart index 43ef7151d2..767e9520c1 100644 --- a/lib/src/analyzers/lint_analyzer/rules/rules_list/avoid_redundant_async/visitor.dart +++ b/lib/src/analyzers/lint_analyzer/rules/rules_list/avoid_redundant_async/visitor.dart @@ -9,12 +9,7 @@ class _Visitor extends RecursiveAstVisitor { void visitMethodDeclaration(MethodDeclaration node) { super.visitMethodDeclaration(node); - final isOverride = node.metadata.any( - (node) => - node.name.name == 'override' && node.atSign.type == TokenType.AT, - ); - - if (!isOverride && _hasRedundantAsync(node.body)) { + if (!isOverride(node.metadata) && _hasRedundantAsync(node.body)) { _nodes.add(node); } } diff --git a/lib/src/analyzers/lint_analyzer/rules/rules_list/avoid_unused_parameters/avoid_unused_parameters_rule.dart b/lib/src/analyzers/lint_analyzer/rules/rules_list/avoid_unused_parameters/avoid_unused_parameters_rule.dart index 200693bbd6..cdff87370b 100644 --- a/lib/src/analyzers/lint_analyzer/rules/rules_list/avoid_unused_parameters/avoid_unused_parameters_rule.dart +++ b/lib/src/analyzers/lint_analyzer/rules/rules_list/avoid_unused_parameters/avoid_unused_parameters_rule.dart @@ -1,7 +1,6 @@ // ignore_for_file: public_member_api_docs import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/ast/token.dart'; import 'package:analyzer/dart/ast/visitor.dart'; import 'package:analyzer/dart/element/element.dart'; diff --git a/lib/src/analyzers/lint_analyzer/rules/rules_list/avoid_unused_parameters/visitor.dart b/lib/src/analyzers/lint_analyzer/rules/rules_list/avoid_unused_parameters/visitor.dart index 25bbc5b7f1..73f732e05e 100644 --- a/lib/src/analyzers/lint_analyzer/rules/rules_list/avoid_unused_parameters/visitor.dart +++ b/lib/src/analyzers/lint_analyzer/rules/rules_list/avoid_unused_parameters/visitor.dart @@ -19,12 +19,9 @@ class _Visitor extends RecursiveAstVisitor { return; } - final isOverride = node.metadata.any( - (node) => - node.name.name == 'override' && node.atSign.type == TokenType.AT, - ); + final isTearOff = _usedAsTearOff(node); - if (!isOverride) { + if (!isOverride(node.metadata) && !isTearOff) { _unusedParameters.addAll( _getUnusedParameters( node.body, @@ -77,6 +74,18 @@ class _Visitor extends RecursiveAstVisitor { bool _hasNoUnderscoresInName(FormalParameter parameter) => parameter.name != null && parameter.name!.lexeme.replaceAll('_', '').isNotEmpty; + + bool _usedAsTearOff(MethodDeclaration node) { + final name = node.name.lexeme; + if (!Identifier.isPrivateName(name)) { + return false; + } + + final visitor = _InvocationsVisitor(name); + node.root.visitChildren(visitor); + + return visitor.hasTearOffInvocations; + } } class _IdentifiersVisitor extends RecursiveAstVisitor { @@ -92,3 +101,22 @@ class _IdentifiersVisitor extends RecursiveAstVisitor { } } } + +class _InvocationsVisitor extends RecursiveAstVisitor { + final String methodName; + + bool hasTearOffInvocations = false; + + _InvocationsVisitor(this.methodName); + + @override + void visitSimpleIdentifier(SimpleIdentifier node) { + if (node.name == methodName && + node.staticElement is MethodElement && + node.parent! is MethodInvocation) { + hasTearOffInvocations = true; + } + + super.visitSimpleIdentifier(node); + } +} diff --git a/lib/src/utils/node_utils.dart b/lib/src/utils/node_utils.dart index adbeffb952..19179c11b7 100644 --- a/lib/src/utils/node_utils.dart +++ b/lib/src/utils/node_utils.dart @@ -1,5 +1,6 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/syntactic_entity.dart'; +import 'package:analyzer/dart/ast/token.dart'; import 'package:source_span/source_span.dart'; import '../analyzers/lint_analyzer/models/internal_resolved_unit_result.dart'; @@ -37,6 +38,11 @@ SourceSpan nodeLocation({ ); } +bool isOverride(List metadata) => metadata.any( + (node) => + node.name.name == 'override' && node.atSign.type == TokenType.AT, + ); + bool isEntrypoint(String name, NodeList metadata) => name == 'main' || _hasPragmaAnnotation(metadata) || diff --git a/test/src/analyzers/lint_analyzer/rules/rules_list/avoid_unused_parameters/avoid_unused_parameters_rule_test.dart b/test/src/analyzers/lint_analyzer/rules/rules_list/avoid_unused_parameters/avoid_unused_parameters_rule_test.dart index e3b48b55f7..05b1939b1d 100644 --- a/test/src/analyzers/lint_analyzer/rules/rules_list/avoid_unused_parameters/avoid_unused_parameters_rule_test.dart +++ b/test/src/analyzers/lint_analyzer/rules/rules_list/avoid_unused_parameters/avoid_unused_parameters_rule_test.dart @@ -8,6 +8,8 @@ const _correctExamplePath = 'avoid_unused_parameters/examples/correct_example.dart'; const _incorrectExamplePath = 'avoid_unused_parameters/examples/incorrect_example.dart'; +const _tearOffExamplePath = + 'avoid_unused_parameters/examples/tear_off_example.dart'; void main() { group('AvoidUnusedParametersRule', () { @@ -61,5 +63,26 @@ void main() { ], ); }); + + test('should report about found issues for tear-offs', () async { + final unit = await RuleTestHelper.resolveFromFile(_tearOffExamplePath); + final issues = AvoidUnusedParametersRule().check(unit); + + RuleTestHelper.verifyIssues( + issues: issues, + startLines: [8, 13, 13], + startColumns: [36, 23, 43], + locationTexts: [ + 'int value', + 'String firstString', + 'String secondString', + ], + messages: [ + 'Parameter is unused.', + 'Parameter is unused.', + 'Parameter is unused.', + ], + ); + }); }); } diff --git a/test/src/analyzers/lint_analyzer/rules/rules_list/avoid_unused_parameters/examples/tear_off_example.dart b/test/src/analyzers/lint_analyzer/rules/rules_list/avoid_unused_parameters/examples/tear_off_example.dart new file mode 100644 index 0000000000..3be78c7948 --- /dev/null +++ b/test/src/analyzers/lint_analyzer/rules/rules_list/avoid_unused_parameters/examples/tear_off_example.dart @@ -0,0 +1,20 @@ +class TestClass { + String value; + + void _someMethod(Function(String, int) callback) { + callback(); + } + + void _otherMethod(String string, int value) { + print(string); + } + + // LINT + void _anotherMethod(String firstString, String secondString) { + someMethod(_otherMethod); + _someOtherMethod(value); + } + + // LINT + _someOtherMethod(String value) {} +}