From a8e03365afa01ae61df8dd00cf8b264d9313fc67 Mon Sep 17 00:00:00 2001 From: Islam-Shaaban-Ibrahim Date: Thu, 16 Apr 2026 15:31:11 +0200 Subject: [PATCH 1/6] migrate prefer early return rule with tests --- .../prefer_early_return_rule.dart | 20 +- .../visitors/prefer_early_return_visitor.dart | 86 ++--- lint_test/prefer_early_return_test.dart | 299 ------------------ test/prefer_early_return_rule_test.dart | 91 ++++++ 4 files changed, 122 insertions(+), 374 deletions(-) delete mode 100644 lint_test/prefer_early_return_test.dart create mode 100644 test/prefer_early_return_rule_test.dart 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 4f420de0..812841f8 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 @@ -32,23 +32,20 @@ import 'package:solid_lints/src/lints/prefer_early_return/visitors/prefer_early_ /// } /// ``` class PreferEarlyReturnRule extends AnalysisRule { - /// The name of the lint + /// Lint name static const String lintName = 'prefer_early_return'; - /// 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, + "Use reverse if to reduce nesting", ); - /// Creates a new instance of [PreferEarlyReturnRule] + /// Creates an instance of [PreferEarlyReturnRule] PreferEarlyReturnRule() : super( name: lintName, - description: lintMessage, + description: 'Use reverse if to reduce nesting', ); @override @@ -59,7 +56,12 @@ class PreferEarlyReturnRule extends AnalysisRule { RuleVisitorRegistry registry, RuleContext context, ) { - final visitor = PreferEarlyReturnVisitor(this); - registry.addBlockFunctionBody(this, visitor); + registry.addBlockFunctionBody( + this, + PreferEarlyReturnVisitor( + rule: this, + context: context, + ), + ); } } 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 a8342ef5..12e984ba 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,83 +1,37 @@ +import 'package:analyzer/analysis_rule/rule_context.dart'; 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'; /// Visitor for [PreferEarlyReturnRule]. class PreferEarlyReturnVisitor extends RecursiveAstVisitor { - /// The rule associated with this visitor. + /// The rule associated with the visitor. final PreferEarlyReturnRule rule; - /// Creates an instance of [PreferEarlyReturnVisitor]. - PreferEarlyReturnVisitor(this.rule); + /// The context associated with the visitor. + final RuleContext context; - @override - void visitBlockFunctionBody(BlockFunctionBody node) { - super.visitBlockFunctionBody(node); - - if (node.block.statements.isEmpty) return; - - final (ifStatements, nextStatement) = _getStartIfStatements(node); - if (ifStatements.isEmpty) return; - - // limit visitor to only work with functions - // that don't have a return statement or the return statement is empty - final nextStatementIsEmptyReturn = - nextStatement is ReturnStatement && nextStatement.expression == null; - final nextStatementIsNull = nextStatement == null; - - if (!nextStatementIsEmptyReturn && !nextStatementIsNull) return; - - _handleIfStatement(ifStatements.last); - } - - void _handleIfStatement(IfStatement node) { - if (_isElseIfStatement(node)) return; - if (_hasElseStatement(node)) return; - if (_hasReturnStatement(node)) return; - if (_hasThrowExpression(node)) return; + /// Constructor for [PreferEarlyReturnVisitor]. + PreferEarlyReturnVisitor({ + required this.rule, + required this.context, + }); - rule.reportAtNode(node); - } - -// returns a list of if statements at the start of the function -// and the next statement after it -// examples: -// [if, if, if, return] -> ([if, if, if], return) -// [if, if, if, _doSomething, return] -> ([if, if, if], _doSomething) -// [if, if, if] -> ([if, if, if], null) - (List, Statement?) _getStartIfStatements( - BlockFunctionBody body, - ) { - final List ifStatements = []; - for (final statement in body.block.statements) { - if (statement is IfStatement) { - ifStatements.add(statement); - } else { - return (ifStatements, statement); - } + @override + void visitIfStatement(IfStatement node) { + if (_shouldReport(node)) { + context.currentUnit?.diagnosticReporter.atNode( + node, + rule.diagnosticCode, + ); } - return (ifStatements, null); - } - bool _hasElseStatement(IfStatement node) { - return node.elseStatement != null; + super.visitIfStatement(node); } - bool _isElseIfStatement(IfStatement node) { - return node.elseStatement != null && node.elseStatement is IfStatement; - } - - bool _hasReturnStatement(Statement node) { - final visitor = ReturnStatementVisitor(); - node.accept(visitor); - return visitor.nodes.isNotEmpty; - } + bool _shouldReport(IfStatement node) { + final parent = node.parent; - bool _hasThrowExpression(Statement node) { - final visitor = ThrowExpressionVisitor(); - node.accept(visitor); - return visitor.nodes.isNotEmpty; + return parent is Block && parent.statements.length == 1; } } diff --git a/lint_test/prefer_early_return_test.dart b/lint_test/prefer_early_return_test.dart deleted file mode 100644 index d9108186..00000000 --- a/lint_test/prefer_early_return_test.dart +++ /dev/null @@ -1,299 +0,0 @@ -// ignore_for_file: dead_code, cyclomatic_complexity, no_equal_then_else, prefer_match_file_name - -// ignore: no_empty_block -void _doSomething() {} - -void oneIf() { - //expect_lint: prefer_early_return - if (true) { - _doSomething(); - } -} - -int oneIfWithReturnValue() { - //no lint - if (true) { - _doSomething(); - } - - return 1; -} - -void oneIfWithReturn() { - //expect_lint: prefer_early_return - if (true) { - _doSomething(); - } - - return; -} - -void nestedIf2() { - //expect_lint: prefer_early_return - if (true) { - if (true) { - _doSomething(); - } - } -} - -int nestedIf2WithReturnValue() { - //no lint - if (true) { - if (true) { - _doSomething(); - } - } - - return 1; -} - -void nestedIf3() { - //expect_lint: prefer_early_return - if (true) { - if (true) { - if (true) { - _doSomething(); - } - } - } -} - -void oneNestedIf2WithReturn() { - //expect_lint: prefer_early_return - if (true) { - if (true) { - if (true) { - _doSomething(); - } - } - } - - return; -} - -void oneIfElse() { - //no lint - if (true) { - _doSomething(); - } else { - _doSomething(); - } -} - -void oneIfElseReturn() { - //no lint - if (true) { - _doSomething(); - } else { - return; - } -} - -void twoIfElse() { - //no lint - if (true) { - if (true) { - _doSomething(); - } - } else { - _doSomething(); - } -} - -void twoIfElseInner() { - //expect_lint: prefer_early_return - if (true) { - if (true) { - _doSomething(); - } else { - _doSomething(); - } - } -} - -void threeIf() { - //expect_lint: prefer_early_return - if (true) { - if (true) { - if (true) { - _doSomething(); - } - } - } -} - -void threeIfElse1() { - //no lint - if (true) { - if (true) { - if (true) { - _doSomething(); - } - } - } else { - _doSomething(); - } -} - -void threeIfElse2() { - //expect_lint: prefer_early_return - if (true) { - if (true) { - if (true) { - _doSomething(); - } - } else { - _doSomething(); - } - } -} - -void threeIfElse3() { - //expect_lint: prefer_early_return - if (true) { - if (true) { - if (true) { - _doSomething(); - } else { - _doSomething(); - } - } - } -} - -void twoSeqentialIf() { - if (false) return; - //expect_lint: prefer_early_return - if (true) { - _doSomething(); - } -} - -void twoSeqentialIfReturn() { - //no lint - if (false) return; - if (true) return; - - return; -} - -void twoSeqentialIfReturn2() { - //no lint - if (false) return; - //expect_lint: prefer_early_return - if (true) { - _doSomething(); - } - - return; -} - -void twoSeqentialIfSomething() { - //no lint - if (false) return; - //no lint - if (true) { - _doSomething(); - } - - _doSomething(); -} - -void twoSeqentialIfSomething2() { - //no lint - if (true) { - _doSomething(); - } - //expect_lint: prefer_early_return - if (true) { - _doSomething(); - } -} - -void threeSeqentialIfReturn() { - //no lint - if (false) return; - if (true) return; - //expect_lint: prefer_early_return - if (true) { - _doSomething(); - } - - return; -} - -void threeSeqentialIfReturn2() { - //no lint - if (false) return; - //no lint - if (true) { - _doSomething(); - } - //expect_lint: prefer_early_return - if (true) { - _doSomething(); - } -} - -void oneIfWithThrowWithReturn() { - //no lint - if (true) { - throw ''; - } - - return; -} - -void oneIfElseWithThrowReturn() { - //no lint - if (true) { - _doSomething(); - } else { - throw ''; - } -} - -void twoSeqentialIfWithThrow() { - if (false) throw ''; - //expect_lint: prefer_early_return - if (true) { - _doSomething(); - } -} - -void twoSeqentialIfWithThrowReturn2() { - //no lint - if (false) throw ''; - //expect_lint: prefer_early_return - if (true) { - _doSomething(); - } - - return; -} - -void threeSeqentialIfWithThrowReturn() { - //no lint - if (false) throw ''; - if (true) throw ''; - //expect_lint: prefer_early_return - if (true) { - _doSomething(); - } - - return; -} - -void threeSeqentialIfWithThrowReturn2() { - //no lint - if (false) throw ''; - //no lint - if (true) { - _doSomething(); - } - //expect_lint: prefer_early_return - if (true) { - _doSomething(); - } -} diff --git a/test/prefer_early_return_rule_test.dart b/test/prefer_early_return_rule_test.dart new file mode 100644 index 00000000..46bb94ca --- /dev/null +++ b/test/prefer_early_return_rule_test.dart @@ -0,0 +1,91 @@ +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_nested_if_as_only_statement() async { + await assertDiagnostics( + r''' +void test(bool a, bool b) { + if (a) { + if (b) { + print('nested'); + } + } +} +''', + [ + lint(30, 54), + lint(43, 37), + ], + ); + } + + void test_does_not_report_if_with_following_statement() async { + await assertNoDiagnostics( + r''' +void test(bool a) { + if (a) { + print('hello'); + } + + print('after'); +} +''', + ); + } + + void test_does_not_report_when_multiple_statements_exist() async { + await assertNoDiagnostics( + r''' +void test(bool a) { + print('before'); + + if (a) { + print('hello'); + } +} +''', + ); + } + + void test_does_not_report_regular_inline_if() async { + await assertNoDiagnostics( + r''' +void test(bool a) { + if (a) print('hello'); + print('done'); +} +''', + ); + } +} From 9048b406c3a9070173cac42b2c1d20a2e4056034 Mon Sep 17 00:00:00 2001 From: Andrew Bekhiet Date: Tue, 2 Jun 2026 14:54:01 +0300 Subject: [PATCH 2/6] fix: remove flutter dependency to allow running reflective tests --- pubspec.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index f85b1309..801124c4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,9 +17,6 @@ dependencies: glob: ^2.1.3 path: ^1.9.1 yaml: ^3.1.3 - # These packages are required for pana analysis to run correctly - flutter: - sdk: flutter test: ^1.25.14 dev_dependencies: From 61eef7e4e5ec664bdb34540f0cfd0153906a00be Mon Sep 17 00:00:00 2001 From: Andrew Bekhiet Date: Tue, 2 Jun 2026 16:39:55 +0300 Subject: [PATCH 3/6] test: migrate all old tests --- test/prefer_early_return_rule_test.dart | 406 ++++++++++++++++++++++-- 1 file changed, 371 insertions(+), 35 deletions(-) diff --git a/test/prefer_early_return_rule_test.dart b/test/prefer_early_return_rule_test.dart index 46bb94ca..c86b2ed8 100644 --- a/test/prefer_early_return_rule_test.dart +++ b/test/prefer_early_return_rule_test.dart @@ -19,73 +19,409 @@ class PreferEarlyReturnRuleTest extends AnalysisRuleTest { @override String get analysisRule => PreferEarlyReturnRule.lintName; - void test_reports_if_as_only_statement_in_function() async { + Future test_reports_on_one_if() async { await assertDiagnostics( r''' -void test(bool a) { +void oneIf(bool a) { if (a) { - print('hello'); + print('s'); } -} -''', - [lint(22, 32)], +}''', + [lint(23, 28)], ); } - void test_reports_nested_if_as_only_statement() async { + Future test_doesn_not_report_on_one_if_with_return_value() async { + await assertNoDiagnostics(r''' +int oneIfWithReturnValue(bool a) { + if (a) { + print('s'); + } + + return 1; +}'''); + } + + Future test_reports_on_one_if_with_return() async { await assertDiagnostics( r''' -void test(bool a, bool b) { +void oneIfWithReturn(bool a) { + if (a) { + print('s'); + } + + return; +}''', + [lint(33, 28)], + ); + } + + Future test_reports_on_nested_if2() async { + await assertDiagnostics( + r''' +void nestedIf2(bool a, bool b) { if (a) { if (b) { - print('nested'); + print('s'); } } -} -''', - [ - lint(30, 54), - lint(43, 37), - ], +}''', + [lint(35, 49)], ); } - void test_does_not_report_if_with_following_statement() async { + Future test_doesn_not_report_on_nested_if2_with_return_value() async { await assertNoDiagnostics( r''' -void test(bool a) { +int nestedIf2WithReturnValue(bool a, bool b) { if (a) { - print('hello'); + if (b) { + print('s'); + } } - print('after'); -} -''', + return 1; +}''', ); } - void test_does_not_report_when_multiple_statements_exist() async { - await assertNoDiagnostics( + Future test_reports_on_nested_if3() async { + await assertDiagnostics( r''' -void test(bool a) { - print('before'); +void nestedIf3(bool a, bool b, bool c) { + if (a) { + if (b) { + if (c) { + print('s'); + } + } + } +}''', + [lint(43, 74)], + ); + } + Future test_reports_on_one_nested_if2_with_return() async { + await assertDiagnostics( + r''' +void oneNestedIf2WithReturn(bool a, bool b, bool c) { if (a) { - print('hello'); + if (b) { + if (c) { + print('s'); + } + } } -} -''', + + return; +}''', + [lint(56, 74)], ); } - void test_does_not_report_regular_inline_if() async { - await assertNoDiagnostics( + Future test_doesn_not_report_on_one_if_else() async { + await assertNoDiagnostics(r''' +void oneIfElse(bool a) { + if (a) { + print('s'); + } else { + print('s'); + } +}'''); + } + + Future test_doesn_not_report_on_one_if_else_return() async { + await assertNoDiagnostics(r''' +void oneIfElseReturn(bool a) { + if (a) { + print('s'); + } else { + return; + } +}'''); + } + + Future test_doesn_not_report_on_two_if_else() async { + await assertNoDiagnostics(r''' +void twoIfElse(bool a, bool b) { + if (a) { + if (b) { + print('s'); + } + } else { + print('s'); + } +}'''); + } + + Future test_reports_on_two_if_else_inner() async { + await assertDiagnostics( r''' -void test(bool a) { - if (a) print('hello'); - print('done'); -} -''', +void twoIfElseInner(bool a, bool b) { + if (a) { + if (b) { + print('s'); + } else { + print('s'); + } + } +}''', + [lint(40, 80)], + ); + } + + Future test_reports_on_three_if() async { + await assertDiagnostics( + r''' +void threeIf(bool a, bool b, bool c) { + if (a) { + if (b) { + if (c) { + print('s'); + } + } + } +}''', + [lint(41, 74)], + ); + } + + Future test_doesn_not_report_on_three_if_else1() async { + await assertNoDiagnostics(r''' +void threeIfElse1(bool a, bool b, bool c) { + if (a) { + if (b) { + if (c) { + print('s'); + } + } + } else { + print('s'); + } +}'''); + } + + Future test_reports_on_three_if_else2() async { + await assertDiagnostics( + r''' +void threeIfElse2(bool a, bool b, bool c) { + if (a) { + if (b) { + if (c) { + print('s'); + } + } else { + print('s'); + } + } +}''', + [lint(46, 105)], + ); + } + + Future test_reports_on_three_if_else3() async { + await assertDiagnostics( + r''' +void threeIfElse3(bool a, bool b, bool c) { + if (a) { + if (b) { + if (c) { + print('s'); + } else { + print('s'); + } + } + } +}''', + [lint(46, 109)], + ); + } + + Future test_reports_on_two_seqential_if() async { + await assertDiagnostics( + r''' +void twoSeqentialIf(bool a, bool b) { + if (a) return; + if (b) { + print('s'); + } +}''', + [lint(57, 28)], + ); + } + + Future test_doesn_not_report_on_two_seqential_if_return() async { + await assertNoDiagnostics(r''' +void twoSeqentialIfReturn(bool a, bool b) { + if (a) return; + if (b) return; + + return; +}'''); + } + + Future test_reports_on_two_seqential_if_return2() async { + await assertDiagnostics( + r''' +void twoSeqentialIfReturn2(bool a, bool b) { + //no lint + if (a) return; + if (b) { + print('s'); + } + + return; +}''', + [lint(76, 28)], + ); + } + + Future test_doesn_not_report_on_two_seqential_if_something() async { + await assertNoDiagnostics(r''' +void twoSeqentialIfSomething(bool a, bool b) { + if (a) return; + if (b) { + print('s'); + } + + print('s'); +}'''); + } + + Future test_reports_on_two_seqential_if_something2() async { + await assertDiagnostics( + r''' +void twoSeqentialIfSomething2(bool a, bool b) { + //no lint + if (a) { + print('s'); + } + if (b) { + print('s'); + } +}''', + [lint(93, 28)], + ); + } + + Future test_reports_on_three_seqential_if_return() async { + await assertDiagnostics( + r''' +void threeSeqentialIfReturn(bool a, bool b, bool c) { + //no lint + if (a) return; + if (b) return; + if (c) { + print('s'); + } + + return; +}''', + [lint(102, 28)], + ); + } + + Future test_reports_on_three_seqential_if_return2() async { + await assertDiagnostics( + r''' +void threeSeqentialIfReturn2(bool a, bool b, bool c) { + //no lint + if (a) return; + //no lint + if (b) { + print('s'); + } + if (c) { + print('s'); + } +}''', + [lint(129, 28)], + ); + } + + Future test_doesn_not_report_on_one_if_with_throw_with_return() async { + await assertNoDiagnostics(r''' +void oneIfWithThrowWithReturn(bool a) { + if (a) { + throw ''; + } + + return; +}'''); + } + + Future test_doesn_not_report_on_one_if_else_with_throw_return() async { + await assertNoDiagnostics(r''' +void oneIfElseWithThrowReturn(bool a) { + if (a) { + print('s'); + } else { + throw ''; + } +}'''); + } + + Future test_reports_on_two_seqential_if_with_throw() async { + await assertDiagnostics( + r''' +void twoSeqentialIfWithThrow(bool a, bool b) { + if (a) throw ''; + if (b) { + print('s'); + } +}''', + [lint(68, 28)], + ); + } + + Future test_reports_on_two_seqential_if_with_throw_return2() async { + await assertDiagnostics( + r''' +void twoSeqentialIfWithThrowReturn2(bool a, bool b) { + //no lint + if (a) throw ''; + if (b) { + print('s'); + } + + return; +}''', + [lint(87, 28)], + ); + } + + Future test_reports_on_three_seqential_if_with_throw_return() async { + await assertDiagnostics( + r''' +void threeSeqentialIfWithThrowReturn(bool a, bool b, bool c) { + //no lint + if (a) throw ''; + if (b) throw ''; + if (c) { + print('s'); + } + + return; +}''', + [lint(115, 28)], + ); + } + + Future test_reports_on_three_seqential_if_with_throw_return2() async { + await assertDiagnostics( + r''' +void threeSeqentialIfWithThrowReturn2(bool a, bool b, bool c) { + //no lint + if (a) throw ''; + //no lint + if (b) { + print('s'); + } + if (c) { + print('s'); + } +}''', + [lint(140, 28)], ); } } From 70991ee7ce4345aeb7d7bf243fe1ecf649b31339 Mon Sep 17 00:00:00 2001 From: Andrew Bekhiet Date: Tue, 2 Jun 2026 16:47:14 +0300 Subject: [PATCH 4/6] feat: migrate old lint code improve readability when possible --- .../visitors/prefer_early_return_visitor.dart | 67 ++++++++++++++++--- 1 file changed, 57 insertions(+), 10 deletions(-) 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 12e984ba..eb30616c 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 @@ -2,6 +2,8 @@ import 'package:analyzer/analysis_rule/rule_context.dart'; 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'; /// Visitor for [PreferEarlyReturnRule]. class PreferEarlyReturnVisitor extends RecursiveAstVisitor { @@ -18,20 +20,65 @@ class PreferEarlyReturnVisitor extends RecursiveAstVisitor { }); @override - void visitIfStatement(IfStatement node) { - if (_shouldReport(node)) { - context.currentUnit?.diagnosticReporter.atNode( - node, - rule.diagnosticCode, - ); + void visitBlockFunctionBody(BlockFunctionBody node) { + super.visitBlockFunctionBody(node); + + if (node.block.statements.isEmpty) return; + + final (ifStatements, nextStatement) = _getIfStatementsAndNextStatement( + node, + ); + if (ifStatements.isEmpty) return; + + // limit visitor to only work with functions + // that don't have a return statement or the return statement is empty + final nextStatementIsEmptyReturn = + nextStatement is ReturnStatement && nextStatement.expression == null; + final nextStatementIsNull = nextStatement == null; + + if (!nextStatementIsEmptyReturn && !nextStatementIsNull) return; + + final lastIf = ifStatements.last; + + if (lastIf case IfStatement(elseStatement: Statement())) return; + if (_hasReturnStatement(lastIf) || _hasThrowExpression(lastIf)) return; + + context.currentUnit?.diagnosticReporter.atNode( + lastIf, + rule.diagnosticCode, + ); + } + + // returns a list of if statements at the start of the function + // and the next statement after it + // examples: + // [if, if, if, return] -> ([if, if, if], return) + // [if, if, if, _doSomething, return] -> ([if, if, if], _doSomething) + // [if, if, if] -> ([if, if, if], null) + (List, Statement?) _getIfStatementsAndNextStatement( + BlockFunctionBody body, + ) { + final List ifStatements = []; + for (final statement in body.block.statements) { + if (statement is IfStatement) { + ifStatements.add(statement); + } else { + return (ifStatements, statement); + } } - super.visitIfStatement(node); + return (ifStatements, null); } - bool _shouldReport(IfStatement node) { - final parent = node.parent; + bool _hasReturnStatement(Statement node) { + final visitor = ReturnStatementVisitor(); + node.accept(visitor); + return visitor.nodes.isNotEmpty; + } - return parent is Block && parent.statements.length == 1; + bool _hasThrowExpression(Statement node) { + final visitor = ThrowExpressionVisitor(); + node.accept(visitor); + return visitor.nodes.isNotEmpty; } } From f1863d5bafb1d1a94543799352c47d1becdc292d Mon Sep 17 00:00:00 2001 From: Andrew Bekhiet Date: Tue, 2 Jun 2026 16:48:09 +0300 Subject: [PATCH 5/6] refactor: extract variable for readability --- .../prefer_early_return/prefer_early_return_rule.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) 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 812841f8..582378df 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 @@ -56,12 +56,11 @@ class PreferEarlyReturnRule extends AnalysisRule { RuleVisitorRegistry registry, RuleContext context, ) { - registry.addBlockFunctionBody( - this, - PreferEarlyReturnVisitor( - rule: this, - context: context, - ), + final visitor = PreferEarlyReturnVisitor( + rule: this, + context: context, ); + + registry.addBlockFunctionBody(this, visitor); } } From 7304ea633e9effc98c4f17db5fe160e4ae497e0d Mon Sep 17 00:00:00 2001 From: Andrew Bekhiet Date: Tue, 2 Jun 2026 16:51:07 +0300 Subject: [PATCH 6/6] style: formatting --- test/prefer_early_return_rule_test.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/prefer_early_return_rule_test.dart b/test/prefer_early_return_rule_test.dart index c86b2ed8..895b91cd 100644 --- a/test/prefer_early_return_rule_test.dart +++ b/test/prefer_early_return_rule_test.dart @@ -71,8 +71,7 @@ void nestedIf2(bool a, bool b) { } Future test_doesn_not_report_on_nested_if2_with_return_value() async { - await assertNoDiagnostics( - r''' + await assertNoDiagnostics(r''' int nestedIf2WithReturnValue(bool a, bool b) { if (a) { if (b) { @@ -81,8 +80,7 @@ int nestedIf2WithReturnValue(bool a, bool b) { } return 1; -}''', - ); +}'''); } Future test_reports_on_nested_if3() async {