spoom 1.2.3 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +64 -55
  3. data/lib/spoom/backtrace_filter/minitest.rb +21 -0
  4. data/lib/spoom/cli/deadcode.rb +172 -0
  5. data/lib/spoom/cli/helper.rb +20 -0
  6. data/lib/spoom/cli/srb/bump.rb +200 -0
  7. data/lib/spoom/cli/srb/coverage.rb +224 -0
  8. data/lib/spoom/cli/srb/lsp.rb +159 -0
  9. data/lib/spoom/cli/srb/tc.rb +150 -0
  10. data/lib/spoom/cli/srb.rb +27 -0
  11. data/lib/spoom/cli.rb +72 -32
  12. data/lib/spoom/context/git.rb +2 -2
  13. data/lib/spoom/context/sorbet.rb +2 -2
  14. data/lib/spoom/deadcode/definition.rb +11 -0
  15. data/lib/spoom/deadcode/erb.rb +4 -4
  16. data/lib/spoom/deadcode/indexer.rb +266 -200
  17. data/lib/spoom/deadcode/location.rb +30 -2
  18. data/lib/spoom/deadcode/plugins/action_mailer.rb +21 -0
  19. data/lib/spoom/deadcode/plugins/action_mailer_preview.rb +19 -0
  20. data/lib/spoom/deadcode/plugins/actionpack.rb +59 -0
  21. data/lib/spoom/deadcode/plugins/active_job.rb +13 -0
  22. data/lib/spoom/deadcode/plugins/active_model.rb +46 -0
  23. data/lib/spoom/deadcode/plugins/active_record.rb +108 -0
  24. data/lib/spoom/deadcode/plugins/active_support.rb +32 -0
  25. data/lib/spoom/deadcode/plugins/base.rb +165 -12
  26. data/lib/spoom/deadcode/plugins/graphql.rb +47 -0
  27. data/lib/spoom/deadcode/plugins/minitest.rb +28 -0
  28. data/lib/spoom/deadcode/plugins/namespaces.rb +32 -0
  29. data/lib/spoom/deadcode/plugins/rails.rb +31 -0
  30. data/lib/spoom/deadcode/plugins/rake.rb +12 -0
  31. data/lib/spoom/deadcode/plugins/rspec.rb +19 -0
  32. data/lib/spoom/deadcode/plugins/rubocop.rb +41 -0
  33. data/lib/spoom/deadcode/plugins/ruby.rb +10 -18
  34. data/lib/spoom/deadcode/plugins/sorbet.rb +40 -0
  35. data/lib/spoom/deadcode/plugins/thor.rb +21 -0
  36. data/lib/spoom/deadcode/plugins.rb +91 -0
  37. data/lib/spoom/deadcode/remover.rb +651 -0
  38. data/lib/spoom/deadcode/send.rb +27 -6
  39. data/lib/spoom/deadcode/visitor.rb +755 -0
  40. data/lib/spoom/deadcode.rb +41 -10
  41. data/lib/spoom/file_tree.rb +0 -16
  42. data/lib/spoom/sorbet/errors.rb +1 -1
  43. data/lib/spoom/sorbet/lsp/structures.rb +2 -2
  44. data/lib/spoom/version.rb +1 -1
  45. metadata +36 -15
  46. data/lib/spoom/cli/bump.rb +0 -198
  47. data/lib/spoom/cli/coverage.rb +0 -222
  48. data/lib/spoom/cli/lsp.rb +0 -168
  49. data/lib/spoom/cli/run.rb +0 -148
@@ -3,11 +3,11 @@
3
3
 
4
4
  module Spoom
5
5
  module Deadcode
6
- class Indexer < SyntaxTree::Visitor
6
+ class Indexer < Visitor
7
7
  extend T::Sig
8
8
 
9
9
  sig { returns(String) }
10
- attr_reader :path, :file_name
10
+ attr_reader :path
11
11
 
12
12
  sig { returns(Index) }
13
13
  attr_reader :index
@@ -21,9 +21,9 @@ module Spoom
21
21
  @source = source
22
22
  @index = index
23
23
  @plugins = plugins
24
- @previous_node = T.let(nil, T.nilable(SyntaxTree::Node))
24
+ @previous_node = T.let(nil, T.nilable(Prism::Node))
25
25
  @names_nesting = T.let([], T::Array[String])
