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 346

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 336

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 321

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 97

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 79

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 all 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 302

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

    variable.assignments.each do |assignment|
      next if assignment_nodes_in_loop.none? do |assignment_node|
                assignment_node.equal?(assignment.node)
              end

      assignment.reference!(node)
    end
  end
end

#node_handler_method_name(node) (private)

This method is for internal use only.
[ GitHub ]

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

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 103

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 238

def process_loop(node)
  if POST_CONDITION_LOOP_TYPES.include?(node.type)
    # See the comment at the end of file for this behavior.
    condition_node, body_node = *node
    process_node(body_node)
    process_node(condition_node)
  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 88

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 182

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 164

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 253

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 272

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 291

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 146

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 134

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 226

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 196

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 233

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 264

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

    variable.reference!(node)
  end
end

#regexp_captured_names(node) (private)

This method is for internal use only.
[ GitHub ]

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

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 357

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 361

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 111

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 285

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 74

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