123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::VariableForce Private

Do not use. This class is for internal use only.

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)

[ 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)

[ 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)

[ 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 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)

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)

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)

[ 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)

[ 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)

[ 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)

[ 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)

[ 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)

[ 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)

[ 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)

[ 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)

[ 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)

[ 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)

[ 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)

[ 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)

[ 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)

[ 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)

[ 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)

[ 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)

[ 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)

[ GitHub ]

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

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

#scanned_nodes (private)

[ GitHub ]

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

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

#skip_children! (private)

[ GitHub ]

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

def skip_children!
  :skip_children
end

#twisted_nodes(node) (private)

[ 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

[ GitHub ]

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

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