class RuboCop::Cop::Style::CombinableLoops


end
each_slice(3) { |slice| do_something(slice) }
each_slice(2) { |slice| do_something(slice) }
def method
# good
end
end
do_something_else(item)
do_something(item)
for item in items do
def method
# good
end
end
do_something_else(item)
for item in items do
end
do_something(item)
for item in items do
def method
# bad
end
end
do_something_else(item)
do_something(item)
items.each do |item|
def method
# good
end
end
do_something_else(item)
items.each do |item|
end
do_something(item)
items.each do |item|
def method
# bad
@example
second loop depends on; these two aren’t combinable.
The cop is unsafe, because the first loop might modify state that the
@safety
will make the code more efficient and more concise.
can be combined into a single loop. It is very likely that combining them
Checks for places where multiple consecutive loops over the same data

def collection_looping_method?(node)

def collection_looping_method?(node)
  method_name = node.method_name
  method_name.start_with?('each') || method_name.end_with?('_each')
end

def combine_with_left_sibling(corrector, node)

def combine_with_left_sibling(corrector, node)
  corrector.replace(
    node.left_sibling.body,
    "#{node.left_sibling.body.source}\n#{node.body.source}"
  )
  corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
end

def on_block(node)

def on_block(node)
  return unless node.parent&.begin_type?
  return unless collection_looping_method?(node)
  return unless same_collection_looping_block?(node, node.left_sibling)
  add_offense(node) do |corrector|
    combine_with_left_sibling(corrector, node)
  end
end

def on_for(node)

def on_for(node)
  return unless node.parent&.begin_type?
  return unless same_collection_looping_for?(node, node.left_sibling)
  add_offense(node) do |corrector|
    combine_with_left_sibling(corrector, node)
  end
end

def same_collection_looping_block?(node, sibling)

def same_collection_looping_block?(node, sibling)
  (sibling&.block_type? || sibling&.numblock_type?) &&
    sibling.send_node.method?(node.method_name) &&
    sibling.receiver == node.receiver &&
    sibling.send_node.arguments == node.send_node.arguments
end

def same_collection_looping_for?(node, sibling)

def same_collection_looping_for?(node, sibling)
  sibling&.for_type? && node.collection == sibling.collection
end