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

Inherits:
Object
  • Object
show all
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, and provides a way to add local variables to current scope and find local variables by considering variable visibility of the current scope.

Instance Method Summary collapse

Constructor Details

#initialize(hook_receiver = nil) ⇒ VariableTable

Returns a new instance of VariableTable.



12
13
14
# 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_variablesObject



111
112
113
114
115
116
# 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.block_type?
  end
end

#assign_to_variable(name, node) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
# 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_scopeObject



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

def current_scope
  scope_stack.last
end

#current_scope_levelObject



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

def current_scope_level
  scope_stack.count
end

#declare_variable(name, node) ⇒ Object



48
49
50
51
52
53
54
# 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) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
# 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.block_type?
  end

  nil
end

#invoke_hook(hook_name, *args) ⇒ Object



16
17
18
# 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_scopeObject



32
33
34
35
36
37
38
# 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) ⇒ Object



24
25
26
27
28
29
30
# 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) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# 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_stackObject



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

def scope_stack
  @scope_stack ||= []
end

#variable_exist?(name) ⇒ Boolean

Returns:

  • (Boolean)


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

def variable_exist?(name)
  find_variable(name)
end