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 add_self_to_getters(right_elements)
This makes the sorting algorithm work for expressions such as
Converts (send nil :something) nodes to (send (:self) :something).
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
def allowed_lhs?(node)
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
def allowed_masign?(lhs_elements, rhs_elements)
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
def allowed_rhs?(node)
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
def assignment_corrector(node, order)
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
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) correction = assignment_corrector(node, order) corrector.replace(correction.correction_range, correction.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 && %i[if while until].include?(node.type) && node.modifier_form? end
def on_masgn(node)
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
def return_of_method_call?(node)
def return_of_method_call?(node) node.block_type? || node.send_type? end