26
- @nodes_nesting = T.let([], T::Array[SyntaxTree::Node])
26
+ @nodes_nesting = T.let([], T::Array[Prism::Node])
27
27
  @in_const_field = T.let(false, T::Boolean)
28
28
  @in_opassign = T.let(false, T::Boolean)
29
29
  @in_symbol_literal = T.let(false, T::Boolean)
@@ -31,7 +31,7 @@ module Spoom
31
31
 
32
32
  # Visit
33
33
 
34
- sig { override.params(node: T.nilable(SyntaxTree::Node)).void }
34
+ sig { override.params(node: T.nilable(Prism::Node)).void }
35
35
  def visit(node)
36
36
  return unless node
37
37
 
@@ -41,163 +41,233 @@ module Spoom
41
41
  @previous_node = node
42
42
  end
43
43
 
44
- sig { override.params(node: SyntaxTree::AliasNode).void }
45
- def visit_alias(node)
46
- reference_method(node_string(node.right), node)
44
+ sig { override.params(node: Prism::AliasMethodNode).void }
45
+ def visit_alias_method_node(node)
46
+ reference_method(node.old_name.slice, node)
47
47
  end
48
48
 
49
- sig { override.params(node: SyntaxTree::ARef).void }
50
- def visit_aref(node)
49
+ sig { override.params(node: Prism::AndNode).void }
50
+ def visit_and_node(node)
51
+ reference_method(node.operator_loc.slice, node)
51
52
  super
52
-
53
- reference_method("[]", node)
54
53
  end
55
54
 
56
- sig { override.params(node: SyntaxTree::ARefField).void }
57
- def visit_aref_field(node)
58
- super
59
-
60
- reference_method("[]=", node)
61
- end
62
-
63
- sig { override.params(node: SyntaxTree::ArgBlock).void }
64
- def visit_arg_block(node)
65
- value = node.value
66
-
67
- case value
68
- when SyntaxTree::SymbolLiteral
69
- # If the block call is something like `x.select(&:foo)`, we need to reference the `foo` method
70
- reference_method(symbol_string(value), node)
71
- when SyntaxTree::VCall
72
- # If the block call is something like `x.select { ... }`, we need to visit the block
73
- super
55
+ sig { override.params(node: Prism::BlockArgumentNode).void }
56
+ def visit_block_argument_node(node)
57
+ expression = node.expression
58
+ case expression
59
+ when Prism::SymbolNode
60
+ reference_method(expression.unescaped, expression)
61
+ else
62
+ visit(expression)
74
63
  end
75
64
  end
76
65
 
77
- sig { override.params(node: SyntaxTree::Binary).void }
78
- def visit_binary(node)
79
- super
80
-
81
- op = node.operator
66
+ sig { override.params(node: Prism::CallAndWriteNode).void }
67
+ def visit_call_and_write_node(node)
68
+ visit(node.receiver)
69
+ reference_method(node.read_name.to_s, node)
70
+ reference_method(node.write_name.to_s, node)
71
+ visit(node.value)
72
+ end
82
73
 
83
- # Reference the operator itself
84
- reference_method(op.to_s, node)
74
+ sig { override.params(node: Prism::CallOperatorWriteNode).void }
75
+ def visit_call_operator_write_node(node)
76
+ visit(node.receiver)
77
+ reference_method(node.read_name.to_s, node)
78
+ reference_method(node.write_name.to_s, node)
79
+ visit(node.value)
80
+ end
85
81
 
86
- case op
87
- when :<, :>, :<=, :>=
88
- # For comparison operators, we also reference the `<=>` method
89
- reference_method("<=>", node)
90
- end
82
+ sig { override.params(node: Prism::CallOrWriteNode).void }
83
+ def visit_call_or_write_node(node)
84
+ visit(node.receiver)
85
+ reference_method(node.read_name.to_s, node)
86
+ reference_method(node.write_name.to_s, node)
87
+ visit(node.value)
91
88
  end
92
89
 
