class RuboCop::Cop::Style::RedundantEach


array.each_with_object { |v, o| do_something(v, o) }
array.each.with_object { |v, o| do_something(v, o) }
# good
array.each.each_with_object { |v, o| do_something(v, o) }
# bad
array.each_with_index { |v, i| do_something(v, i) }
array.each.with_index { |v, i| do_something(v, i) }
# good
array.each.each_with_index { |v, i| do_something(v, i) }
# bad
array.each { |v| do_something(v) }
# good
array.each.each { |v| do_something(v) }
# bad
@example
is not an ‘Enumerator`.
This cop is unsafe, as it can produce false positives if the receiver
@safety
Checks for redundant `each`.

def message(node)

def message(node)
  case node.method_name
  when :each
    MSG
  when :each_with_index
    MSG_WITH_INDEX
  when :each_with_object
    MSG_WITH_OBJECT
  end
end

def on_send(node)

def on_send(node)
  return unless (redundant_node = redundant_each_method(node))
  range = range(node)
  add_offense(range, message: message(node)) do |corrector|
    case node.method_name
    when :each
      remove_redundant_each(corrector, range, redundant_node)
    when :each_with_index
      corrector.replace(node.loc.selector, 'with_index')
    when :each_with_object
      corrector.replace(node.loc.selector, 'with_object')
    end
  end
end

def range(node)

def range(node)
  if node.method?(:each)
    node.loc.dot.join(node.loc.selector)
  else
    node.loc.selector
  end
end

def redundant_each_method(node)

rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
def redundant_each_method(node)
  return if node.last_argument&.block_pass_type?
  if node.method?(:each) && !node.parent&.block_type?
    ancestor_node = node.each_ancestor(:send).detect do |ancestor|
      ancestor.receiver == node &&
        (RESTRICT_ON_SEND.include?(ancestor.method_name) || ancestor.method?(:reverse_each))
    end
    return ancestor_node if ancestor_node
  end
  return unless (prev_method = node.children.first)
  return if !prev_method.send_type? ||
            prev_method.parent.block_type? || prev_method.last_argument&.block_pass_type?
  detected = prev_method.method_name.to_s.start_with?('each_') unless node.method?(:each)
  prev_method if detected || prev_method.method?(:reverse_each)
end

def remove_redundant_each(corrector, range, redundant_node)

def remove_redundant_each(corrector, range, redundant_node)
  corrector.remove(range)
  if redundant_node.method?(:each_with_index)
    corrector.replace(redundant_node.loc.selector, 'each.with_index')
  elsif redundant_node.method?(:each_with_object)
    corrector.replace(redundant_node.loc.selector, 'each.with_object')
  end
end