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.modifier_form? 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? || node.parent.ensure_type?)) end