module Ameba::AST # A class that utilizes a logic to traverse AST nodes and # fire a source test callback if a redundant `Crystal::ControlExpression` # is reached. class RedundantControlExpressionVisitor # A corresponding rule that uses this visitor. getter rule : Rule::Base # A source that needs to be traversed. getter source : Source # A node to run traversal on. getter node : Crystal::ASTNode def initialize(@rule, @source, @node) traverse_node node end private def traverse_control_expression(node) @rule.test(@source, node, self) end private def traverse_node(node) case node when Crystal::ControlExpression then traverse_control_expression node when Crystal::Expressions then traverse_expressions node when Crystal::If, Crystal::Unless then traverse_condition node when Crystal::Case then traverse_case node when Crystal::BinaryOp then traverse_binary_op node when Crystal::ExceptionHandler then traverse_exception_handler node end end private def traverse_expressions(node) traverse_node node.expressions.last? end private def traverse_condition(node) return if node.else.nil? || node.else.nop? traverse_node(node.then) traverse_node(node.else) end private def traverse_case(node) node.whens.each { |when_node| traverse_node when_node.body } traverse_node(node.else) end private def traverse_binary_op(node) traverse_node(node.right) end private def traverse_exception_handler(node) traverse_node node.body traverse_node node.else node.rescues.try &.each { |rescue_node| traverse_node rescue_node.body } end end end