class RuboCop::Cop::Style::ParallelAssignment

Checks for simple usages of parallel assignment. This will only complain when the number of variables being assigned matched the number of assigning variables.

@example

# bad
a, b, c = 1, 2, 3
a, b, c = [1, 2, 3]

# good
one, two = *foo
a, b = foo()
a, b = b, a

a = 1
b = 2
c = 3

Constants

MSG

Public Instance Methods

on_masgn(node) click to toggle source
# File lib/rubocop/cop/style/parallel_assignment.rb, line 31
def on_masgn(node)
  lhs, rhs = *node
  lhs_elements = *lhs
  rhs_elements = Array(rhs).compact # edge case for one constant

  return if allowed_lhs?(lhs) || allowed_rhs?(rhs) ||
            allowed_masign?(lhs_elements, rhs_elements)

  add_offense(node) { |corrector| autocorrect(corrector, node) }
end

Private Instance Methods

add_self_to_getters(right_elements) click to toggle source

Converts (send nil :something) nodes to (send (:self) :something). This makes the sorting algorithm work for expressions such as ‘self.a, self.b = b, a`.

# File lib/rubocop/cop/style/parallel_assignment.rb, line 108
def add_self_to_getters(right_elements)
  right_elements.map do |e|
    implicit_self_getter?(e) { |var| s(:send, s(:self), var) } || e
  end
end
allowed_lhs?(node) click to toggle source
# File lib/rubocop/cop/style/parallel_assignment.rb, line 60
def allowed_lhs?(node)
  elements = *node

  # Account for edge cases using one variable with a comma
  # E.g.: `foo, = *bar`
  elements.one? || elements.any?(&:splat_type?)
end
allowed_masign?(lhs_elements, rhs_elements) click to toggle source
# File lib/rubocop/cop/style/parallel_assignment.rb, line 54
def allowed_masign?(lhs_elements, rhs_elements)
  lhs_elements.size != rhs_elements.size ||
    !find_valid_order(lhs_elements,
                      add_self_to_getters(rhs_elements))
end
allowed_rhs?(node) click to toggle source
# File lib/rubocop/cop/style/parallel_assignment.rb, line 68
def allowed_rhs?(node)
  # Edge case for one constant
  elements = Array(node).compact

  # Account for edge case of `Constant::CONSTANT`
  !node.array_type? || return_of_method_call?(node) || elements.any?(&:splat_type?)
end
assignment_corrector(node, order) click to toggle source
# File lib/rubocop/cop/style/parallel_assignment.rb, line 80
def assignment_corrector(node, order)
  _assignment, modifier = *node.parent
  if modifier_statement?(node.parent)
    ModifierCorrector.new(node, config, order)
  elsif rescue_modifier?(modifier)
    RescueCorrector.new(node, config, order)
  else
    GenericCorrector.new(node, config, order)
  end
end
autocorrect(corrector, node) click to toggle source
# File lib/rubocop/cop/style/parallel_assignment.rb, line 44
def autocorrect(corrector, node)
  left, right = *node
  left_elements = *left
  right_elements = Array(right).compact
  order = find_valid_order(left_elements, right_elements)
  correction = assignment_corrector(node, order)

  corrector.replace(correction.correction_range, correction.correction)
end
find_valid_order(left_elements, right_elements) click to toggle source
# File lib/rubocop/cop/style/parallel_assignment.rb, line 91
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
modifier_statement?(node) click to toggle source
# File lib/rubocop/cop/style/parallel_assignment.rb, line 174
def modifier_statement?(node)
  node && %i[if while until].include?(node.type) && node.modifier_form?
end
return_of_method_call?(node) click to toggle source
# File lib/rubocop/cop/style/parallel_assignment.rb, line 76
def return_of_method_call?(node)
  node.block_type? || node.send_type?
end