123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::VariableForce::VariableTable Private

Relationships & Source Files
Inherits: Object
Defined in: lib/rubocop/cop/variable_force/variable_table.rb

Overview

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

Class Method Summary

Instance Method Summary

Constructor Details

.new(hook_receiver = nil) ⇒ VariableTable

[ GitHub ]

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

def initialize(hook_receiver = nil)
  @hook_receiver = hook_receiver
end

Instance Method Details

#accessible_variables

[ GitHub ]

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

def accessible_variables
  scope_stack.reverse_each.with_object([]) do |scope, variables|
    variables.concat(scope.variables.values)
    break variables unless scope.node.block_type? || scope.node.numblock_type?
  end
end

#assign_to_variable(name, node)

[ GitHub ]

  
# 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

  mark_variable_as_captured_by_block_if_so(variable)
  variable.assign(node)
end

#current_scope

[ GitHub ]

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

def current_scope
  scope_stack.last
end

#current_scope_level

[ GitHub ]

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

def current_scope_level
  scope_stack.count
end

#declare_variable(name, node)

[ GitHub ]

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

[ GitHub ]

  
# 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.
    node = scope.node
    return nil unless node.block_type? || node.numblock_type?
  end

  nil
end

#invoke_hook(hook_name, *args)

[ GitHub ]

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

def invoke_hook(hook_name, *args)
  @hook_receiver&.send(hook_name, *args)
end

#mark_variable_as_captured_by_block_if_so(variable) (private)

[ GitHub ]

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

def mark_variable_as_captured_by_block_if_so(variable)
  return unless current_scope.node.block_type? || current_scope.node.numblock_type?
  return if variable.scope == current_scope

  variable.capture_with_block!
end

#pop_scope

[ GitHub ]

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

[ GitHub ]

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

[ GitHub ]

  
# 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

  mark_variable_as_captured_by_block_if_so(variable)
  variable.reference!(node)
end

#scope_stack

[ GitHub ]

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

def scope_stack
  @scope_stack ||= []
end

#variable_exist?(name) ⇒ Boolean

[ GitHub ]

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

def variable_exist?(name)
  find_variable(name)
end