class RuboCop::Cop::Performance::CaseWhenSplat

Place `when` conditions that use splat at the end of the list of `when` branches.

Ruby has to allocate memory for the splat expansion every time that the `case` `when` statement is run. Since Ruby does not support fall through inside of `case` `when`, like some other languages do, the order of the `when` branches does not matter. By placing any splat expansions at the end of the list of `when` branches we will reduce the number of times that memory has to be allocated for the expansion.

This is not a guaranteed performance improvement. If the data being processed by the `case` condition is normalized in a manner that favors hitting a condition in the splat expansion, it is possible that moving the splat condition to the end will use more memory, and run slightly slower.

@example

# bad
case foo
when *condition
  bar
when baz
  foobar
end

case foo
when *[1, 2, 3, 4]
  bar
when 5
  baz
end

# good
case foo
when baz
  foobar
when *condition
  bar
end

case foo
when 1, 2, 3, 4
  bar
when 5
  baz
end

Constants

ARRAY_MSG
MSG
PERCENT_CAPITAL_I
PERCENT_CAPITAL_W
PERCENT_I
PERCENT_W

Public Instance Methods

on_case(case_node) click to toggle source
# File lib/rubocop/cop/performance/case_when_splat.rb, line 64
def on_case(case_node)
  when_conditions = case_node.when_branches.flat_map(&:conditions)

  splat_offenses(when_conditions).reverse_each do |condition|
    range = condition.parent.loc.keyword.join(condition.source_range)
    variable, = *condition
    message = variable.array_type? ? ARRAY_MSG : MSG
    add_offense(condition.parent, location: range, message: message)
  end
end

Private Instance Methods

autocorrect(when_node) click to toggle source
# File lib/rubocop/cop/performance/case_when_splat.rb, line 77
def autocorrect(when_node)
  lambda do |corrector|
    if needs_reorder?(when_node)
      reorder_condition(corrector, when_node)
    else
      inline_fix_branch(corrector, when_node)
    end
  end
end
indent_for(node) click to toggle source
# File lib/rubocop/cop/performance/case_when_splat.rb, line 138
def indent_for(node)
  ' ' * node.loc.column
end
inline_fix_branch(corrector, when_node) click to toggle source
# File lib/rubocop/cop/performance/case_when_splat.rb, line 92
def inline_fix_branch(corrector, when_node)
  conditions = when_node.conditions
  range = range_between(conditions[0].loc.expression.begin_pos,
                        conditions[-1].loc.expression.end_pos)

  corrector.replace(range, replacement(conditions))
end
needs_reorder?(when_node) click to toggle source
# File lib/rubocop/cop/performance/case_when_splat.rb, line 163
def needs_reorder?(when_node)
  following_branches =
    when_node.parent.when_branches[(when_node.branch_index + 1)..-1]

  following_branches.any? do |when_branch|
    when_branch.conditions.any? do |condition|
      non_splat?(condition)
    end
  end
end
new_branch_without_then(node, new_condition) click to toggle source
# File lib/rubocop/cop/performance/case_when_splat.rb, line 133
def new_branch_without_then(node, new_condition)
  "\n#{indent_for(node)}when #{new_condition}" \
  "\n#{indent_for(node.body)}#{node.body.source}"
end
new_condition_with_then(node, new_condition) click to toggle source
# File lib/rubocop/cop/performance/case_when_splat.rb, line 128
def new_condition_with_then(node, new_condition)
  "\n#{indent_for(node)}when " \
  "#{new_condition} then #{node.body.source}"
end
non_splat?(condition) click to toggle source
# File lib/rubocop/cop/performance/case_when_splat.rb, line 156
def non_splat?(condition)
  variable, = *condition

  (condition.splat_type? && variable.array_type?) ||
    !condition.splat_type?
end
reorder_condition(corrector, when_node) click to toggle source
# File lib/rubocop/cop/performance/case_when_splat.rb, line 100
def reorder_condition(corrector, when_node)
  when_branches = when_node.parent.when_branches

  return if when_branches.one?

  corrector.remove(when_branch_range(when_node))
  corrector.insert_after(when_branches.last.source_range,
                         reordering_correction(when_node))
end
reordering_correction(when_node) click to toggle source
# File lib/rubocop/cop/performance/case_when_splat.rb, line 110
def reordering_correction(when_node)
  new_condition = replacement(when_node.conditions)

  if same_line?(when_node, when_node.body)
    new_condition_with_then(when_node, new_condition)
  else
    new_branch_without_then(when_node, new_condition)
  end
end
replacement(conditions) click to toggle source
# File lib/rubocop/cop/performance/case_when_splat.rb, line 87
def replacement(conditions)
  reordered = conditions.partition(&:splat_type?).reverse
  reordered.flatten.map(&:source).join(', ')
end
splat_offenses(when_conditions) click to toggle source
# File lib/rubocop/cop/performance/case_when_splat.rb, line 142
def splat_offenses(when_conditions)
  found_non_splat = false

  offenses = when_conditions.reverse.map do |condition|
    found_non_splat ||= non_splat?(condition)

    next if non_splat?(condition)

    condition if found_non_splat
  end

  offenses.compact
end
when_branch_range(when_node) click to toggle source
# File lib/rubocop/cop/performance/case_when_splat.rb, line 120
def when_branch_range(when_node)
  next_branch =
    when_node.parent.when_branches[when_node.branch_index + 1]

  range_between(when_node.source_range.begin_pos,
                next_branch.source_range.begin_pos)
end