93
- sig { override.params(node: SyntaxTree::CallNode).void }
94
- def visit_call(node)
90
+ sig { override.params(node: Prism::CallNode).void }
91
+ def visit_call_node(node)
95
92
  visit_send(
96
93
  Send.new(
97
94
  node: node,
98
- name: node_string(node.message),
95
+ name: node.name.to_s,
99
96
  recv: node.receiver,
100
- args: call_args(node.arguments),
97
+ args: node.arguments&.arguments || [],
98
+ block: node.block,
101
99
  ),
102
100
  )
103
101
  end
104
102
 
105
- sig { override.params(node: SyntaxTree::ClassDeclaration).void }
106
- def visit_class(node)
107
- const_name = node_string(node.constant)
108
- @names_nesting << const_name
109
- define_class(T.must(const_name.split("::").last), @names_nesting.join("::"), node)
103
+ sig { override.params(node: Prism::ClassNode).void }
104
+ def visit_class_node(node)
105
+ constant_path = node.constant_path.slice
110
106
 
111
- # We do not call `super` here because we don't want to visit the `constant` again
112
- visit(node.superclass) if node.superclass
113
- visit(node.bodystmt)
107
+ if constant_path.start_with?("::")
108
+ full_name = constant_path.delete_prefix("::")
114
109
 
115
- @names_nesting.pop
110
+ # We found a top level definition such as `class ::A; end`, we need to reset the name nesting
111
+ old_nesting = @names_nesting.dup
112
+ @names_nesting.clear
113
+ @names_nesting << full_name
114
+
115
+ define_class(T.must(constant_path.split("::").last), full_name, node)
116
+
117
+ # We do not call `super` here because we don't want to visit the `constant` again
118
+ visit(node.superclass) if node.superclass
119
+ visit(node.body)
120
+
121
+ # Restore the name nesting once we finished visited the class
122
+ @names_nesting.clear
123
+ @names_nesting = old_nesting
124
+ else
125
+ @names_nesting << constant_path
126
+ define_class(T.must(constant_path.split("::").last), @names_nesting.join("::"), node)
127
+
128
+ # We do not call `super` here because we don't want to visit the `constant` again
129
+ visit(node.superclass) if node.superclass
130
+ visit(node.body)
131
+
132
+ @names_nesting.pop
133
+ end
116
134
  end
117
135
 
118
- sig { override.params(node: SyntaxTree::Command).void }
119
- def visit_command(node)
120
- visit_send(
121
- Send.new(
122
- node: node,
123
- name: node_string(node.message),
124
- args: call_args(node.arguments),
125
- block: node.block,
126
- ),
127
- )
136
+ sig { override.params(node: Prism::ConstantAndWriteNode).void }
137
+ def visit_constant_and_write_node(node)
138
+ reference_constant(node.name.to_s, node)
139
+ visit(node.value)
128
140
  end
129
141
 
130
- sig { override.params(node: SyntaxTree::CommandCall).void }
131
- def visit_command_call(node)
132
- visit_send(
133
- Send.new(
134
- node: node,
135
- name: node_string(node.message),
136
- recv: node.receiver,
137
- args: call_args(node.arguments),
138
- block: node.block,
139
- ),
140
- )
142
+ sig { override.params(node: Prism::ConstantOperatorWriteNode).void }
143
+ def visit_constant_operator_write_node(node)
144
+ reference_constant(node.name.to_s, node)
145
+ visit(node.value)
146
+ end
147
+
148
+ sig { override.params(node: Prism::ConstantOrWriteNode).void }
149
+ def visit_constant_or_write_node(node)
150
+ reference_constant(node.name.to_s, node)
151
+ visit(node.value)
141
152
  end
142
153
 
143
- sig { override.params(node: SyntaxTree::Const).void }
144
- def visit_const(node)
145
- reference_constant(node.value, node) unless @in_symbol_literal
154
+ sig { override.params(node: Prism::ConstantPathWriteNode).void }
155
+ def visit_constant_path_write_node(node)
156
+ parent = node.target.parent
157
+ name = node.target.child.slice
158
+
159
+ if parent
160
+ visit(parent)
161
+
162
+ parent_name = parent.slice
163
+ full_name = [*@names_nesting, parent_name, name].compact.join("::")
164
+ define_constant(name, full_name, node)
165
+ else
166
+ define_constant(name, name, node)
167
+ end
168
+
169
+ visit(node.value)
146
170
  end
147
171
 
