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 339

def descendant_reference(node)
  case node.type
  when :lvar
    VariableReference.new(node.children.first)
  when :lvasgn
    AssignmentReference.new(node)
  when *OPERATOR_ASSIGNMENT_TYPES
    asgn_node = node.children.first
    VariableReference.new(asgn_node.children.first) if asgn_node.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 329

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 314

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 94

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 76

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 295

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 126

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 100

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 231

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 85

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_regexp_named_captures(node) (private)

This method is for internal use only.
[ GitHub ]

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

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 246

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 265

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 284

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 142

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 130

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 219

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 184

def process_variable_operator_assignment(node)
  if LOGICAL_OPERATOR_ASSIGNMENT_TYPES.include?(node.type)
    asgn_node, rhs_node = *node
  else
    asgn_node, _operator, rhs_node = *node
  end

  return unless asgn_node.lvasgn_type?

  name = asgn_node.children.first

  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(rhs_node)
  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 226

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 257

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 178

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 351

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 355

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 108

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 278

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 71

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