class RuboCop::Cop::VariableForce::VariableTable

A VariableTable manages the lifetime of all scopes and local variables in a program. This holds scopes as stack structure, and provides a way to add local variables to current scope and find local variables by considering variable visibility of the current scope.

Public Class Methods

new(hook_receiver = nil) click to toggle source
# File lib/rubocop/cop/variable_force/variable_table.rb, line 12
def initialize(hook_receiver = nil)
  @hook_receiver = hook_receiver
end

Public Instance Methods

accessible_variables() click to toggle source
# File lib/rubocop/cop/variable_force/variable_table.rb, line 111
def accessible_variables
  scope_stack.reverse_each.each_with_object([]) do |scope, variables|
    variables.concat(scope.variables.values)
    break variables unless scope.node.type == :block
  end
end
assign_to_variable(name, node) click to toggle source
# File lib/rubocop/cop/variable_force/variable_table.rb, line 56
def assign_to_variable(name, node)
  variable = find_variable(name)

  unless variable
    raise "Assigning to undeclared local variable \"#{name}\" "                   "at #{node.source_range}, #{node.inspect}"
  end

  variable.assign(node)
  mark_variable_as_captured_by_block_if_so(variable)
end
current_scope() click to toggle source
# File lib/rubocop/cop/variable_force/variable_table.rb, line 40
def current_scope
  scope_stack.last
end
current_scope_level() click to toggle source
# File lib/rubocop/cop/variable_force/variable_table.rb, line 44
def current_scope_level
  scope_stack.count
end
declare_variable(name, node) click to toggle source
# File lib/rubocop/cop/variable_force/variable_table.rb, line 48
def declare_variable(name, node)
  variable = Variable.new(name, node, current_scope)
  invoke_hook(:before_declaring_variable, variable)
  current_scope.variables[variable.name] = variable
  invoke_hook(:after_declaring_variable, variable)
  variable
end
find_variable(name) click to toggle source
# File lib/rubocop/cop/variable_force/variable_table.rb, line 94
def find_variable(name)
  name = name.to_sym

  scope_stack.reverse_each do |scope|
    variable = scope.variables[name]
    return variable if variable
    # Only block scope allows referencing outer scope variables.
    return nil unless scope.node.type == :block
  end

  nil
end
invoke_hook(hook_name, *args) click to toggle source
# File lib/rubocop/cop/variable_force/variable_table.rb, line 16
def invoke_hook(hook_name, *args)
  @hook_receiver.send(hook_name, *args) if @hook_receiver
end
pop_scope() click to toggle source
# File lib/rubocop/cop/variable_force/variable_table.rb, line 32
def pop_scope
  scope = current_scope
  invoke_hook(:before_leaving_scope, scope)
  scope_stack.pop
  invoke_hook(:after_leaving_scope, scope)
  scope
end
push_scope(scope_node) click to toggle source
# File lib/rubocop/cop/variable_force/variable_table.rb, line 24
def push_scope(scope_node)
  scope = Scope.new(scope_node)
  invoke_hook(:before_entering_scope, scope)
  scope_stack.push(scope)
  invoke_hook(:after_entering_scope, scope)
  scope
end
reference_variable(name, node) click to toggle source
# File lib/rubocop/cop/variable_force/variable_table.rb, line 68
def reference_variable(name, node)
  variable = find_variable(name)

  # In this code:
  #
  #   foo = 1 unless foo
  #
  #   (if
  #     (lvar :foo) nil
  #     (lvasgn :foo
  #       (int 1)))
  #
  # Parser knows whether the foo is a variable or method invocation.
  # This means that if a :lvar node is shown in AST, the variable is
  # assumed to be already declared, even if we haven't seen any :lvasgn
  # or :arg node before the :lvar node.
  #
  # We don't invoke #declare_variable here otherwise
  # Variable#declaration_node will be :lvar node, that is actually not.
  # So just skip.
  return unless variable

  variable.reference!(node)
  mark_variable_as_captured_by_block_if_so(variable)
end
scope_stack() click to toggle source
# File lib/rubocop/cop/variable_force/variable_table.rb, line 20
def scope_stack
  @scope_stack ||= []
end
variable_exist?(name) click to toggle source
# File lib/rubocop/cop/variable_force/variable_table.rb, line 107
def variable_exist?(name)
  find_variable(name)
end

Private Instance Methods

mark_variable_as_captured_by_block_if_so(variable) click to toggle source
# File lib/rubocop/cop/variable_force/variable_table.rb, line 120
def mark_variable_as_captured_by_block_if_so(variable)
  return unless current_scope.node.type == :block
  return if variable.scope == current_scope
  variable.capture_with_block!
end