Class: Spoom::Deadcode::Indexer
- Inherits:
-
SyntaxTree::Visitor
- Object
- SyntaxTree::Visitor
- Spoom::Deadcode::Indexer
- Extended by:
- T::Sig
- Defined in:
- lib/spoom/deadcode/indexer.rb
Instance Attribute Summary collapse
-
#file_name ⇒ Object
readonly
Returns the value of attribute file_name.
-
#index ⇒ Object
readonly
Returns the value of attribute index.
-
#path ⇒ Object
readonly
Returns the value of attribute path.
Instance Method Summary collapse
- #call_args(node) ⇒ Object
- #current_node ⇒ Object
- #define_attr_reader(name, full_name, node) ⇒ Object
- #define_attr_writer(name, full_name, node) ⇒ Object
- #define_class(name, full_name, node) ⇒ Object
- #define_constant(name, full_name, node) ⇒ Object
- #define_method(name, full_name, node) ⇒ Object
- #define_module(name, full_name, node) ⇒ Object
-
#initialize(path, source, index, plugins: []) ⇒ Indexer
constructor
A new instance of Indexer.
- #last_sig ⇒ Object
- #nesting_block ⇒ Object
- #nesting_block_call ⇒ Object
- #nesting_block_call_name ⇒ Object
- #nesting_class ⇒ Object
- #nesting_class_name ⇒ Object
- #nesting_class_superclass_name ⇒ Object
- #nesting_node(type) ⇒ Object
- #node_location(node) ⇒ Object
- #node_string(node) ⇒ Object
- #reference_constant(name, node) ⇒ Object
- #reference_method(name, node) ⇒ Object
- #symbol_string(node) ⇒ Object
- #visit(node) ⇒ Object
- #visit_alias(node) ⇒ Object
- #visit_aref(node) ⇒ Object
- #visit_aref_field(node) ⇒ Object
- #visit_arg_block(node) ⇒ Object
- #visit_binary(node) ⇒ Object
- #visit_call(node) ⇒ Object
- #visit_class(node) ⇒ Object
- #visit_command(node) ⇒ Object
- #visit_command_call(node) ⇒ Object
- #visit_const(node) ⇒ Object
- #visit_const_path_field(node) ⇒ Object
- #visit_def(node) ⇒ Object
- #visit_field(node) ⇒ Object
- #visit_module(node) ⇒ Object
- #visit_opassign(node) ⇒ Object
- #visit_send(send) ⇒ Object
- #visit_symbol_literal(node) ⇒ Object
- #visit_top_const_field(node) ⇒ Object
- #visit_var_field(node) ⇒ Object
- #visit_vcall(node) ⇒ Object
Constructor Details
#initialize(path, source, index, plugins: []) ⇒ Indexer
Returns a new instance of Indexer.
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/spoom/deadcode/indexer.rb', line 16 def initialize(path, source, index, plugins: []) super() @path = path @file_name = T.let(File.basename(path), String) @source = source @index = index @plugins = plugins @previous_node = T.let(nil, T.nilable(SyntaxTree::Node)) @names_nesting = T.let([], T::Array[String]) @nodes_nesting = T.let([], T::Array[SyntaxTree::Node]) @in_const_field = T.let(false, T::Boolean) @in_opassign = T.let(false, T::Boolean) @in_symbol_literal = T.let(false, T::Boolean) end |
Instance Attribute Details
#file_name ⇒ Object (readonly)
Returns the value of attribute file_name.
10 11 12 |
# File 'lib/spoom/deadcode/indexer.rb', line 10 def file_name @file_name end |
#index ⇒ Object (readonly)
Returns the value of attribute index.
13 14 15 |
# File 'lib/spoom/deadcode/indexer.rb', line 13 def index @index end |
#path ⇒ Object (readonly)
Returns the value of attribute path.
10 11 12 |
# File 'lib/spoom/deadcode/indexer.rb', line 10 def path @path end |
Instance Method Details
#call_args(node) ⇒ Object
459 460 461 462 463 464 465 466 467 468 |
# File 'lib/spoom/deadcode/indexer.rb', line 459 def call_args(node) case node when SyntaxTree::ArgParen call_args(node.arguments) when SyntaxTree::Args node.parts else [] end end |
#current_node ⇒ Object
367 368 369 |
# File 'lib/spoom/deadcode/indexer.rb', line 367 def current_node T.must(@nodes_nesting.last) end |
#define_attr_reader(name, full_name, node) ⇒ Object
281 282 283 284 285 286 287 288 289 290 |
# File 'lib/spoom/deadcode/indexer.rb', line 281 def define_attr_reader(name, full_name, node) definition = Definition.new( kind: Definition::Kind::AttrReader, name: name, full_name: full_name, location: node_location(node), ) @index.define(definition) @plugins.each { |plugin| plugin.internal_on_define_accessor(self, definition) } end |
#define_attr_writer(name, full_name, node) ⇒ Object
293 294 295 296 297 298 299 300 301 302 |
# File 'lib/spoom/deadcode/indexer.rb', line 293 def define_attr_writer(name, full_name, node) definition = Definition.new( kind: Definition::Kind::AttrWriter, name: name, full_name: full_name, location: node_location(node), ) @index.define(definition) @plugins.each { |plugin| plugin.internal_on_define_accessor(self, definition) } end |
#define_class(name, full_name, node) ⇒ Object
305 306 307 308 309 310 311 312 313 314 |
# File 'lib/spoom/deadcode/indexer.rb', line 305 def define_class(name, full_name, node) definition = Definition.new( kind: Definition::Kind::Class, name: name, full_name: full_name, location: node_location(node), ) @index.define(definition) @plugins.each { |plugin| plugin.internal_on_define_class(self, definition) } end |
#define_constant(name, full_name, node) ⇒ Object
317 318 319 320 321 322 323 324 325 326 |
# File 'lib/spoom/deadcode/indexer.rb', line 317 def define_constant(name, full_name, node) definition = Definition.new( kind: Definition::Kind::Constant, name: name, full_name: full_name, location: node_location(node), ) @index.define(definition) @plugins.each { |plugin| plugin.internal_on_define_constant(self, definition) } end |
#define_method(name, full_name, node) ⇒ Object
329 330 331 332 333 334 335 336 337 338 |
# File 'lib/spoom/deadcode/indexer.rb', line 329 def define_method(name, full_name, node) definition = Definition.new( kind: Definition::Kind::Method, name: name, full_name: full_name, location: node_location(node), ) @index.define(definition) @plugins.each { |plugin| plugin.internal_on_define_method(self, definition) } end |
#define_module(name, full_name, node) ⇒ Object
341 342 343 344 345 346 347 348 349 350 |
# File 'lib/spoom/deadcode/indexer.rb', line 341 def define_module(name, full_name, node) definition = Definition.new( kind: Definition::Kind::Module, name: name, full_name: full_name, location: node_location(node), ) @index.define(definition) @plugins.each { |plugin| plugin.internal_on_define_module(self, definition) } end |
#last_sig ⇒ Object
426 427 428 429 430 |
# File 'lib/spoom/deadcode/indexer.rb', line 426 def last_sig return unless @previous_node.is_a?(SyntaxTree::MethodAddBlock) node_string(@previous_node) end |
#nesting_block ⇒ Object
386 387 388 |
# File 'lib/spoom/deadcode/indexer.rb', line 386 def nesting_block nesting_node(SyntaxTree::BlockNode) end |
#nesting_block_call ⇒ Object
391 392 393 |
# File 'lib/spoom/deadcode/indexer.rb', line 391 def nesting_block_call nesting_node(SyntaxTree::MethodAddBlock) end |
#nesting_block_call_name ⇒ Object
396 397 398 399 400 401 402 403 404 405 406 407 |
# File 'lib/spoom/deadcode/indexer.rb', line 396 def nesting_block_call_name block = nesting_block_call return unless block.is_a?(SyntaxTree::MethodAddBlock) call = block.call case call when SyntaxTree::ARef node_string(call.collection) when SyntaxTree::CallNode, SyntaxTree::Command, SyntaxTree::CommandCall node_string(call.) end end |
#nesting_class ⇒ Object
381 382 383 |
# File 'lib/spoom/deadcode/indexer.rb', line 381 def nesting_class nesting_node(SyntaxTree::ClassDeclaration) end |
#nesting_class_name ⇒ Object
410 411 412 413 414 415 |
# File 'lib/spoom/deadcode/indexer.rb', line 410 def nesting_class_name nesting_class = self.nesting_class return unless nesting_class node_string(nesting_class.constant) end |
#nesting_class_superclass_name ⇒ Object
418 419 420 421 422 423 |
# File 'lib/spoom/deadcode/indexer.rb', line 418 def nesting_class_superclass_name nesting_class_superclass = nesting_class&.superclass return unless nesting_class_superclass node_string(nesting_class_superclass).delete_prefix("::") end |
#nesting_node(type) ⇒ Object
372 373 374 375 376 377 378 |
# File 'lib/spoom/deadcode/indexer.rb', line 372 def nesting_node(type) @nodes_nesting.reverse_each do |node| return T.unsafe(node) if node.is_a?(type) end nil end |
#node_location(node) ⇒ Object
445 446 447 |
# File 'lib/spoom/deadcode/indexer.rb', line 445 def node_location(node) Location.from_syntax_tree(@path, node.location) end |
#node_string(node) ⇒ Object
435 436 437 438 439 440 441 442 |
# File 'lib/spoom/deadcode/indexer.rb', line 435 def node_string(node) case node when Symbol node.to_s else T.must(@source[node.location.start_char...node.location.end_char]) end end |
#reference_constant(name, node) ⇒ Object
355 356 357 |
# File 'lib/spoom/deadcode/indexer.rb', line 355 def reference_constant(name, node) @index.reference(Reference.new(name: name, kind: Reference::Kind::Constant, location: node_location(node))) end |
#reference_method(name, node) ⇒ Object
360 361 362 |
# File 'lib/spoom/deadcode/indexer.rb', line 360 def reference_method(name, node) @index.reference(Reference.new(name: name, kind: Reference::Kind::Method, location: node_location(node))) end |
#symbol_string(node) ⇒ Object
450 451 452 |
# File 'lib/spoom/deadcode/indexer.rb', line 450 def symbol_string(node) node_string(node).delete_prefix(":") end |
#visit(node) ⇒ Object
35 36 37 38 39 40 41 42 |
# File 'lib/spoom/deadcode/indexer.rb', line 35 def visit(node) return unless node @nodes_nesting << node super @nodes_nesting.pop @previous_node = node end |
#visit_alias(node) ⇒ Object
45 46 47 |
# File 'lib/spoom/deadcode/indexer.rb', line 45 def visit_alias(node) reference_method(node_string(node.right), node) end |
#visit_aref(node) ⇒ Object
50 51 52 53 54 |
# File 'lib/spoom/deadcode/indexer.rb', line 50 def visit_aref(node) super reference_method("[]", node) end |
#visit_aref_field(node) ⇒ Object
57 58 59 60 61 |
# File 'lib/spoom/deadcode/indexer.rb', line 57 def visit_aref_field(node) super reference_method("[]=", node) end |
#visit_arg_block(node) ⇒ Object
64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/spoom/deadcode/indexer.rb', line 64 def visit_arg_block(node) value = node.value case value when SyntaxTree::SymbolLiteral # If the block call is something like `x.select(&:foo)`, we need to reference the `foo` method reference_method(symbol_string(value), node) when SyntaxTree::VCall # If the block call is something like `x.select { ... }`, we need to visit the block super end end |
#visit_binary(node) ⇒ Object
78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/spoom/deadcode/indexer.rb', line 78 def visit_binary(node) super op = node.operator # Reference the operator itself reference_method(op.to_s, node) case op when :<, :>, :<=, :>= # For comparison operators, we also reference the `<=>` method reference_method("<=>", node) end end |
#visit_call(node) ⇒ Object
94 95 96 97 98 99 100 101 102 103 |
# File 'lib/spoom/deadcode/indexer.rb', line 94 def visit_call(node) visit_send( Send.new( node: node, name: node_string(node.), recv: node.receiver, args: call_args(node.arguments), ), ) end |
#visit_class(node) ⇒ Object
106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/spoom/deadcode/indexer.rb', line 106 def visit_class(node) const_name = node_string(node.constant) @names_nesting << const_name define_class(T.must(const_name.split("::").last), @names_nesting.join("::"), node) # We do not call `super` here because we don't want to visit the `constant` again visit(node.superclass) if node.superclass visit(node.bodystmt) @names_nesting.pop end |
#visit_command(node) ⇒ Object
119 120 121 122 123 124 125 126 127 128 |
# File 'lib/spoom/deadcode/indexer.rb', line 119 def visit_command(node) visit_send( Send.new( node: node, name: node_string(node.), args: call_args(node.arguments), block: node.block, ), ) end |
#visit_command_call(node) ⇒ Object
131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/spoom/deadcode/indexer.rb', line 131 def visit_command_call(node) visit_send( Send.new( node: node, name: node_string(node.), recv: node.receiver, args: call_args(node.arguments), block: node.block, ), ) end |
#visit_const(node) ⇒ Object
144 145 146 |
# File 'lib/spoom/deadcode/indexer.rb', line 144 def visit_const(node) reference_constant(node.value, node) unless @in_symbol_literal end |
#visit_const_path_field(node) ⇒ Object
149 150 151 152 153 154 155 156 |
# File 'lib/spoom/deadcode/indexer.rb', line 149 def visit_const_path_field(node) # We do not call `super` here because we don't want to visit the `constant` again visit(node.parent) name = node.constant.value full_name = [*@names_nesting, node_string(node.parent), name].join("::") define_constant(name, full_name, node) end |
#visit_def(node) ⇒ Object
159 160 161 162 163 164 |
# File 'lib/spoom/deadcode/indexer.rb', line 159 def visit_def(node) name = node_string(node.name) define_method(name, [*@names_nesting, name].join("::"), node) super end |
#visit_field(node) ⇒ Object
167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/spoom/deadcode/indexer.rb', line 167 def visit_field(node) visit(node.parent) name = node.name case name when SyntaxTree::Const name = name.value full_name = [*@names_nesting, node_string(node.parent), name].join("::") define_constant(name, full_name, node) when SyntaxTree::Ident reference_method(name.value, node) if @in_opassign reference_method("#{name.value}=", node) end end |
#visit_module(node) ⇒ Object
183 184 185 186 187 188 189 190 191 192 |
# File 'lib/spoom/deadcode/indexer.rb', line 183 def visit_module(node) const_name = node_string(node.constant) @names_nesting << const_name define_module(T.must(const_name.split("::").last), @names_nesting.join("::"), node) # We do not call `super` here because we don't want to visit the `constant` again visit(node.bodystmt) @names_nesting.pop end |
#visit_opassign(node) ⇒ Object
195 196 197 198 199 200 201 |
# File 'lib/spoom/deadcode/indexer.rb', line 195 def visit_opassign(node) # Both `FOO = x` and `FOO += x` yield a VarField node, but the former is a constant definition and the latter is # a constant reference. We need to distinguish between the two cases. @in_opassign = true super @in_opassign = false end |
#visit_send(send) ⇒ Object
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/spoom/deadcode/indexer.rb', line 204 def visit_send(send) visit(send.recv) case send.name when "attr_reader" send.args.each do |arg| next unless arg.is_a?(SyntaxTree::SymbolLiteral) name = symbol_string(arg) define_attr_reader(name, [*@names_nesting, name].join("::"), arg) end when "attr_writer" send.args.each do |arg| next unless arg.is_a?(SyntaxTree::SymbolLiteral) name = symbol_string(arg) define_attr_writer("#{name}=", "#{[*@names_nesting, name].join("::")}=", arg) end when "attr_accessor" send.args.each do |arg| next unless arg.is_a?(SyntaxTree::SymbolLiteral) name = symbol_string(arg) full_name = [*@names_nesting, name].join("::") define_attr_reader(name, full_name, arg) define_attr_writer("#{name}=", "#{full_name}=", arg) end else @plugins.each do |plugin| plugin.internal_on_send(self, send) end reference_method(send.name, send.node) visit_all(send.args) visit(send.block) end end |
#visit_symbol_literal(node) ⇒ Object
243 244 245 246 247 248 249 |
# File 'lib/spoom/deadcode/indexer.rb', line 243 def visit_symbol_literal(node) # Something like `:FOO` will yield a Const node but we do not want to treat it as a constant reference. # So we need to distinguish between the two cases. @in_symbol_literal = true super @in_symbol_literal = false end |
#visit_top_const_field(node) ⇒ Object
252 253 254 |
# File 'lib/spoom/deadcode/indexer.rb', line 252 def visit_top_const_field(node) define_constant(node.constant.value, node.constant.value, node) end |
#visit_var_field(node) ⇒ Object
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 |
# File 'lib/spoom/deadcode/indexer.rb', line 257 def visit_var_field(node) value = node.value case value when SyntaxTree::Const if @in_opassign reference_constant(value.value, node) else name = value.value define_constant(name, [*@names_nesting, name].join("::"), node) end when SyntaxTree::Ident reference_method(value.value, node) if @in_opassign reference_method("#{value.value}=", node) end end |
#visit_vcall(node) ⇒ Object
274 275 276 |
# File 'lib/spoom/deadcode/indexer.rb', line 274 def visit_vcall(node) visit_send(Send.new(node: node, name: node_string(node.value))) end |