148
- sig { override.params(node: SyntaxTree::ConstPathField).void }
149
- def visit_const_path_field(node)
150
- # We do not call `super` here because we don't want to visit the `constant` again
151
- visit(node.parent)
172
+ sig { override.params(node: Prism::ConstantReadNode).void }
173
+ def visit_constant_read_node(node)
174
+ reference_constant(node.name.to_s, node)
175
+ end
152
176
 
153
- name = node.constant.value
154
- full_name = [*@names_nesting, node_string(node.parent), name].join("::")
177
+ sig { override.params(node: Prism::ConstantWriteNode).void }
178
+ def visit_constant_write_node(node)
179
+ name = node.name.to_s
180
+ full_name = [*@names_nesting, name].join("::")
155
181
  define_constant(name, full_name, node)
182
+ visit(node.value)
156
183
  end
157
184
 
158
- sig { override.params(node: SyntaxTree::DefNode).void }
159
- def visit_def(node)
185
+ sig { override.params(node: Prism::DefNode).void }
186
+ def visit_def_node(node)
187
+ name = node.name.to_s
188
+ define_method(name, [*@names_nesting, name].join("::"), node)
189
+
160
190
  super
191
+ end
161
192
 
162
- name = node_string(node.name)
163
- define_method(name, [*@names_nesting, name].join("::"), node)
193
+ sig { override.params(node: Prism::LocalVariableAndWriteNode).void }
194
+ def visit_local_variable_and_write_node(node)
195
+ name = node.name.to_s
196
+ reference_method(name, node)
197
+ reference_method("#{name}=", node)
198
+ visit(node.value)
164
199
  end
165
200
 
166
- sig { override.params(node: SyntaxTree::Field).void }
167
- def visit_field(node)
168
- visit(node.parent)
201
+ sig { override.params(node: Prism::LocalVariableOperatorWriteNode).void }
202
+ def visit_local_variable_operator_write_node(node)
203
+ name = node.name.to_s
204
+ reference_method(name, node)
205
+ reference_method("#{name}=", node)
206
+ visit(node.value)
207
+ end
169
208
 
170
- name = node.name
171
- case name
172
- when SyntaxTree::Const
173
- name = name.value
174
- full_name = [*@names_nesting, node_string(node.parent), name].join("::")
175
- define_constant(name, full_name, node)
176
- when SyntaxTree::Ident
177
- reference_method(name.value, node) if @in_opassign
178
- reference_method("#{name.value}=", node)
179
- end
209
+ sig { override.params(node: Prism::LocalVariableOrWriteNode).void }
210
+ def visit_local_variable_or_write_node(node)
211
+ name = node.name.to_s
212
+ reference_method(name, node)
213
+ reference_method("#{name}=", node)
214
+ visit(node.value)
180
215
  end
181
216
 
182
- sig { override.params(node: SyntaxTree::ModuleDeclaration).void }
183
- def visit_module(node)
184
- const_name = node_string(node.constant)
185
- @names_nesting << const_name
186
- define_module(T.must(const_name.split("::").last), @names_nesting.join("::"), node)
217
+ sig { override.params(node: Prism::LocalVariableWriteNode).void }
218
+ def visit_local_variable_write_node(node)
219
+ visit(node.value)
220
+ reference_method("#{node.name}=", node)
221
+ end
222
+
223
+ sig { override.params(node: Prism::ModuleNode).void }
224
+ def visit_module_node(node)
225
+ constant_path = node.constant_path.slice
226
+
227
+ if constant_path.start_with?("::")
228
+ full_name = constant_path.delete_prefix("::")
229
+
230
+ # We found a top level definition such as `class ::A; end`, we need to reset the name nesting
231
+ old_nesting = @names_nesting.dup
232
+ @names_nesting.clear
233
+ @names_nesting << full_name
234
+
235
+ define_module(T.must(constant_path.split("::").last), full_name, node)
236
+
237
+ visit(node.body)
187
238
 
188
- # We do not call `super` here because we don't want to visit the `constant` again
189
- visit(node.bodystmt)
239
+ # Restore the name nesting once we finished visited the class
240
+ @names_nesting.clear
241
+ @names_nesting = old_nesting
242
+ else
243
+ @names_nesting << constant_path
244
+ define_module(T.must(constant_path.split("::").last), @names_nesting.join("::"), node)
245
+
246
+ # We do not call `super` here because we don't want to visit the `constant` again
247
+ visit(node.body)
248
+
249
+ @names_nesting.pop
250
+ end
251
+ end
190
252
 
