spoom 1.3.1 → 1.3.3
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.
- checksums.yaml +4 -4
- data/lib/spoom/cli/deadcode.rb +21 -17
- data/lib/spoom/deadcode/index.rb +178 -10
- data/lib/spoom/deadcode/indexer.rb +14 -435
- data/lib/spoom/deadcode/plugins/action_mailer.rb +3 -3
- data/lib/spoom/deadcode/plugins/action_mailer_preview.rb +9 -3
- data/lib/spoom/deadcode/plugins/actionpack.rb +12 -9
- data/lib/spoom/deadcode/plugins/active_model.rb +8 -8
- data/lib/spoom/deadcode/plugins/active_record.rb +5 -5
- data/lib/spoom/deadcode/plugins/active_support.rb +4 -4
- data/lib/spoom/deadcode/plugins/base.rb +70 -57
- data/lib/spoom/deadcode/plugins/graphql.rb +8 -8
- data/lib/spoom/deadcode/plugins/minitest.rb +4 -3
- data/lib/spoom/deadcode/plugins/namespaces.rb +9 -12
- data/lib/spoom/deadcode/plugins/rails.rb +9 -9
- data/lib/spoom/deadcode/plugins/rubocop.rb +13 -17
- data/lib/spoom/deadcode/plugins/ruby.rb +9 -9
- data/lib/spoom/deadcode/plugins/sorbet.rb +15 -18
- data/lib/spoom/deadcode/plugins/thor.rb +5 -4
- data/lib/spoom/deadcode/plugins.rb +4 -5
- data/lib/spoom/deadcode/remover.rb +14 -10
- data/lib/spoom/deadcode/send.rb +1 -0
- data/lib/spoom/deadcode.rb +4 -73
- data/lib/spoom/location.rb +84 -0
- data/lib/spoom/model/builder.rb +246 -0
- data/lib/spoom/model/model.rb +328 -0
- data/lib/spoom/model/namespace_visitor.rb +50 -0
- data/lib/spoom/model/reference.rb +49 -0
- data/lib/spoom/model/references_visitor.rb +200 -0
- data/lib/spoom/model.rb +10 -0
- data/lib/spoom/parse.rb +28 -0
- data/lib/spoom/poset.rb +197 -0
- data/lib/spoom/sorbet/errors.rb +5 -3
- data/lib/spoom/sorbet/lsp/errors.rb +1 -1
- data/lib/spoom/sorbet.rb +1 -1
- data/lib/spoom/version.rb +1 -1
- data/lib/spoom/visitor.rb +755 -0
- data/lib/spoom.rb +2 -0
- metadata +20 -13
- data/lib/spoom/deadcode/location.rb +0 -86
- data/lib/spoom/deadcode/reference.rb +0 -34
- data/lib/spoom/deadcode/visitor.rb +0 -755
@@ -12,457 +12,36 @@ module Spoom
|
|
12
12
|
sig { returns(Index) }
|
13
13
|
attr_reader :index
|
14
14
|
|
15
|
-
sig { params(path: String,
|
16
|
-
def initialize(path,
|
15
|
+
sig { params(path: String, index: Index, plugins: T::Array[Plugins::Base]).void }
|
16
|
+
def initialize(path, index, plugins: [])
|
17
17
|
super()
|
18
18
|
|
19
19
|
@path = path
|
20
|
-
@file_name = T.let(File.basename(path), String)
|
21
|
-
@source = source
|
22
20
|
@index = index
|
23
21
|
@plugins = plugins
|
24
|
-
@previous_node = T.let(nil, T.nilable(Prism::Node))
|
25
|
-
@names_nesting = T.let([], T::Array[String])
|
26
|
-
@nodes_nesting = T.let([], T::Array[Prism::Node])
|
27
|
-
@in_const_field = T.let(false, T::Boolean)
|
28
|
-
@in_opassign = T.let(false, T::Boolean)
|
29
|
-
@in_symbol_literal = T.let(false, T::Boolean)
|
30
22
|
end
|
31
23
|
|
32
24
|
# Visit
|
33
25
|
|
34
|
-
sig { override.params(node: T.nilable(Prism::Node)).void }
|
35
|
-
def visit(node)
|
36
|
-
return unless node
|
37
|
-
|
38
|
-
@nodes_nesting << node
|
39
|
-
super
|
40
|
-
@nodes_nesting.pop
|
41
|
-
@previous_node = node
|
42
|
-
end
|
43
|
-
|
44
|
-
sig { override.params(node: Prism::AliasMethodNode).void }
|
45
|
-
def visit_alias_method_node(node)
|
46
|
-
reference_method(node.old_name.slice, node)
|
47
|
-
end
|
48
|
-
|
49
|
-
sig { override.params(node: Prism::AndNode).void }
|
50
|
-
def visit_and_node(node)
|
51
|
-
reference_method(node.operator_loc.slice, node)
|
52
|
-
super
|
53
|
-
end
|
54
|
-
|
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)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
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
|
73
|
-
|
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
|
81
|
-
|
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)
|
88
|
-
end
|
89
|
-
|
90
26
|
sig { override.params(node: Prism::CallNode).void }
|
91
27
|
def visit_call_node(node)
|
92
|
-
|
93
|
-
Send.new(
|
94
|
-
node: node,
|
95
|
-
name: node.name.to_s,
|
96
|
-
recv: node.receiver,
|
97
|
-
args: node.arguments&.arguments || [],
|
98
|
-
block: node.block,
|
99
|
-
),
|
100
|
-
)
|
101
|
-
end
|
102
|
-
|
103
|
-
sig { override.params(node: Prism::ClassNode).void }
|
104
|
-
def visit_class_node(node)
|
105
|
-
constant_path = node.constant_path.slice
|
106
|
-
|
107
|
-
if constant_path.start_with?("::")
|
108
|
-
full_name = constant_path.delete_prefix("::")
|
109
|
-
|
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
|
134
|
-
end
|
135
|
-
|
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)
|
140
|
-
end
|
141
|
-
|
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)
|
152
|
-
end
|
153
|
-
|
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)
|
170
|
-
end
|
171
|
-
|
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
|
176
|
-
|
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("::")
|
181
|
-
define_constant(name, full_name, node)
|
182
|
-
visit(node.value)
|
183
|
-
end
|
184
|
-
|
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
|
-
|
190
|
-
super
|
191
|
-
end
|
192
|
-
|
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)
|
199
|
-
end
|
200
|
-
|
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
|
208
|
-
|
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)
|
215
|
-
end
|
216
|
-
|
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)
|
238
|
-
|
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
|
252
|
-
|
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)
|
265
|
-
end
|
266
|
-
|
267
|
-
sig { override.params(node: Prism::OrNode).void }
|
268
|
-
def visit_or_node(node)
|
269
|
-
reference_method(node.operator_loc.slice, node)
|
270
|
-
super
|
271
|
-
end
|
272
|
-
|
273
|
-
sig { params(send: Send).void }
|
274
|
-
def visit_send(send)
|
275
|
-
visit(send.recv)
|
276
|
-
|
277
|
-
case send.name
|
278
|
-
when "attr_reader"
|
279
|
-
send.args.each do |arg|
|
280
|
-
next unless arg.is_a?(Prism::SymbolNode)
|
281
|
-
|
282
|
-
name = arg.unescaped
|
283
|
-
define_attr_reader(name, [*@names_nesting, name].join("::"), arg)
|
284
|
-
end
|
285
|
-
when "attr_writer"
|
286
|
-
send.args.each do |arg|
|
287
|
-
next unless arg.is_a?(Prism::SymbolNode)
|
288
|
-
|
289
|
-
name = arg.unescaped
|
290
|
-
define_attr_writer("#{name}=", "#{[*@names_nesting, name].join("::")}=", arg)
|
291
|
-
end
|
292
|
-
when "attr_accessor"
|
293
|
-
send.args.each do |arg|
|
294
|
-
next unless arg.is_a?(Prism::SymbolNode)
|
295
|
-
|
296
|
-
name = arg.unescaped
|
297
|
-
full_name = [*@names_nesting, name].join("::")
|
298
|
-
define_attr_reader(name, full_name, arg)
|
299
|
-
define_attr_writer("#{name}=", "#{full_name}=", arg)
|
300
|
-
end
|
301
|
-
else
|
302
|
-
@plugins.each do |plugin|
|
303
|
-
plugin.internal_on_send(self, send)
|
304
|
-
end
|
305
|
-
|
306
|
-
reference_method(send.name, send.node)
|
307
|
-
|
308
|
-
case send.name
|
309
|
-
when "<", ">", "<=", ">="
|
310
|
-
# For comparison operators, we also reference the `<=>` method
|
311
|
-
reference_method("<=>", send.node)
|
312
|
-
end
|
313
|
-
|
314
|
-
visit_all(send.args)
|
315
|
-
visit(send.block)
|
316
|
-
end
|
317
|
-
end
|
318
|
-
|
319
|
-
# Definition indexing
|
320
|
-
|
321
|
-
sig { params(name: String, full_name: String, node: Prism::Node).void }
|
322
|
-
def define_attr_reader(name, full_name, node)
|
323
|
-
definition = Definition.new(
|
324
|
-
kind: Definition::Kind::AttrReader,
|
325
|
-
name: name,
|
326
|
-
full_name: full_name,
|
327
|
-
location: node_location(node),
|
328
|
-
)
|
329
|
-
@index.define(definition)
|
330
|
-
@plugins.each { |plugin| plugin.internal_on_define_accessor(self, definition) }
|
331
|
-
end
|
332
|
-
|
333
|
-
sig { params(name: String, full_name: String, node: Prism::Node).void }
|
334
|
-
def define_attr_writer(name, full_name, node)
|
335
|
-
definition = Definition.new(
|
336
|
-
kind: Definition::Kind::AttrWriter,
|
337
|
-
name: name,
|
338
|
-
full_name: full_name,
|
339
|
-
location: node_location(node),
|
340
|
-
)
|
341
|
-
@index.define(definition)
|
342
|
-
@plugins.each { |plugin| plugin.internal_on_define_accessor(self, definition) }
|
343
|
-
end
|
344
|
-
|
345
|
-
sig { params(name: String, full_name: String, node: Prism::Node).void }
|
346
|
-
def define_class(name, full_name, node)
|
347
|
-
definition = Definition.new(
|
348
|
-
kind: Definition::Kind::Class,
|
349
|
-
name: name,
|
350
|
-
full_name: full_name,
|
351
|
-
location: node_location(node),
|
352
|
-
)
|
353
|
-
@index.define(definition)
|
354
|
-
@plugins.each { |plugin| plugin.internal_on_define_class(self, definition) }
|
355
|
-
end
|
356
|
-
|
357
|
-
sig { params(name: String, full_name: String, node: Prism::Node).void }
|
358
|
-
def define_constant(name, full_name, node)
|
359
|
-
definition = Definition.new(
|
360
|
-
kind: Definition::Kind::Constant,
|
361
|
-
name: name,
|
362
|
-
full_name: full_name,
|
363
|
-
location: node_location(node),
|
364
|
-
)
|
365
|
-
@index.define(definition)
|
366
|
-
@plugins.each { |plugin| plugin.internal_on_define_constant(self, definition) }
|
367
|
-
end
|
368
|
-
|
369
|
-
sig { params(name: String, full_name: String, node: Prism::Node).void }
|
370
|
-
def define_method(name, full_name, node)
|
371
|
-
definition = Definition.new(
|
372
|
-
kind: Definition::Kind::Method,
|
373
|
-
name: name,
|
374
|
-
full_name: full_name,
|
375
|
-
location: node_location(node),
|
376
|
-
)
|
377
|
-
@index.define(definition)
|
378
|
-
@plugins.each { |plugin| plugin.internal_on_define_method(self, definition) }
|
379
|
-
end
|
28
|
+
visit(node.receiver)
|
380
29
|
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
location:
|
30
|
+
send = Send.new(
|
31
|
+
node: node,
|
32
|
+
name: node.name.to_s,
|
33
|
+
recv: node.receiver,
|
34
|
+
args: node.arguments&.arguments || [],
|
35
|
+
block: node.block,
|
36
|
+
location: Location.from_prism(@path, node.location),
|
388
37
|
)
|
389
|
-
@index.define(definition)
|
390
|
-
@plugins.each { |plugin| plugin.internal_on_define_module(self, definition) }
|
391
|
-
end
|
392
|
-
|
393
|
-
# Reference indexing
|
394
|
-
|
395
|
-
sig { params(name: String, node: Prism::Node).void }
|
396
|
-
def reference_constant(name, node)
|
397
|
-
@index.reference(Reference.new(name: name, kind: Reference::Kind::Constant, location: node_location(node)))
|
398
|
-
end
|
399
38
|
|
400
|
-
|
401
|
-
|
402
|
-
@index.reference(Reference.new(name: name, kind: Reference::Kind::Method, location: node_location(node)))
|
403
|
-
end
|
404
|
-
|
405
|
-
# Context
|
406
|
-
|
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)
|
39
|
+
@plugins.each do |plugin|
|
40
|
+
plugin.on_send(send)
|
416
41
|
end
|
417
42
|
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
sig { returns(T.nilable(Prism::ClassNode)) }
|
422
|
-
def nesting_class
|
423
|
-
nesting_node(Prism::ClassNode)
|
424
|
-
end
|
425
|
-
|
426
|
-
sig { returns(T.nilable(Prism::BlockNode)) }
|
427
|
-
def nesting_block
|
428
|
-
nesting_node(Prism::BlockNode)
|
429
|
-
end
|
430
|
-
|
431
|
-
sig { returns(T.nilable(Prism::CallNode)) }
|
432
|
-
def nesting_call
|
433
|
-
nesting_node(Prism::CallNode)
|
434
|
-
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)
|
43
|
+
visit(node.arguments)
|
44
|
+
visit(send.block)
|
466
45
|
end
|
467
46
|
end
|
468
47
|
end
|
@@ -7,12 +7,12 @@ module Spoom
|
|
7
7
|
class ActionMailer < Base
|
8
8
|
extend T::Sig
|
9
9
|
|
10
|
-
sig { override.params(
|
11
|
-
def on_send(
|
10
|
+
sig { override.params(send: Send).void }
|
11
|
+
def on_send(send)
|
12
12
|
return unless send.recv.nil? && ActionPack::CALLBACKS.include?(send.name)
|
13
13
|
|
14
14
|
send.each_arg(Prism::SymbolNode) do |arg|
|
15
|
-
|
15
|
+
@index.reference_method(arg.unescaped, send.location)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
@@ -9,9 +9,15 @@ module Spoom
|
|
9
9
|
|
10
10
|
ignore_classes_inheriting_from("ActionMailer::Preview")
|
11
11
|
|
12
|
-
sig { override.params(
|
13
|
-
def on_define_method(
|
14
|
-
|
12
|
+
sig { override.params(definition: Model::Method).void }
|
13
|
+
def on_define_method(definition)
|
14
|
+
owner = definition.owner
|
15
|
+
return unless owner.is_a?(Model::Class)
|
16
|
+
|
17
|
+
superclass_name = owner.superclass_name
|
18
|
+
return unless superclass_name
|
19
|
+
|
20
|
+
@index.ignore(definition) if superclass_name == "ActionMailer::Preview"
|
15
21
|
end
|
16
22
|
end
|
17
23
|
end
|
@@ -7,6 +7,8 @@ module Spoom
|
|
7
7
|
class ActionPack < Base
|
8
8
|
extend T::Sig
|
9
9
|
|
10
|
+
ignore_classes_inheriting_from("ApplicationController")
|
11
|
+
|
10
12
|
CALLBACKS = T.let(
|
11
13
|
[
|
12
14
|
"after_action",
|
@@ -25,21 +27,22 @@ module Spoom
|
|
25
27
|
T::Array[String],
|
26
28
|
)
|
27
29
|
|
28
|
-
|
30
|
+
sig { override.params(definition: Model::Method).void }
|
31
|
+
def on_define_method(definition)
|
32
|
+
owner = definition.owner
|
33
|
+
return unless owner.is_a?(Model::Class)
|
29
34
|
|
30
|
-
|
31
|
-
def on_define_method(indexer, definition)
|
32
|
-
definition.ignored! if ignored_class_name?(indexer.nesting_class_name)
|
35
|
+
@index.ignore(definition) if ignored_subclass?(owner)
|
33
36
|
end
|
34
37
|
|
35
|
-
sig { override.params(
|
36
|
-
def on_send(
|
38
|
+
sig { override.params(send: Send).void }
|
39
|
+
def on_send(send)
|
37
40
|
return unless send.recv.nil? && CALLBACKS.include?(send.name)
|
38
41
|
|
39
42
|
arg = send.args.first
|
40
43
|
case arg
|
41
44
|
when Prism::SymbolNode
|
42
|
-
|
45
|
+
@index.reference_method(arg.unescaped, send.location)
|
43
46
|
end
|
44
47
|
|
45
48
|
send.each_arg_assoc do |key, value|
|
@@ -47,9 +50,9 @@ module Spoom
|
|
47
50
|
|
48
51
|
case key
|
49
52
|
when "if", "unless"
|
50
|
-
|
53
|
+
@index.reference_method(value.slice.delete_prefix(":"), send.location) if value
|
51
54
|
else
|
52
|
-
|
55
|
+
@index.reference_constant(camelize(key), send.location)
|
53
56
|
end
|
54
57
|
end
|
55
58
|
end
|
@@ -7,36 +7,36 @@ module Spoom
|
|
7
7
|
class ActiveModel < Base
|
8
8
|
extend T::Sig
|
9
9
|
|
10
|
-
ignore_classes_inheriting_from(
|
10
|
+
ignore_classes_inheriting_from("ActiveModel::EachValidator")
|
11
11
|
ignore_methods_named("validate_each")
|
12
12
|
|
13
|
-
sig { override.params(
|
14
|
-
def on_send(
|
13
|
+
sig { override.params(send: Send).void }
|
14
|
+
def on_send(send)
|
15
15
|
return if send.recv
|
16
16
|
|
17
17
|
case send.name
|
18
18
|
when "attribute", "attributes"
|
19
19
|
send.each_arg(Prism::SymbolNode) do |arg|
|
20
|
-
|
20
|
+
@index.reference_method(arg.unescaped, send.location)
|
21
21
|
end
|
22
22
|
when "validate", "validates", "validates!", "validates_each"
|
23
23
|
send.each_arg(Prism::SymbolNode) do |arg|
|
24
|
-
|
24
|
+
@index.reference_method(arg.unescaped, send.location)
|
25
25
|
end
|
26
26
|
send.each_arg_assoc do |key, value|
|
27
27
|
key = key.slice.delete_suffix(":")
|
28
28
|
|
29
29
|
case key
|
30
30
|
when "if", "unless"
|
31
|
-
|
31
|
+
@index.reference_method(value.slice.delete_prefix(":"), send.location) if value
|
32
32
|
else
|
33
|
-
|
33
|
+
@index.reference_constant(camelize(key), send.location)
|
34
34
|
end
|
35
35
|
end
|
36
36
|
when "validates_with"
|
37
37
|
arg = send.args.first
|
38
38
|
if arg.is_a?(Prism::SymbolNode)
|
39
|
-
|
39
|
+
@index.reference_constant(arg.unescaped, send.location)
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
@@ -70,11 +70,11 @@ module Spoom
|
|
70
70
|
T::Array[String],
|
71
71
|
)
|
72
72
|
|
73
|
-
sig { override.params(
|
74
|
-
def on_send(
|
73
|
+
sig { override.params(send: Send).void }
|
74
|
+
def on_send(send)
|
75
75
|
if send.recv.nil? && CALLBACKS.include?(send.name)
|
76
76
|
send.each_arg(Prism::SymbolNode) do |arg|
|
77
|
-
|
77
|
+
@index.reference_method(arg.unescaped, send.location)
|
78
78
|
end
|
79
79
|
return
|
80
80
|
end
|
@@ -85,7 +85,7 @@ module Spoom
|
|
85
85
|
when *CRUD_METHODS
|
86
86
|
send.each_arg_assoc do |key, _value|
|
87
87
|
key = key.slice.delete_suffix(":")
|
88
|
-
|
88
|
+
@index.reference_method("#{key}=", send.location)
|
89
89
|
end
|
90
90
|
when *ARRAY_METHODS
|
91
91
|
send.each_arg(Prism::ArrayNode) do |arg|
|
@@ -96,7 +96,7 @@ module Spoom
|
|
96
96
|
next unless assoc.is_a?(Prism::AssocNode)
|
97
97
|
|
98
98
|
key = assoc.key.slice.delete_suffix(":")
|
99
|
-
|
99
|
+
@index.reference_method("#{key}=", send.location)
|
100
100
|
end
|
101
101
|
end
|
102
102
|
end
|
@@ -5,7 +5,7 @@ module Spoom
|
|
5
5
|
module Deadcode
|
6
6
|
module Plugins
|
7
7
|
class ActiveSupport < Base
|
8
|
-
ignore_classes_inheriting_from(
|
8
|
+
ignore_classes_inheriting_from("ActiveSupport::TestCase")
|
9
9
|
|
10
10
|
ignore_methods_named(
|
11
11
|
"after_all",
|
@@ -18,12 +18,12 @@ module Spoom
|
|
18
18
|
|
19
19
|
SETUP_AND_TEARDOWN_METHODS = T.let(["setup", "teardown"], T::Array[String])
|
20
20
|
|
21
|
-
sig { override.params(
|
22
|
-
def on_send(
|
21
|
+
sig { override.params(send: Send).void }
|
22
|
+
def on_send(send)
|
23
23
|
return unless send.recv.nil? && SETUP_AND_TEARDOWN_METHODS.include?(send.name)
|
24
24
|
|
25
25
|
send.each_arg(Prism::SymbolNode) do |arg|
|
26
|
-
|
26
|
+
@index.reference_method(T.must(arg.value), send.location)
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|