123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::VariableForce Private

Overview

This force provides a way to track local variables and scopes of Ruby. Cops interact with this force need to override some of the hook methods.

def before_entering_scope(scope, variable_table)
end
def after_entering_scope(scope, variable_table)
end
def before_leaving_scope(scope, variable_table)
end
def after_leaving_scope(scope, variable_table)
end
def before_declaring_variable(variable, variable_table)
end
def after_declaring_variable(variable, variable_table)
end

Constant Summary

Class Method Summary

Instance Attribute Summary

Force - Inherited

Instance Method Summary

Force - Inherited

Constructor Details

This class inherits a constructor from RuboCop::Cop::Force

Instance Method Details

#descendant_reference(node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 352

def descendant_reference(node)
  case node.type
  when :lvar
    VariableReference.new(node.children.first)
  when :lvasgn
    AssignmentReference.new(node)
  when *OPERATOR_ASSIGNMENT_TYPES
    VariableReference.new(node.lhs.name) if node.lhs.lvasgn_type?
  end
end

#each_descendant_reference(loop_node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 342

def each_descendant_reference(loop_node)
  # #each_descendant does not consider scope,
  # but we don't need to care about it here.
  loop_node.each_descendant do |node|
    reference = descendant_reference(node)

    yield reference if reference
  end
end

#find_variables_in_loop(loop_node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 327

def find_variables_in_loop(loop_node)
  referenced_variable_names_in_loop = []
  assignment_nodes_in_loop = []

  each_descendant_reference(loop_node) do |reference|
    if reference.assignment?
      assignment_nodes_in_loop << reference.node
    else
      referenced_variable_names_in_loop << reference.name
    end
  end

  [referenced_variable_names_in_loop, assignment_nodes_in_loop]
end

#inspect_variables_in_scope(scope_node) (private)

This method is for internal use only.

This is called for each scope recursively.

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 99

def inspect_variables_in_scope(scope_node)
  variable_table.push_scope(scope_node)
  process_children(scope_node)
  variable_table.pop_scope
end

#investigate(processed_source)

This method is for internal use only.

Starting point.

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 81

def investigate(processed_source)
  root_node = processed_source.ast
  return unless root_node

  variable_table.push_scope(root_node)
  process_node(root_node)
  variable_table.pop_scope
end

#mark_assignments_as_referenced_in_loop(node) (private)

This method is for internal use only.

Mark last assignments which are referenced in the same loop as referenced by ignoring AST order since they would be referenced in next iteration.

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 309

def mark_assignments_as_referenced_in_loop(node)
  referenced_variable_names_in_loop, assignment_nodes_in_loop = find_variables_in_loop(node)

  referenced_variable_names_in_loop.each do |name|
    variable = variable_table.find_variable(name)
    # Non related references which are caught in the above scan
    # would be skipped here.
    next unless variable

    loop_assignments = variable.assignments.select do |assignment|
      assignment_nodes_in_loop.include?(assignment.node)
    end
    next unless loop_assignments.any?

    reference_assignments(loop_assignments, node)
  end
end

#node_handler_method_name(node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 132

def node_handler_method_name(node)
  NODE_HANDLER_METHOD_NAMES[node.type]
end

#process_children(origin_node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 105

def process_children(origin_node)
  origin_node.each_child_node do |child_node|
    next if scanned_node?(child_node)

    process_node(child_node)
  end
end

#process_loop(node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 240

def process_loop(node)
  if node.post_condition_loop?
    # See the comment at the end of file for this behavior.
    condition_node, body_node = *node
    process_node(body_node)
    process_node(condition_node)
  elsif node.for_type?
    # In `for item in items` the rightmost expression is evaluated first.
    process_node(node.collection)
    process_node(node.variable)
    process_node(node.body) if node.body
  else
    process_children(node)
  end

  mark_assignments_as_referenced_in_loop(node)

  skip_children!
end

#process_node(node)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 90

def process_node(node)
  method_name = node_handler_method_name(node)
  retval = send(method_name, node) if method_name
  process_children(node) unless retval == :skip_children
end

#process_pattern_match_variable(node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 184

def process_pattern_match_variable(node)
  name = node.children.first

  variable_table.declare_variable(name, node) unless variable_table.variable_exist?(name)

  skip_children!
end

#process_regexp_named_captures(node) (private)

This method is for internal use only.
[ GitHub ]

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