191
- @names_nesting.pop
253
+ sig { override.params(node: Prism::MultiWriteNode).void }
254
+ def visit_multi_write_node(node)
255
+ node.lefts.each do |const|
256
+ case const
257
+ when Prism::ConstantTargetNode, Prism::ConstantPathTargetNode
258
+ name = const.slice
259
+ define_constant(T.must(name.split("::").last), [*@names_nesting, name].join("::"), const)
260
+ when Prism::LocalVariableTargetNode
261
+ reference_method("#{const.name}=", node)
262
+ end
263
+ end
264
+ visit(node.value)
192
265
  end
193
266
 
194
- sig { override.params(node: SyntaxTree::OpAssign).void }
195
- def visit_opassign(node)
196
- # Both `FOO = x` and `FOO += x` yield a VarField node, but the former is a constant definition and the latter is
197
- # a constant reference. We need to distinguish between the two cases.
198
- @in_opassign = true
267
+ sig { override.params(node: Prism::OrNode).void }
268
+ def visit_or_node(node)
269
+ reference_method(node.operator_loc.slice, node)
199
270
  super
200
- @in_opassign = false
201
271
  end
202
272
 
203
273
  sig { params(send: Send).void }
@@ -207,77 +277,48 @@ module Spoom
207
277
  case send.name
208
278
  when "attr_reader"
209
279
  send.args.each do |arg|
210
- next unless arg.is_a?(SyntaxTree::SymbolLiteral)
280
+ next unless arg.is_a?(Prism::SymbolNode)
211
281
 
212
- name = symbol_string(arg)
282
+ name = arg.unescaped
213
283
  define_attr_reader(name, [*@names_nesting, name].join("::"), arg)
214
284
  end
215
285
  when "attr_writer"
216
286
  send.args.each do |arg|
217
- next unless arg.is_a?(SyntaxTree::SymbolLiteral)
287
+ next unless arg.is_a?(Prism::SymbolNode)
218
288
 
219
- name = symbol_string(arg)
289
+ name = arg.unescaped
220
290
  define_attr_writer("#{name}=", "#{[*@names_nesting, name].join("::")}=", arg)
221
291
  end
222
292
  when "attr_accessor"
223
293
  send.args.each do |arg|
224
- next unless arg.is_a?(SyntaxTree::SymbolLiteral)
294
+ next unless arg.is_a?(Prism::SymbolNode)
225
295
 
226
- name = symbol_string(arg)
296
+ name = arg.unescaped
227
297
  full_name = [*@names_nesting, name].join("::")
228
298
  define_attr_reader(name, full_name, arg)
229
299
  define_attr_writer("#{name}=", "#{full_name}=", arg)
230
300
  end
231
301
  else
232
302
  @plugins.each do |plugin|
233
- plugin.on_send(self, send)
303
+ plugin.internal_on_send(self, send)
234
304
  end
235
305
 
236
306
  reference_method(send.name, send.node)
237
- visit_all(send.args)
238
- visit(send.block)
239
- end
240
- end
241
-
242
- sig { override.params(node: SyntaxTree::SymbolLiteral).void }
243
- def visit_symbol_literal(node)
244
- # Something like `:FOO` will yield a Const node but we do not want to treat it as a constant reference.
245
- # So we need to distinguish between the two cases.
246
- @in_symbol_literal = true
247
- super
248
- @in_symbol_literal = false
249
- end
250
-
251
- sig { override.params(node: SyntaxTree::TopConstField).void }
252
- def visit_top_const_field(node)
253
- define_constant(node.constant.value, node.constant.value, node)
254
- end
255
307
 
256
- sig { override.params(node: SyntaxTree::VarField).void }
257
- def visit_var_field(node)
258
- value = node.value
259
- case value
260
- when SyntaxTree::Const
261
- if @in_opassign
262
- reference_constant(value.value, node)
263
- else
264
- name = value.value
265
- define_constant(name, [*@names_nesting, name].join("::"), node)
308
+ case send.name
309
+ when "<", ">", "<=", ">="
310
+ # For comparison operators, we also reference the `<=>` method
311
+ reference_method("<=>", send.node)
266
312
  end
