diff --git a/lib/src/lints/prefer_early_return/prefer_early_return_rule.dart b/lib/src/lints/prefer_early_return/prefer_early_return_rule.dart index e4b54b18..4f420de0 100644 --- a/lib/src/lints/prefer_early_return/prefer_early_return_rule.dart +++ b/lib/src/lints/prefer_early_return/prefer_early_return_rule.dart @@ -1,8 +1,8 @@ -import 'package:analyzer/error/listener.dart'; -import 'package:custom_lint_builder/custom_lint_builder.dart'; +import 'package:analyzer/analysis_rule/analysis_rule.dart'; +import 'package:analyzer/analysis_rule/rule_context.dart'; +import 'package:analyzer/analysis_rule/rule_visitor_registry.dart'; +import 'package:analyzer/error/error.dart'; import 'package:solid_lints/src/lints/prefer_early_return/visitors/prefer_early_return_visitor.dart'; -import 'package:solid_lints/src/models/rule_config.dart'; -import 'package:solid_lints/src/models/solid_lint_rule.dart'; /// A rule which highlights `if` statements that span the entire body, /// and suggests replacing them with a reversed boolean check @@ -31,38 +31,35 @@ import 'package:solid_lints/src/models/solid_lint_rule.dart'; /// c; /// } /// ``` -class PreferEarlyReturnRule extends SolidLintRule { - /// This lint rule represents the error if - /// 'if' statements should be reversed +class PreferEarlyReturnRule extends AnalysisRule { + /// The name of the lint static const String lintName = 'prefer_early_return'; - PreferEarlyReturnRule._(super.config); + /// The message shown when the lint is triggered + static const String lintMessage = 'Use reverse if to reduce nesting'; + + /// Lint code + static const LintCode _code = LintCode( + lintName, + lintMessage, + ); /// Creates a new instance of [PreferEarlyReturnRule] - /// based on the lint configuration. - factory PreferEarlyReturnRule.createRule(CustomLintConfigs configs) { - final rule = RuleConfig( - configs: configs, - name: lintName, - problemMessage: (_) => "Use reverse if to reduce nesting", - ); + PreferEarlyReturnRule() + : super( + name: lintName, + description: lintMessage, + ); - return PreferEarlyReturnRule._(rule); - } + @override + LintCode get diagnosticCode => _code; @override - void run( - CustomLintResolver resolver, - DiagnosticReporter reporter, - CustomLintContext context, + void registerNodeProcessors( + RuleVisitorRegistry registry, + RuleContext context, ) { - context.registry.addBlockFunctionBody((node) { - final visitor = PreferEarlyReturnVisitor(); - node.accept(visitor); - - for (final element in visitor.nodes) { - reporter.atNode(element, code); - } - }); + final visitor = PreferEarlyReturnVisitor(this); + registry.addBlockFunctionBody(this, visitor); } } diff --git a/lib/src/lints/prefer_early_return/visitors/prefer_early_return_visitor.dart b/lib/src/lints/prefer_early_return/visitors/prefer_early_return_visitor.dart index 127de6e8..a8342ef5 100644 --- a/lib/src/lints/prefer_early_return/visitors/prefer_early_return_visitor.dart +++ b/lib/src/lints/prefer_early_return/visitors/prefer_early_return_visitor.dart @@ -1,14 +1,16 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/visitor.dart'; +import 'package:solid_lints/src/lints/prefer_early_return/prefer_early_return_rule.dart'; import 'package:solid_lints/src/lints/prefer_early_return/visitors/return_statement_visitor.dart'; import 'package:solid_lints/src/lints/prefer_early_return/visitors/throw_expression_visitor.dart'; -/// The AST visitor that will collect all unnecessary if statements +/// Visitor for [PreferEarlyReturnRule]. class PreferEarlyReturnVisitor extends RecursiveAstVisitor { - final _nodes = []; + /// The rule associated with this visitor. + final PreferEarlyReturnRule rule; - /// All unnecessary if statements and conditional expressions. - Iterable get nodes => _nodes; + /// Creates an instance of [PreferEarlyReturnVisitor]. + PreferEarlyReturnVisitor(this.rule); @override void visitBlockFunctionBody(BlockFunctionBody node) { @@ -36,7 +38,7 @@ class PreferEarlyReturnVisitor extends RecursiveAstVisitor { if (_hasReturnStatement(node)) return; if (_hasThrowExpression(node)) return; - _nodes.add(node); + rule.reportAtNode(node); } // returns a list of if statements at the start of the function diff --git a/test/lints/prefer_early_return/prefer_early_return_rule_test.dart b/test/lints/prefer_early_return/prefer_early_return_rule_test.dart new file mode 100644 index 00000000..e39dae80 --- /dev/null +++ b/test/lints/prefer_early_return/prefer_early_return_rule_test.dart @@ -0,0 +1,478 @@ +import 'package:analyzer_testing/analysis_rule/analysis_rule.dart'; +import 'package:solid_lints/src/lints/prefer_early_return/prefer_early_return_rule.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +void main() { + defineReflectiveSuite(() { + defineReflectiveTests(PreferEarlyReturnRuleTest); + }); +} + +@reflectiveTest +class PreferEarlyReturnRuleTest extends AnalysisRuleTest { + @override + void setUp() { + rule = PreferEarlyReturnRule(); + super.setUp(); + } + + @override + String get analysisRule => PreferEarlyReturnRule.lintName; + + void test_reports_if_as_only_statement_in_function() async { + await assertDiagnostics( + r''' +void test(bool a) { + if (a) { + print('hello'); + } +} +''', + [lint(22, 32)], + ); + } + + void test_reports_if_with_return() async { + await assertDiagnostics( + r''' +void test(bool a) { + if (a) { + print('hello'); + } + + return; +} +''', + [lint(22, 32)], + ); + } + + void test_does_not_report_if_with_return_value() async { + await assertNoDiagnostics( + r''' +int test(bool a) { + if (a) { + print('hello'); + } + + return 1; +} +''', + ); + } + + void test_reports_nested_if_as_only_statement() async { + await assertDiagnostics( + r''' +void test(bool a, bool b) { + if (a) { + if (b) { + print('nested'); + } + } +} +''', + [ + lint(30, 54), + ], + ); + } + + void test_does_not_report_nested_if_with_return_value() async { + await assertNoDiagnostics( + r''' +int test(bool a, bool b) { + if (a) { + if (b) { + print('nested'); + } + } + + return 1; +} +''', + ); + } + + void test_reports_nested_3_if_as_only_statement() async { + await assertDiagnostics( + r''' +void test(bool a, bool b, bool c) { + if (a) { + if (b) { + if (c){ + print('nested'); + } + } + } +} +''', + [ + lint(38, 78), + ], + ); + } + + void test_reports_nested_3_with_return() async { + await assertDiagnostics( + r''' +void test(bool a, bool b, bool c) { + if (a) { + if (b) { + if (c){ + print('nested'); + } + } + } + return; +} +''', + [ + lint(38, 78), + ], + ); + } + + void test_does_not_report_if_else() async { + await assertNoDiagnostics( + r''' +void test(bool a) { + if (a) { + print('hello'); + } else { + print('hello'); + } +} +''', + ); + } + + void test_does_not_report_if_else_return() async { + await assertNoDiagnostics( + r''' +void test(bool a) { + if (a) { + print('hello'); + } else { + return; + } +} +''', + ); + } + + void test_does_not_report_nested_if_else() async { + await assertNoDiagnostics( + r''' +void test(bool a, bool b) { + if (a) { + if(b){ + print('hello'); + } + } else { + print('hello'); + } +} +''', + ); + } + + void test_reports_inner_if_else() async { + await assertDiagnostics( + r''' +void test(bool a, bool b) { + if (a) { + if(b){ + print('hello'); + } + else { + print('hello'); + } + } +} +''', + [ + lint(30, 90), + ], + ); + } + + void test_does_not_report_nested_3_with_else_1() async { + await assertNoDiagnostics( + r''' +void test(bool a, bool b, bool c) { + if (a) { + if (b) { + if (c){ + print('nested'); + } + } + } else{ + print('hello'); + } +} +''', + ); + } + + void test_reports_nested_3_with_else_2() async { + await assertDiagnostics( + r''' +void test(bool a, bool b, bool c) { + if (a) { + if (b) { + if (c) { + print('nested'); + } + } else { + print('nested'); + } + } +} +''', + [ + lint(38, 115), + ], + ); + } + + void test_reports_nested_3_with_else_3() async { + await assertDiagnostics( + r''' +void test(bool a, bool b, bool c) { + if (a) { + if (b) { + if (c) { + print('nested'); + } + else { + print('nested'); + } + } + } +} +''', + [ + lint(38, 126), + ], + ); + } + + void test_reports_2_sequencial_if() async { + await assertDiagnostics( + r''' +void test(bool a, bool b) { + if (a) return; + if (b) { + print('gello'); + } +} +''', + [ + lint(47, 32), + ], + ); + } + + void test_does_not_report_2_sequencial_if_with_return() async { + await assertNoDiagnostics( + r''' +void test(bool a, bool b) { + if (a) return; + if (b) return; + + return; +} +''', + ); + } + + void test_reports_2_sequencial_if_with_return_2() async { + await assertDiagnostics( + r''' +void test(bool a, bool b) { + if (a) return; + if (b) { + print('hello'); + } + + return; +} +''', + [ + lint(47, 32), + ], + ); + } + + void test_does_not_report_2_sequencial_with_following_statement() async { + await assertNoDiagnostics( + r''' +void test(bool a, bool b) { + if (a) return; + if (b) { + print('hello'); + } + + print('after'); +} +''', + ); + } + + void test_reports_2_sequencial_if_with_something() async { + await assertDiagnostics( + r''' +void test(bool a, bool b) { + if (a) { + print('hello'); + } + if (b) { + print('hello'); + } +} +''', + [ + lint(65, 32), + ], + ); + } + + void test_reports_3_sequencial_if_with_return() async { + await assertDiagnostics( + r''' +void test(bool a, bool b, bool c) { + if (a) return; + if (b) return; + if (c) { + print('hello'); + } + + return; +} +''', + [ + lint(72, 32), + ], + ); + } + + void test_reports_3_sequencial_if_with_something_2() async { + await assertDiagnostics( + r''' +void test(bool a, bool b, bool c) { + if (a) return; + if (b) { + print('hello'); + } + if (c) { + print('hello'); + } +} +''', + [ + lint(90, 32), + ], + ); + } + + void test_does_not_report_if_throw_with_return() async { + await assertNoDiagnostics( + r''' +void test(bool a) { + if (a) { + throw ''; + } + + return; +} +''', + ); + } + + void test_does_not_report_if_else_throw() async { + await assertNoDiagnostics( + r''' +void test(bool a) { + if (a) { + print('hello'); + } else { + throw ''; + } +} +''', + ); + } + + void test_reports_2_sequencial_if_throw() async { + await assertDiagnostics( + r''' +void test(bool a, bool b) { + if (a) throw ''; + if (b) { + print('hello'); + } +} +''', + [ + lint(49, 32), + ], + ); + } + + void test_reports_2_sequencial_if_throw_with_return() async { + await assertDiagnostics( + r''' +void test(bool a, bool b) { + if (a) throw ''; + if (b) { + print('hello'); + } + + return; +} +''', + [ + lint(49, 32), + ], + ); + } + + void test_reports_3_sequencial_if_throw_with_return() async { + await assertDiagnostics( + r''' +void test(bool a, bool b, bool c) { + if (a) throw ''; + if (b) throw ''; + if (c) { + print('hello'); + } + + return; +} +''', + [ + lint(76, 32), + ], + ); + } + + void test_reports_3_sequencial_if_throw_with_something() async { + await assertDiagnostics( + r''' +void test(bool a, bool b, bool c) { + if (a) throw ''; + if (b) { + print('hello'); + } + if (c) { + print('hello'); + } +} +''', + [ + lint(92, 32), + ], + ); + } +}