class RuboCop::Cop::Style::ParallelAssignment

c = 3
b = 2
a = 1
a, b = b, a
a, b = foo()
one, two = *foo
# good
a, b, c = [1, 2, 3]
a, b, c = 1, 2, 3
# bad
@example
being assigned matched the number of assigning variables.
This will only complain when the number of variables
Checks for simple usages of parallel assignment.

def autocorrect(node)

def autocorrect(node)
  lambda do |corrector|
    left, right = *node
    left_elements = *left
    right_elements = [*right].compact
    order = find_valid_order(left_elements, right_elements)
    assignment_corrector =
      if modifier_statement?(node.parent)
        ModifierCorrector.new(node, config, order)
      elsif rescue_modifier?(node.parent)
        RescueCorrector.new(node, config, order)
      else
        GenericCorrector.new(node, config, order)
      end
    corrector.replace(assignment_corrector.correction_range,
                      assignment_corrector.correction)
  end
end

def find_valid_order(left_elements, right_elements)

def find_valid_order(left_elements, right_elements)
  # arrange left_elements in an order such that no corresponding right
  # element refers to a left element earlier in the sequence
  # this can be done using an algorithm called a "topological sort"
  # fortunately for us, Ruby's stdlib contains an implementation
  assignments = left_elements.zip(right_elements)
  begin
    AssignmentSorter.new(assignments).tsort
  rescue TSort::Cyclic
    nil
  end
end

def modifier_statement?(node)

def modifier_statement?(node)
  node &&
    ((node.if_type? && modifier_if?(node)) ||
    ((node.while_type? || node.until_type?) && modifier_while?(node)))
end

def modifier_while?(node)

def modifier_while?(node)
  node.loc.respond_to?(:keyword) &&
    %w(while until).include?(node.loc.keyword.source) &&
    node.loc.respond_to?(:end) && node.loc.end.nil?
end

def on_masgn(node)

def on_masgn(node)
  left, right = *node
  left_elements = *left
  right_elements = [*right].compact # edge case for one constant
  # only complain when the number of variables matches
  return if left_elements.size != right_elements.size
  # account for edge cases using one variable with a comma
  return if left_elements.size == 1
  # account for edge case of Constant::CONSTANT
  return unless right.array_type?
  # allow mass assignment as the return of a method call
  return if right.block_type? || right.send_type?
  # allow mass assignment when using splat
  return if (left_elements + right_elements).any?(&:splat_type?)
  order = find_valid_order(left_elements, right_elements)
  # For `a, b = b, a` or similar, there is no valid order
  return if order.nil?
  add_offense(node, :expression)
end

def rescue_modifier?(node)

def rescue_modifier?(node)
  node &&
    node.rescue_type? &&
    (node.parent.nil? || !node.parent.kwbegin_type?)
end