267
- when SyntaxTree::Ident
268
- reference_method(value.value, node) if @in_opassign
269
- reference_method("#{value.value}=", node)
270
- end
271
- end
272
313
 
273
- sig { override.params(node: SyntaxTree::VCall).void }
274
- def visit_vcall(node)
275
- visit_send(Send.new(node: node, name: node_string(node.value)))
314
+ visit_all(send.args)
315
+ visit(send.block)
316
+ end
276
317
  end
277
318
 
278
319
  # Definition indexing
279
320
 
280
- sig { params(name: String, full_name: String, node: SyntaxTree::Node).void }
321
+ sig { params(name: String, full_name: String, node: Prism::Node).void }
281
322
  def define_attr_reader(name, full_name, node)
282
323
  definition = Definition.new(
283
324
  kind: Definition::Kind::AttrReader,
@@ -286,10 +327,10 @@ module Spoom
286
327
  location: node_location(node),
287
328
  )
288
329
  @index.define(definition)
289
- @plugins.each { |plugin| plugin.on_define_accessor(self, definition) }
330
+ @plugins.each { |plugin| plugin.internal_on_define_accessor(self, definition) }
290
331
  end
291
332
 
292
- sig { params(name: String, full_name: String, node: SyntaxTree::Node).void }
333
+ sig { params(name: String, full_name: String, node: Prism::Node).void }
293
334
  def define_attr_writer(name, full_name, node)
294
335
  definition = Definition.new(
295
336
  kind: Definition::Kind::AttrWriter,
@@ -298,10 +339,10 @@ module Spoom
298
339
  location: node_location(node),
299
340
  )
300
341
  @index.define(definition)
301
- @plugins.each { |plugin| plugin.on_define_accessor(self, definition) }
342
+ @plugins.each { |plugin| plugin.internal_on_define_accessor(self, definition) }
302
343
  end
303
344
 
304
- sig { params(name: String, full_name: String, node: SyntaxTree::Node).void }
345
+ sig { params(name: String, full_name: String, node: Prism::Node).void }
305
346
  def define_class(name, full_name, node)
306
347
  definition = Definition.new(
307
348
  kind: Definition::Kind::Class,
@@ -310,10 +351,10 @@ module Spoom
310
351
  location: node_location(node),
311
352
  )
312
353
  @index.define(definition)
313
- @plugins.each { |plugin| plugin.on_define_class(self, definition) }
354
+ @plugins.each { |plugin| plugin.internal_on_define_class(self, definition) }
314
355
  end
315
356
 
316
- sig { params(name: String, full_name: String, node: SyntaxTree::Node).void }
357
+ sig { params(name: String, full_name: String, node: Prism::Node).void }
317
358
  def define_constant(name, full_name, node)
318
359
  definition = Definition.new(
319
360
  kind: Definition::Kind::Constant,
@@ -322,10 +363,10 @@ module Spoom
322
363
  location: node_location(node),
323
364
  )
324
365
  @index.define(definition)
325
- @plugins.each { |plugin| plugin.on_define_constant(self, definition) }
366
+ @plugins.each { |plugin| plugin.internal_on_define_constant(self, definition) }
326
367
  end
327
368
 
328
- sig { params(name: String, full_name: String, node: SyntaxTree::Node).void }
369
+ sig { params(name: String, full_name: String, node: Prism::Node).void }
329
370
  def define_method(name, full_name, node)
330
371
  definition = Definition.new(
331
372
  kind: Definition::Kind::Method,
@@ -334,10 +375,10 @@ module Spoom
334
375
  location: node_location(node),
335
376
  )
336
377
  @index.define(definition)
337
- @plugins.each { |plugin| plugin.on_define_method(self, definition) }
378
+ @plugins.each { |plugin| plugin.internal_on_define_method(self, definition) }
338
379
  end
339
380
 
340
- sig { params(name: String, full_name: String, node: SyntaxTree::Node).void }
381
+ sig { params(name: String, full_name: String, node: Prism::Node).void }
341
382
  def define_module(name, full_name, node)
342
383
  definition = Definition.new(
343
384
  kind: Definition::Kind::Module,
@@ -346,57 +387,82 @@ module Spoom
346
387
  location: node_location(node),
347
388
  )
