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 29
def on_masgn(node)
  lhs, rhs = *node
  lhs_elements = *lhs
  rhs_elements = [*rhs].compact # edge case for one constant

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

  add_offense(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 111
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 48
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 42
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 56
def allowed_rhs?(node)
  # Edge case for one constant
  elements = [*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 83
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(node) click to toggle source
# File lib/rubocop/cop/style/parallel_assignment.rb, line 70
def autocorrect(node)
  lambda do |corrector|
    left, right = *node
    left_elements = *left
    right_elements = [*right].compact
    order = find_valid_order(left_elements, right_elements)
    correction = assignment_corrector(node, order)

    corrector.replace(correction.correction_range,
                      correction.correction)
  end
end
find_valid_order(left_elements, right_elements) click to toggle source
# File lib/rubocop/cop/style/parallel_assignment.rb, line 94
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 170
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 66
def return_of_method_call?(node)
  node.block_type? || node.send_type?
end