def process_regexp_named_captures(node)
  regexp_node, rhs_node = *node
  variable_names = regexp_captured_names(regexp_node)

  variable_names.each do |name|
    next if variable_table.variable_exist?(name)

    variable_table.declare_variable(name, node)
  end

  process_node(rhs_node)
  process_node(regexp_node)

  variable_names.each { |name| variable_table.assign_to_variable(name, node) }

  skip_children!
end

#process_rescue(node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 260

def process_rescue(node)
  resbody_nodes = node.each_child_node(:resbody)

  contain_retry = resbody_nodes.any? do |resbody_node|
    resbody_node.each_descendant.any?(&:retry_type?)
  end

  # Treat begin..rescue..end with retry as a loop.
  process_loop(node) if contain_retry
end

#process_scope(node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 279

def process_scope(node)
  if TWISTED_SCOPE_TYPES.include?(node.type)
    # See the comment at the end of file for this behavior.
    twisted_nodes(node).each do |twisted_node|
      process_node(twisted_node)
      scanned_nodes << twisted_node
    end
  end

  inspect_variables_in_scope(node)
  skip_children!
end

#process_send(node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 298

def process_send(node)
  _receiver, method_name, args = *node
  return unless method_name == :binding
  return if args && !args.children.empty?

  variable_table.accessible_variables.each { |variable| variable.reference!(node) }
end

#process_variable_assignment(node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 148

def process_variable_assignment(node)
  name = node.children.first

  variable_table.declare_variable(name, node) unless variable_table.variable_exist?(name)

  # Need to scan rhs before assignment so that we can mark previous
  # assignments as referenced if rhs has referencing to the variable
  # itself like:
  #
  #   foo = 1
  #   foo = foo + 1
  process_children(node)

  variable_table.assign_to_variable(name, node)

  skip_children!
end

#process_variable_declaration(node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 136

def process_variable_declaration(node)
  variable_name = node.children.first

  # restarg and kwrestarg would have no name:
  #
  #   def initialize(*)
  #   end
  return unless variable_name

  variable_table.declare_variable(variable_name, node)
end

#process_variable_multiple_assignment(node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 228

def process_variable_multiple_assignment(node)
  lhs_node, rhs_node = *node
  process_node(rhs_node)
  process_node(lhs_node)
  skip_children!
end

#process_variable_operator_assignment(node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 198

def process_variable_operator_assignment(node)
  asgn_node = node.lhs
  return unless asgn_node.lvasgn_type?

  name = asgn_node.name

  variable_table.declare_variable(name, asgn_node) unless variable_table.variable_exist?(name)

  # The following statements:
  #
  #   foo = 1
  #   foo += foo = 2
  #   # => 3
  #
  # are equivalent to:
  #
  #   foo = 1
  #   foo = foo + (foo = 2)
  #   # => 3
  #
  # So, at operator assignment node, we need to reference the variable
  # before processing rhs nodes.

  variable_table.reference_variable(name, node)
  process_node(node.rhs)
  variable_table.assign_to_variable(name, asgn_node)

  skip_children!
end

#process_variable_referencing(node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 235

def process_variable_referencing(node)
  name = node.children.first
  variable_table.reference_variable(name, node)
end

#process_zero_arity_super(node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 271

def process_zero_arity_super(node)
  variable_table.accessible_variables.each do |variable|
    next unless variable.method_argument?

    variable.reference!(node)
  end
end

#reference_assignments(loop_assignments, loop_node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 363

def reference_assignments(loop_assignments, loop_node)
  # If inside a branching statement, mark all as referenced.
  # Otherwise, mark only the last assignment as referenced.
  # Note that `rescue` must be considered as branching because of
  # the `retry` keyword.
  loop_assignments.each do |assignment|
    assignment.reference!(loop_node) if assignment.node.each_ancestor(*BRANCH_NODES).any?
  end
  loop_assignments.last&.reference!(loop_node)
end

#regexp_captured_names(node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 192

def regexp_captured_names(node)
  regexp = node.to_regexp

  regexp.named_captures.keys
end

#scanned_node?(node) ⇒ Boolean (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 374

def scanned_node?(node)
  scanned_nodes.include?(node)
end

#scanned_nodes (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 378

def scanned_nodes
  @scanned_nodes ||= Set.new.compare_by_identity
end

#skip_children! (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 113

def skip_children!
  :skip_children
end

#twisted_nodes(node) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 292

def twisted_nodes(node)
  twisted_nodes = [node.children[0]]
  twisted_nodes << node.children[1] if node.class_type?
  twisted_nodes.compact
end

#variable_table

This method is for internal use only.
[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force.rb', line 76

def variable_table
  @variable_table ||= VariableTable.new(self)
end