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.



13
14
15
# File 'lib/rubocop/cop/variable_force/variable_table.rb', line 13

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

Instance Method Details

#accessible_variablesObject



112
113
114
115
116
117
# File 'lib/rubocop/cop/variable_force/variable_table.rb', line 112

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



57
58
59
60
61
62
63
64
65
66
67
# File 'lib/rubocop/cop/variable_force/variable_table.rb', line 57

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



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

def current_scope
  scope_stack.last
end

#current_scope_levelObject



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

def current_scope_level
  scope_stack.count
end

#declare_variable(name, node) ⇒ Object



49
50
51
52
53
54
55
# File 'lib/rubocop/cop/variable_force/variable_table.rb', line 49

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



95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/rubocop/cop/variable_force/variable_table.rb', line 95

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



17
18
19
# File 'lib/rubocop/cop/variable_force/variable_table.rb', line 17

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

#pop_scopeObject



33
34
35
36
37
38
39
# File 'lib/rubocop/cop/variable_force/variable_table.rb', line 33

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



25
26
27
28
29
30
31
# File 'lib/rubocop/cop/variable_force/variable_table.rb', line 25

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



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/rubocop/cop/variable_force/variable_table.rb', line 69

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



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

def scope_stack
  @scope_stack ||= []
end

#variable_exist?(name) ⇒ Boolean

Returns:

  • (Boolean)


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

def variable_exist?(name)
  find_variable(name)
end