class RuboCop::Cop::Style::CaseLikeIf
end
final_action
else
report_invalid
elsif status == :invalid
check_timeout
elsif status == :inactive || status == :hibernating
perform_action
if status == :active
# good
@example MinBranchesCount: 4
end
final_action
else
report_invalid
when :invalid
check_timeout
when :inactive, :hibernating
perform_action
when :active
case status
# good
end
final_action
else
report_invalid
elsif status == :invalid
check_timeout
elsif status == :inactive || status == :hibernating
perform_action
if status == :active
# bad
@example MinBranchesCount: 3 (default)
behavior may be different.
so if the original conditional used a different equality operator, the
This cop is unsafe. ‘case` statements use `===` for equality,
@safety
can be replaced with `case-when`.
Identifies places where `if-elsif` constructions
def autocorrect(corrector, node)
def autocorrect(corrector, node) target = find_target(node.condition) corrector.insert_before(node, "case #{target.source}\n#{indent(node)}") branch_conditions(node).each do |branch_condition| conditions = [] collect_conditions(branch_condition, target, conditions) range = correction_range(branch_condition) branch_replacement = "when #{conditions.map(&:source).join(', ')}" corrector.replace(range, branch_replacement) end end
def branch_conditions(node)
def branch_conditions(node) conditions = [] while node&.if_type? conditions << node.condition node = node.else_branch end conditions end
def class_reference?(node)
def class_reference?(node) node.const_type? && node.children[1].match?(/[[:lower:]]/) end
def collect_conditions(node, target, conditions)
def collect_conditions(node, target, conditions) condition = case node.type when :begin return collect_conditions(node.children.first, target, conditions) when :or return collect_conditions(node.lhs, target, conditions) && collect_conditions(node.rhs, target, conditions) when :match_with_lvasgn lhs, rhs = *node condition_from_binary_op(lhs, rhs, target) when :send condition_from_send_node(node, target) end conditions << condition if condition end
def condition_from_binary_op(lhs, rhs, target)
def condition_from_binary_op(lhs, rhs, target) lhs = deparenthesize(lhs) rhs = deparenthesize(rhs) if lhs == target rhs elsif rhs == target lhs end end
def condition_from_equality_node(node, target)
def condition_from_equality_node(node, target) lhs, _method, rhs = *node condition = condition_from_binary_op(lhs, rhs, target) condition if condition && !class_reference?(condition) end
def condition_from_include_or_cover_node(node, target)
def condition_from_include_or_cover_node(node, target) return unless (receiver = node.receiver) receiver = deparenthesize(receiver) receiver if receiver.range_type? && node.first_argument == target end
def condition_from_match_node(node, target)
def condition_from_match_node(node, target) lhs, _method, rhs = *node condition_from_binary_op(lhs, rhs, target) end
def condition_from_send_node(node, target)
def condition_from_send_node(node, target) case node.method_name when :is_a? node.arguments.first if node.receiver == target when :==, :eql?, :equal? condition_from_equality_node(node, target) when :=~, :match, :match? condition_from_match_node(node, target) when :=== lhs, _method, rhs = *node lhs if rhs == target when :include?, :cover? condition_from_include_or_cover_node(node, target) end end
def const_reference?(node)
def const_reference?(node) return false unless node.const_type? name = node.children[1].to_s # We can no be sure if, e.g. `C`, represents a constant or a class reference name.length > 1 && name == name.upcase end
def correction_range(node)
def correction_range(node) range_between(node.parent.loc.keyword.begin_pos, node.source_range.end_pos) end
def deparenthesize(node)
def deparenthesize(node) node = node.children.last while node.begin_type? node end
def find_target(node)
def find_target(node) case node.type when :begin find_target(node.children.first) when :or find_target(node.lhs) when :match_with_lvasgn lhs, rhs = *node if lhs.regexp_type? rhs elsif rhs.regexp_type? lhs end when :send find_target_in_send_node(node) end end
def find_target_in_equality_node(node)
def find_target_in_equality_node(node) argument = node.arguments.first receiver = node.receiver return unless argument && receiver if argument.literal? || const_reference?(argument) receiver elsif receiver.literal? || const_reference?(receiver) argument end end
def find_target_in_include_or_cover_node(node)
def find_target_in_include_or_cover_node(node) return unless (receiver = node.receiver) node.first_argument if deparenthesize(receiver).range_type? end
def find_target_in_match_node(node)
def find_target_in_match_node(node) argument = node.arguments.first receiver = node.receiver return unless receiver if receiver.regexp_type? argument elsif argument.regexp_type? receiver end end
def find_target_in_send_node(node)
def find_target_in_send_node(node) case node.method_name when :is_a? node.receiver when :==, :eql?, :equal? find_target_in_equality_node(node) when :=== node.arguments.first when :include?, :cover? find_target_in_include_or_cover_node(node) when :match, :match?, :=~ find_target_in_match_node(node) end end
def on_if(node)
def on_if(node) return unless should_check?(node) target = find_target(node.condition) return unless target conditions = [] convertible = true branch_conditions(node).each do |branch_condition| return false if regexp_with_working_captures?(branch_condition) conditions << [] convertible = collect_conditions(branch_condition, target, conditions.last) break unless convertible end return unless convertible add_offense(node) { |corrector| autocorrect(corrector, node) } end
def regexp_with_named_captures?(node)
def regexp_with_named_captures?(node) node.regexp_type? && node.each_capture(named: true).count.positive? end
def regexp_with_working_captures?(node)
def regexp_with_working_captures?(node) case node.type when :match_with_lvasgn lhs, _rhs = *node node.loc.selector.source == '=~' && regexp_with_named_captures?(lhs) when :send lhs, method, rhs = *node method == :match && [lhs, rhs].any? { |n| regexp_with_named_captures?(n) } end end
def should_check?(node)
def should_check?(node) !node.unless? && !node.elsif? && !node.modifier_form? && !node.ternary? && node.elsif_conditional? && min_branches_count?(node) end