|
| 1 | +import 'package:analyzer/dart/ast/ast.dart'; |
| 2 | +import 'package:analyzer/dart/ast/visitor.dart'; |
| 3 | +import 'package:solid_lints/src/lints/prefer_early_return/visitors/return_statement_visitor.dart'; |
| 4 | + |
| 5 | +/// The AST visitor that will collect all unnecessary if statements |
| 6 | +class PreferEarlyReturnVisitor extends RecursiveAstVisitor<void> { |
| 7 | + final _nodes = <AstNode>[]; |
| 8 | + |
| 9 | + /// All unnecessary if statements and conditional expressions. |
| 10 | + Iterable<AstNode> get nodes => _nodes; |
| 11 | + |
| 12 | + @override |
| 13 | + void visitBlockFunctionBody(BlockFunctionBody node) { |
| 14 | + super.visitBlockFunctionBody(node); |
| 15 | + |
| 16 | + if (node.block.statements.isEmpty) return; |
| 17 | + |
| 18 | + final (ifStatements, nextStatement) = _getStartIfStatements(node); |
| 19 | + if (ifStatements.isEmpty) return; |
| 20 | + |
| 21 | + // limit visitor to only work with functions |
| 22 | + // that don't have a return statement or the return statement is empty |
| 23 | + final nextStatementIsEmptyReturn = |
| 24 | + nextStatement is ReturnStatement && nextStatement.expression == null; |
| 25 | + final nextStatementIsNull = nextStatement == null; |
| 26 | + |
| 27 | + if (!nextStatementIsEmptyReturn && !nextStatementIsNull) return; |
| 28 | + |
| 29 | + _handleIfStatement(ifStatements.last); |
| 30 | + } |
| 31 | + |
| 32 | + void _handleIfStatement(IfStatement node) { |
| 33 | + if (_isElseIfStatement(node)) return; |
| 34 | + if (_hasElseStatement(node)) return; |
| 35 | + if (_hasReturnStatement(node)) return; |
| 36 | + |
| 37 | + _nodes.add(node); |
| 38 | + } |
| 39 | + |
| 40 | +// returns a list of if statements at the start of the function |
| 41 | +// and the next statement after it |
| 42 | +// examples: |
| 43 | +// [if, if, if, return] -> ([if, if, if], return) |
| 44 | +// [if, if, if, _doSomething, return] -> ([if, if, if], _doSomething) |
| 45 | +// [if, if, if] -> ([if, if, if], null) |
| 46 | + (List<IfStatement>, Statement?) _getStartIfStatements( |
| 47 | + BlockFunctionBody body, |
| 48 | + ) { |
| 49 | + final List<IfStatement> ifStatements = []; |
| 50 | + for (final statement in body.block.statements) { |
| 51 | + if (statement is IfStatement) { |
| 52 | + ifStatements.add(statement); |
| 53 | + } else { |
| 54 | + return (ifStatements, statement); |
| 55 | + } |
| 56 | + } |
| 57 | + return (ifStatements, null); |
| 58 | + } |
| 59 | + |
| 60 | + bool _hasElseStatement(IfStatement node) { |
| 61 | + return node.elseStatement != null; |
| 62 | + } |
| 63 | + |
| 64 | + bool _isElseIfStatement(IfStatement node) { |
| 65 | + return node.elseStatement != null && node.elseStatement is IfStatement; |
| 66 | + } |
| 67 | + |
| 68 | + bool _hasReturnStatement(Statement node) { |
| 69 | + final visitor = ReturnStatementVisitor(); |
| 70 | + node.accept(visitor); |
| 71 | + return visitor.nodes.isNotEmpty; |
| 72 | + } |
| 73 | +} |
0 commit comments