Class: Rubocop::Cop::VariableInspector::NodeScanner

Inherits:
Object
  • Object
show all
Defined in:
lib/rubocop/cop/variable_inspector.rb

Overview

This provides a way to scan all nodes only in current scope.

Constant Summary collapse

TWISTED_SCOPE_NODE_TYPES =
[:block, :sclass, :defs].freeze
POST_CONDITION_LOOP_NODE_TYPES =
[:while_post, :until_post].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(callback) ⇒ NodeScanner

Returns a new instance of NodeScanner.



133
134
135
# File 'lib/rubocop/cop/variable_inspector.rb', line 133

def initialize(callback)
  @callback = callback
end

Class Method Details

.scan_nodes_in_scope(origin_node, &block) ⇒ Object



128
129
130
131
# File 'lib/rubocop/cop/variable_inspector.rb', line 128

def self.scan_nodes_in_scope(origin_node, &block)
  instance = new(block)
  instance.scan_nodes_in_scope(origin_node)
end

Instance Method Details

#scan_children(node) ⇒ Object



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/rubocop/cop/variable_inspector.rb', line 155

def scan_children(node)
  case node.type
  when *POST_CONDITION_LOOP_NODE_TYPES
    # Loop body nodes need to be scanned first.
    #
    # Ruby:
    #   begin
    #     foo = 1
    #   end while foo > 10
    #   puts foo
    #
    # AST:
    #   (begin
    #     (while-post
    #       (send
    #         (lvar :foo) :>
    #         (int 10))
    #       (kwbegin
    #         (lvasgn :foo
    #           (int 1))))
    #     (send nil :puts
    #       (lvar :foo)))
    scan_nodes_in_scope(node.children[1], true)
    scan_nodes_in_scope(node.children[0], true)
  when *TWISTED_SCOPE_NODE_TYPES
    # The variable foo belongs to the top level scope,
    # but in AST, it's under the block node.
    #
    # Ruby:
    #   some_method(foo = 1) do
    #   end
    #   puts foo
    #
    # AST:
    #   (begin
    #     (block
    #       (send nil :some_method
    #         (lvasgn :foo
    #           (int 1)))
    #       (args) nil)
    #     (send nil :puts
    #       (lvar :foo)))
    #
    # So the the method argument nodes need to be processed
    # in current scope.
    #
    # Same thing.
    #
    # Ruby:
    #   instance = Object.new
    #   class << instance
    #     foo = 1
    #   end
    #
    # AST:
    #   (begin
    #     (lvasgn :instance
    #       (send
    #         (const nil :Object) :new))
    #     (sclass
    #       (lvar :instance)
    #       (begin
    #         (lvasgn :foo
    #           (int 1))
    scan_nodes_in_scope(node.children.first, true)
  when *SCOPE_TYPES
    # Do not go into inner scope.
  else
    scan_nodes_in_scope(node)
  end
end

#scan_nodes_in_scope(origin_node, yield_origin_node = false) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/rubocop/cop/variable_inspector.rb', line 137

def scan_nodes_in_scope(origin_node, yield_origin_node = false)
  @callback.call(origin_node) if yield_origin_node

  origin_node.children.each_with_index do |child, index|
    next unless child.is_a?(Parser::AST::Node)
    node = child

    if index == 0 &&
       TWISTED_SCOPE_NODE_TYPES.include?(origin_node.type)
      next
    end

    @callback.call(node)

    scan_children(node)
  end
end