348
389
  @index.define(definition)
349
- @plugins.each { |plugin| plugin.on_define_module(self, definition) }
390
+ @plugins.each { |plugin| plugin.internal_on_define_module(self, definition) }
350
391
  end
351
392
 
352
393
  # Reference indexing
353
394
 
354
- sig { params(name: String, node: SyntaxTree::Node).void }
395
+ sig { params(name: String, node: Prism::Node).void }
355
396
  def reference_constant(name, node)
356
397
  @index.reference(Reference.new(name: name, kind: Reference::Kind::Constant, location: node_location(node)))
357
398
  end
358
399
 
359
- sig { params(name: String, node: SyntaxTree::Node).void }
400
+ sig { params(name: String, node: Prism::Node).void }
360
401
  def reference_method(name, node)
361
402
  @index.reference(Reference.new(name: name, kind: Reference::Kind::Method, location: node_location(node)))
362
403
  end
363
404
 
364
- # Node utils
405
+ # Context
365
406
 
366
- sig { params(node: T.any(Symbol, SyntaxTree::Node)).returns(String) }
367
- def node_string(node)
368
- case node
369
- when Symbol
370
- node.to_s
371
- else
372
- T.must(@source[node.location.start_char...node.location.end_char])
407
+ sig { returns(Prism::Node) }
408
+ def current_node
409
+ T.must(@nodes_nesting.last)
410
+ end
411
+
412
+ sig { type_parameters(:N).params(type: T::Class[T.type_parameter(:N)]).returns(T.nilable(T.type_parameter(:N))) }
413
+ def nesting_node(type)
414
+ @nodes_nesting.reverse_each do |node|
415
+ return T.unsafe(node) if node.is_a?(type)
373
416
  end
417
+
418
+ nil
374
419
  end
375
420
 
376
- sig { params(node: SyntaxTree::Node).returns(Location) }
377
- def node_location(node)
378
- Location.from_syntax_tree(@path, node.location)
421
+ sig { returns(T.nilable(Prism::ClassNode)) }
422
+ def nesting_class
423
+ nesting_node(Prism::ClassNode)
379
424
  end
380
425
 
381
- sig { params(node: SyntaxTree::Node).returns(String) }
382
- def symbol_string(node)
383
- node_string(node).delete_prefix(":")
426
+ sig { returns(T.nilable(Prism::BlockNode)) }
427
+ def nesting_block
428
+ nesting_node(Prism::BlockNode)
384
429
  end
385
430
 
386
- sig do
387
- params(
388
- node: T.any(SyntaxTree::Args, SyntaxTree::ArgParen, SyntaxTree::ArgsForward, NilClass),
389
- ).returns(T::Array[SyntaxTree::Node])
431
+ sig { returns(T.nilable(Prism::CallNode)) }
432
+ def nesting_call
433
+ nesting_node(Prism::CallNode)
390
434
  end
391
- def call_args(node)
392
- case node
393
- when SyntaxTree::ArgParen
394
- call_args(node.arguments)
395
- when SyntaxTree::Args
396
- node.parts
397
- else
398
- []
399
- end
435
+
436
+ sig { returns(T.nilable(String)) }
437
+ def nesting_class_name
438
+ nesting_class = self.nesting_class
439
+ return unless nesting_class
440
+
441
+ nesting_class.name.to_s
442
+ end
443
+
444
+ sig { returns(T.nilable(String)) }
445
+ def nesting_class_superclass_name
446
+ nesting_class_superclass = nesting_class&.superclass
447
+ return unless nesting_class_superclass
448
+
449
+ nesting_class_superclass.slice.delete_prefix("::")
450
+ end
451
+
452
+ sig { returns(T.nilable(String)) }
453
+ def last_sig
454
+ previous_call = @previous_node
455
+ return unless previous_call.is_a?(Prism::CallNode)
456
+ return unless previous_call.name == :sig
457
+
458
+ previous_call.slice
459
+ end
460
+
461
+ # Node utils
462
+
463
+ sig { params(node: Prism::Node).returns(Location) }
464
+ def node_location(node)
465
+ Location.from_prism(@path, node.location)
400
466
  end
401
467
  end
402
468
  end