123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Style::ParallelAssignment::AssignmentSorter

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, Macros
Inherits: Object
Defined in: lib/rubocop/cop/style/parallel_assignment.rb

Overview

Topologically sorts the assignments with Kahn’s algorithm. https://en.wikipedia.org/wiki/Topological_sorting#Kahn’s_algorithm

Class Method Summary

Instance Method Summary

Constructor Details

.new(assignments) ⇒ AssignmentSorter

[ GitHub ]

  
# File 'lib/rubocop/cop/style/parallel_assignment.rb', line 126

def initialize(assignments)
  @assignments = assignments
end

Instance Method Details

#accesses?(rhs, lhs) ⇒ Boolean

lhs is an assignment method call like obj.attr= or ary[idx]=. Does rhs access the same value which is assigned by lhs?

[ GitHub ]

  
# File 'lib/rubocop/cop/style/parallel_assignment.rb', line 173

def accesses?(rhs, lhs)
  if lhs.method?(:[]=)
    # FIXME: Workaround `rubocop:disable` comment for JRuby.
    # rubocop:disable Performance/RedundantEqualityComparisonBlock
    matching_calls(rhs, lhs.receiver, :[]).any? { |args| args == lhs.arguments }
    # rubocop:enable Performance/RedundantEqualityComparisonBlock
  else
    access_method = lhs.method_name.to_s.chop.to_sym
    matching_calls(rhs, lhs.receiver, access_method).any?
  end
end

#dependencies_for_assignment(assignment)

Returns all the assignments which must come after assignment (due to dependencies on the previous value of the assigned var)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/parallel_assignment.rb', line 152

def dependencies_for_assignment(assignment)
  my_lhs, _my_rhs = *assignment

  @assignments.filter_map do |other|
    # Exclude self, there are no dependencies in cases such as `a, b = a, b`.
    next if other == assignment

    _other_lhs, other_rhs = *other
    next unless dependency?(my_lhs, other_rhs)

    other
  end
end

#dependency?(lhs, rhs) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rubocop/cop/style/parallel_assignment.rb', line 166

def dependency?(lhs, rhs)
  uses_var?(rhs, var_name(lhs)) ||
    (lhs.send_type? && lhs.assignment_method? && accesses?(rhs, lhs))
end

#matching_calls(node, receiver, method_name)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/parallel_assignment.rb', line 124

def_node_search :matching_calls, '(send %1 %2 $...)'

#tsort

[ GitHub ]

  
# File 'lib/rubocop/cop/style/parallel_assignment.rb', line 130

def tsort
  dependencies = @assignments.to_h do |assignment|
    [assignment, dependencies_for_assignment(assignment)]
  end
  result = []

  while (matched_node, = dependencies.find { |_node, edges| edges.empty? })
    dependencies.delete(matched_node)
    result.push(matched_node)

    dependencies.each do |node, edges|
      dependencies[node].delete(matched_node) if edges.include?(matched_node)
    end
  end
  # Cyclic dependency
  return nil if dependencies.any?

  result
end

#uses_var?(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/parallel_assignment.rb', line 121

def_node_search :uses_var?, '{({lvar ivar cvar gvar} %) (const _ %)}'

#var_name(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/parallel_assignment.rb', line 118

def_node_matcher :var_name, '{(casgn _ $_) (_ $_)}'