spoom 1.5.4 → 1.6.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.
- checksums.yaml +4 -4
- data/lib/spoom/backtrace_filter/minitest.rb +2 -3
- data/lib/spoom/cli/deadcode.rb +1 -2
- data/lib/spoom/cli/helper.rb +36 -28
- data/lib/spoom/cli/srb/assertions.rb +48 -0
- data/lib/spoom/cli/srb/bump.rb +1 -2
- data/lib/spoom/cli/srb/sigs.rb +133 -18
- data/lib/spoom/cli/srb.rb +8 -4
- data/lib/spoom/cli.rb +1 -2
- data/lib/spoom/colors.rb +2 -6
- data/lib/spoom/context/bundle.rb +8 -9
- data/lib/spoom/context/exec.rb +2 -5
- data/lib/spoom/context/file_system.rb +12 -19
- data/lib/spoom/context/git.rb +14 -19
- data/lib/spoom/context/sorbet.rb +13 -26
- data/lib/spoom/context.rb +3 -7
- data/lib/spoom/coverage/d3/base.rb +6 -8
- data/lib/spoom/coverage/d3/circle_map.rb +6 -16
- data/lib/spoom/coverage/d3/pie.rb +14 -19
- data/lib/spoom/coverage/d3/timeline.rb +46 -47
- data/lib/spoom/coverage/d3.rb +2 -4
- data/lib/spoom/coverage/report.rb +38 -76
- data/lib/spoom/coverage/snapshot.rb +7 -13
- data/lib/spoom/coverage.rb +3 -5
- data/lib/spoom/deadcode/definition.rb +12 -14
- data/lib/spoom/deadcode/erb.rb +10 -8
- data/lib/spoom/deadcode/index.rb +19 -23
- data/lib/spoom/deadcode/indexer.rb +5 -6
- data/lib/spoom/deadcode/plugins/action_mailer.rb +2 -3
- data/lib/spoom/deadcode/plugins/action_mailer_preview.rb +2 -3
- data/lib/spoom/deadcode/plugins/actionpack.rb +4 -4
- data/lib/spoom/deadcode/plugins/active_model.rb +2 -3
- data/lib/spoom/deadcode/plugins/active_record.rb +2 -3
- data/lib/spoom/deadcode/plugins/active_support.rb +2 -1
- data/lib/spoom/deadcode/plugins/base.rb +29 -32
- data/lib/spoom/deadcode/plugins/graphql.rb +2 -3
- data/lib/spoom/deadcode/plugins/minitest.rb +4 -4
- data/lib/spoom/deadcode/plugins/namespaces.rb +5 -5
- data/lib/spoom/deadcode/plugins/rails.rb +5 -5
- data/lib/spoom/deadcode/plugins/rubocop.rb +4 -4
- data/lib/spoom/deadcode/plugins/ruby.rb +3 -4
- data/lib/spoom/deadcode/plugins/sorbet.rb +12 -6
- data/lib/spoom/deadcode/plugins/thor.rb +2 -3
- data/lib/spoom/deadcode/plugins.rb +2 -4
- data/lib/spoom/deadcode/remover.rb +37 -59
- data/lib/spoom/deadcode/send.rb +2 -8
- data/lib/spoom/file_collector.rb +10 -18
- data/lib/spoom/file_tree.rb +31 -46
- data/lib/spoom/location.rb +9 -20
- data/lib/spoom/model/builder.rb +60 -15
- data/lib/spoom/model/model.rb +65 -68
- data/lib/spoom/model/namespace_visitor.rb +3 -2
- data/lib/spoom/model/reference.rb +4 -8
- data/lib/spoom/model/references_visitor.rb +49 -29
- data/lib/spoom/parse.rb +17 -3
- data/lib/spoom/poset.rb +17 -19
- data/lib/spoom/printer.rb +10 -13
- data/lib/spoom/sorbet/assertions.rb +278 -0
- data/lib/spoom/sorbet/config.rb +8 -12
- data/lib/spoom/sorbet/errors.rb +16 -31
- data/lib/spoom/sorbet/lsp/base.rb +9 -15
- data/lib/spoom/sorbet/lsp/errors.rb +8 -16
- data/lib/spoom/sorbet/lsp/structures.rb +36 -59
- data/lib/spoom/sorbet/lsp.rb +15 -17
- data/lib/spoom/sorbet/metrics.rb +3 -5
- data/lib/spoom/sorbet/sigils.rb +7 -11
- data/lib/spoom/sorbet/sigs.rb +118 -25
- data/lib/spoom/sorbet.rb +3 -9
- data/lib/spoom/timeline.rb +4 -6
- data/lib/spoom/version.rb +1 -1
- data/lib/spoom/visitor.rb +298 -151
- data/lib/spoom.rb +0 -2
- data/rbi/spoom.rbi +3963 -0
- metadata +6 -3
@@ -5,8 +5,6 @@ module Spoom
|
|
5
5
|
module Deadcode
|
6
6
|
module Plugins
|
7
7
|
class GraphQL < Base
|
8
|
-
extend T::Sig
|
9
|
-
|
10
8
|
ignore_classes_inheriting_from(
|
11
9
|
"GraphQL::Schema::Enum",
|
12
10
|
"GraphQL::Schema::Object",
|
@@ -24,7 +22,8 @@ module Spoom
|
|
24
22
|
"unsubscribed",
|
25
23
|
)
|
26
24
|
|
27
|
-
|
25
|
+
# @override
|
26
|
+
#: (Send send) -> void
|
28
27
|
def on_send(send)
|
29
28
|
return unless send.recv.nil? && send.name == "field"
|
30
29
|
|
@@ -5,8 +5,6 @@ module Spoom
|
|
5
5
|
module Deadcode
|
6
6
|
module Plugins
|
7
7
|
class Minitest < Base
|
8
|
-
extend T::Sig
|
9
|
-
|
10
8
|
ignore_classes_named(/Test$/)
|
11
9
|
|
12
10
|
ignore_methods_named(
|
@@ -18,13 +16,15 @@ module Spoom
|
|
18
16
|
"teardown",
|
19
17
|
)
|
20
18
|
|
21
|
-
|
19
|
+
# @override
|
20
|
+
#: (Model::Method definition) -> void
|
22
21
|
def on_define_method(definition)
|
23
22
|
file = definition.location.file
|
24
23
|
@index.ignore(definition) if file.match?(%r{test/.*test\.rb$}) && definition.name.match?(/^test_/)
|
25
24
|
end
|
26
25
|
|
27
|
-
|
26
|
+
# @override
|
27
|
+
#: (Send send) -> void
|
28
28
|
def on_send(send)
|
29
29
|
case send.name
|
30
30
|
when "assert_predicate", "refute_predicate"
|
@@ -5,21 +5,21 @@ module Spoom
|
|
5
5
|
module Deadcode
|
6
6
|
module Plugins
|
7
7
|
class Namespaces < Base
|
8
|
-
|
9
|
-
|
10
|
-
sig { override.params(definition: Model::Class).void }
|
8
|
+
# @override
|
9
|
+
#: (Model::Class definition) -> void
|
11
10
|
def on_define_class(definition)
|
12
11
|
@index.ignore(definition) if used_as_namespace?(definition)
|
13
12
|
end
|
14
13
|
|
15
|
-
|
14
|
+
# @override
|
15
|
+
#: (Model::Module definition) -> void
|
16
16
|
def on_define_module(definition)
|
17
17
|
@index.ignore(definition) if used_as_namespace?(definition)
|
18
18
|
end
|
19
19
|
|
20
20
|
private
|
21
21
|
|
22
|
-
|
22
|
+
#: (Model::Namespace symbol_def) -> bool
|
23
23
|
def used_as_namespace?(symbol_def)
|
24
24
|
symbol_def.children.any?
|
25
25
|
end
|
@@ -5,23 +5,23 @@ module Spoom
|
|
5
5
|
module Deadcode
|
6
6
|
module Plugins
|
7
7
|
class Rails < Base
|
8
|
-
extend T::Sig
|
9
|
-
|
10
8
|
ignore_constants_named("APP_PATH", "ENGINE_PATH", "ENGINE_ROOT")
|
11
9
|
|
12
|
-
|
10
|
+
# @override
|
11
|
+
#: (Model::Class definition) -> void
|
13
12
|
def on_define_class(definition)
|
14
13
|
@index.ignore(definition) if file_is_helper?(definition)
|
15
14
|
end
|
16
15
|
|
17
|
-
|
16
|
+
# @override
|
17
|
+
#: (Model::Module definition) -> void
|
18
18
|
def on_define_module(definition)
|
19
19
|
@index.ignore(definition) if file_is_helper?(definition)
|
20
20
|
end
|
21
21
|
|
22
22
|
private
|
23
23
|
|
24
|
-
|
24
|
+
#: (Model::Namespace symbol_def) -> bool
|
25
25
|
def file_is_helper?(symbol_def)
|
26
26
|
symbol_def.location.file.match?(%r{app/helpers/.*\.rb$})
|
27
27
|
end
|
@@ -5,8 +5,6 @@ module Spoom
|
|
5
5
|
module Deadcode
|
6
6
|
module Plugins
|
7
7
|
class Rubocop < Base
|
8
|
-
extend T::Sig
|
9
|
-
|
10
8
|
RUBOCOP_CONSTANTS = T.let(["MSG", "RESTRICT_ON_SEND"].to_set.freeze, T::Set[String])
|
11
9
|
|
12
10
|
ignore_classes_inheriting_from(
|
@@ -14,7 +12,8 @@ module Spoom
|
|
14
12
|
"RuboCop::Cop::Base",
|
15
13
|
)
|
16
14
|
|
17
|
-
|
15
|
+
# @override
|
16
|
+
#: (Model::Constant definition) -> void
|
18
17
|
def on_define_constant(definition)
|
19
18
|
owner = definition.owner
|
20
19
|
return false unless owner.is_a?(Model::Class)
|
@@ -22,7 +21,8 @@ module Spoom
|
|
22
21
|
@index.ignore(definition) if ignored_subclass?(owner) && RUBOCOP_CONSTANTS.include?(definition.name)
|
23
22
|
end
|
24
23
|
|
25
|
-
|
24
|
+
# @override
|
25
|
+
#: (Model::Method definition) -> void
|
26
26
|
def on_define_method(definition)
|
27
27
|
return unless definition.name == "on_send"
|
28
28
|
|
@@ -5,8 +5,6 @@ module Spoom
|
|
5
5
|
module Deadcode
|
6
6
|
module Plugins
|
7
7
|
class Ruby < Base
|
8
|
-
extend T::Sig
|
9
|
-
|
10
8
|
ignore_methods_named(
|
11
9
|
"==",
|
12
10
|
"extended",
|
@@ -20,7 +18,8 @@ module Spoom
|
|
20
18
|
"to_s",
|
21
19
|
)
|
22
20
|
|
23
|
-
|
21
|
+
# @override
|
22
|
+
#: (Send send) -> void
|
24
23
|
def on_send(send)
|
25
24
|
case send.name
|
26
25
|
when "const_defined?", "const_get", "const_source_location"
|
@@ -42,7 +41,7 @@ module Spoom
|
|
42
41
|
|
43
42
|
private
|
44
43
|
|
45
|
-
|
44
|
+
#: (Send send, Prism::Node node) -> void
|
46
45
|
def reference_symbol_as_constant(send, node)
|
47
46
|
case node
|
48
47
|
when Prism::SymbolNode
|
@@ -5,26 +5,32 @@ module Spoom
|
|
5
5
|
module Deadcode
|
6
6
|
module Plugins
|
7
7
|
class Sorbet < Base
|
8
|
-
|
9
|
-
|
10
|
-
sig { override.params(definition: Model::Constant).void }
|
8
|
+
# @override
|
9
|
+
#: (Model::Constant definition) -> void
|
11
10
|
def on_define_constant(definition)
|
12
11
|
@index.ignore(definition) if sorbet_type_member?(definition) || sorbet_enum_constant?(definition)
|
13
12
|
end
|
14
13
|
|
15
|
-
|
14
|
+
# @override
|
15
|
+
#: (Model::Method definition) -> void
|
16
16
|
def on_define_method(definition)
|
17
|
+
# Ignore signatures containing `override` or `overridable`, like `sig { override.void }`
|
17
18
|
@index.ignore(definition) if definition.sigs.any? { |sig| sig.string =~ /(override|overridable)/ }
|
19
|
+
|
20
|
+
# Ignore comments `@override` and `@overridable`
|
21
|
+
@index.ignore(definition) if definition.comments.any? do |comment|
|
22
|
+
comment.string == "@override" || comment.string == "@overridable"
|
23
|
+
end
|
18
24
|
end
|
19
25
|
|
20
26
|
private
|
21
27
|
|
22
|
-
|
28
|
+
#: (Model::Constant definition) -> bool
|
23
29
|
def sorbet_type_member?(definition)
|
24
30
|
definition.value.match?(/^(type_member|type_template)/)
|
25
31
|
end
|
26
32
|
|
27
|
-
|
33
|
+
#: (Model::Constant definition) -> bool
|
28
34
|
def sorbet_enum_constant?(definition)
|
29
35
|
owner = definition.owner
|
30
36
|
return false unless owner.is_a?(Model::Class)
|
@@ -5,11 +5,10 @@ module Spoom
|
|
5
5
|
module Deadcode
|
6
6
|
module Plugins
|
7
7
|
class Thor < Base
|
8
|
-
extend T::Sig
|
9
|
-
|
10
8
|
ignore_methods_named("exit_on_failure?")
|
11
9
|
|
12
|
-
|
10
|
+
# @override
|
11
|
+
#: (Model::Method definition) -> void
|
13
12
|
def on_define_method(definition)
|
14
13
|
owner = definition.owner
|
15
14
|
return unless owner.is_a?(Model::Class)
|
@@ -55,9 +55,7 @@ module Spoom
|
|
55
55
|
)
|
56
56
|
|
57
57
|
class << self
|
58
|
-
|
59
|
-
|
60
|
-
sig { params(context: Context).returns(T::Set[T.class_of(Plugins::Base)]) }
|
58
|
+
#: (Context context) -> Set[singleton(Plugins::Base)]
|
61
59
|
def plugins_from_gemfile_lock(context)
|
62
60
|
# These plugins are always loaded
|
63
61
|
plugin_classes = DEFAULT_PLUGINS.dup
|
@@ -71,7 +69,7 @@ module Spoom
|
|
71
69
|
plugin_classes
|
72
70
|
end
|
73
71
|
|
74
|
-
|
72
|
+
#: (Context context) -> Array[singleton(Plugins::Base)]
|
75
73
|
def load_custom_plugins(context)
|
76
74
|
context.glob("#{DEFAULT_CUSTOM_PLUGINS_PATH}/*.rb").each do |path|
|
77
75
|
require("#{context.absolute_path}/#{path}")
|
@@ -4,16 +4,14 @@
|
|
4
4
|
module Spoom
|
5
5
|
module Deadcode
|
6
6
|
class Remover
|
7
|
-
extend T::Sig
|
8
|
-
|
9
7
|
class Error < Spoom::Error; end
|
10
8
|
|
11
|
-
|
9
|
+
#: (Context context) -> void
|
12
10
|
def initialize(context)
|
13
11
|
@context = context
|
14
12
|
end
|
15
13
|
|
16
|
-
|
14
|
+
#: (Definition::Kind? kind, Location location) -> String
|
17
15
|
def remove_location(kind, location)
|
18
16
|
file = location.file
|
19
17
|
|
@@ -27,12 +25,10 @@ module Spoom
|
|
27
25
|
end
|
28
26
|
|
29
27
|
class NodeRemover
|
30
|
-
|
31
|
-
|
32
|
-
sig { returns(String) }
|
28
|
+
#: String
|
33
29
|
attr_reader :new_source
|
34
30
|
|
35
|
-
|
31
|
+
#: (String source, Definition::Kind? kind, Location location) -> void
|
36
32
|
def initialize(source, kind, location)
|
37
33
|
@old_source = source
|
38
34
|
@new_source = T.let(source.dup, String)
|
@@ -42,7 +38,7 @@ module Spoom
|
|
42
38
|
@node_context = T.let(NodeFinder.find(source, location, kind), NodeContext)
|
43
39
|
end
|
44
40
|
|
45
|
-
|
41
|
+
#: -> void
|
46
42
|
def apply_edit
|
47
43
|
sclass_context = @node_context.sclass_context
|
48
44
|
if sclass_context
|
@@ -69,7 +65,7 @@ module Spoom
|
|
69
65
|
|
70
66
|
private
|
71
67
|
|
72
|
-
|
68
|
+
#: (NodeContext context) -> void
|
73
69
|
def delete_constant_assignment(context)
|
74
70
|
case context.node
|
75
71
|
when Prism::ConstantWriteNode, Prism::ConstantOperatorWriteNode,
|
@@ -149,7 +145,7 @@ module Spoom
|
|
149
145
|
end
|
150
146
|
end
|
151
147
|
|
152
|
-
|
148
|
+
#: (NodeContext context) -> void
|
153
149
|
def delete_attr_accessor(context)
|
154
150
|
args_context = context.parent_context
|
155
151
|
send_context = args_context.parent_context
|
@@ -208,13 +204,7 @@ module Spoom
|
|
208
204
|
insert_accessor(context.node, send_context, was_removed: false) if need_accessor
|
209
205
|
end
|
210
206
|
|
211
|
-
|
212
|
-
params(
|
213
|
-
node: Prism::Node,
|
214
|
-
send_context: NodeContext,
|
215
|
-
was_removed: T::Boolean,
|
216
|
-
).void
|
217
|
-
end
|
207
|
+
#: (Prism::Node node, NodeContext send_context, was_removed: bool) -> void
|
218
208
|
def insert_accessor(node, send_context, was_removed:)
|
219
209
|
name = node.slice
|
220
210
|
code = case @kind
|
@@ -257,7 +247,7 @@ module Spoom
|
|
257
247
|
@new_source = lines.join
|
258
248
|
end
|
259
249
|
|
260
|
-
|
250
|
+
#: (NodeContext context) -> void
|
261
251
|
def delete_node_and_comments_and_sigs(context)
|
262
252
|
start_line = context.node.location.start_line
|
263
253
|
end_line = context.node.location.end_line
|
@@ -320,24 +310,24 @@ module Spoom
|
|
320
310
|
delete_lines(start_line, end_line)
|
321
311
|
end
|
322
312
|
|
323
|
-
|
313
|
+
#: (Integer start_line, Integer end_line) -> void
|
324
314
|
def delete_lines(start_line, end_line)
|
325
315
|
lines = @new_source.lines
|
326
316
|
lines[start_line - 1...end_line] = []
|
327
317
|
@new_source = lines.join
|
328
318
|
end
|
329
319
|
|
330
|
-
|
320
|
+
#: (Integer start_char, Integer end_char) -> void
|
331
321
|
def delete_chars(start_char, end_char)
|
332
322
|
@new_source[start_char...end_char] = ""
|
333
323
|
end
|
334
324
|
|
335
|
-
|
325
|
+
#: (Integer start_char, Integer end_char, String replacement) -> void
|
336
326
|
def replace_chars(start_char, end_char, replacement)
|
337
327
|
@new_source[start_char...end_char] = replacement
|
338
328
|
end
|
339
329
|
|
340
|
-
|
330
|
+
#: (Prism::CallNode node, name: String, kind: Definition::Kind?) -> String
|
341
331
|
def transform_sig(node, name:, kind:)
|
342
332
|
type = T.let(nil, T.nilable(String))
|
343
333
|
|
@@ -370,25 +360,16 @@ module Spoom
|
|
370
360
|
end
|
371
361
|
|
372
362
|
class NodeContext
|
373
|
-
|
374
|
-
|
375
|
-
sig { returns(T::Hash[Integer, Prism::Comment]) }
|
363
|
+
#: Hash[Integer, Prism::Comment]
|
376
364
|
attr_reader :comments
|
377
365
|
|
378
|
-
|
366
|
+
#: Prism::Node
|
379
367
|
attr_reader :node
|
380
368
|
|
381
|
-
|
369
|
+
#: Array[Prism::Node]
|
382
370
|
attr_accessor :nesting
|
383
371
|
|
384
|
-
|
385
|
-
params(
|
386
|
-
source: String,
|
387
|
-
comments: T::Hash[Integer, Prism::Comment],
|
388
|
-
node: Prism::Node,
|
389
|
-
nesting: T::Array[Prism::Node],
|
390
|
-
).void
|
391
|
-
end
|
372
|
+
#: (String source, Hash[Integer, Prism::Comment] comments, Prism::Node node, Array[Prism::Node] nesting) -> void
|
392
373
|
def initialize(source, comments, node, nesting)
|
393
374
|
@source = source
|
394
375
|
@comments = comments
|
@@ -396,7 +377,7 @@ module Spoom
|
|
396
377
|
@nesting = nesting
|
397
378
|
end
|
398
379
|
|
399
|
-
|
380
|
+
#: -> Prism::Node
|
400
381
|
def parent_node
|
401
382
|
parent = @nesting.last
|
402
383
|
raise Error, "No parent for node #{node}" unless parent
|
@@ -404,7 +385,7 @@ module Spoom
|
|
404
385
|
parent
|
405
386
|
end
|
406
387
|
|
407
|
-
|
388
|
+
#: -> NodeContext
|
408
389
|
def parent_context
|
409
390
|
nesting = @nesting.dup
|
410
391
|
parent = nesting.pop
|
@@ -413,7 +394,7 @@ module Spoom
|
|
413
394
|
NodeContext.new(@source, @comments, parent, nesting)
|
414
395
|
end
|
415
396
|
|
416
|
-
|
397
|
+
#: -> Array[Prism::Node]
|
417
398
|
def previous_nodes
|
418
399
|
parent = parent_node
|
419
400
|
child_nodes = parent.child_nodes.compact
|
@@ -424,12 +405,12 @@ module Spoom
|
|
424
405
|
T.must(child_nodes[0...index])
|
425
406
|
end
|
426
407
|
|
427
|
-
|
408
|
+
#: -> Prism::Node?
|
428
409
|
def previous_node
|
429
410
|
previous_nodes.last
|
430
411
|
end
|
431
412
|
|
432
|
-
|
413
|
+
#: -> Array[Prism::Node]
|
433
414
|
def next_nodes
|
434
415
|
parent = parent_node
|
435
416
|
child_nodes = parent.child_nodes.compact
|
@@ -440,12 +421,12 @@ module Spoom
|
|
440
421
|
T.must(child_nodes.compact[(index + 1)..-1])
|
441
422
|
end
|
442
423
|
|
443
|
-
|
424
|
+
#: -> Prism::Node?
|
444
425
|
def next_node
|
445
426
|
next_nodes.first
|
446
427
|
end
|
447
428
|
|
448
|
-
|
429
|
+
#: -> NodeContext?
|
449
430
|
def sclass_context
|
450
431
|
sclass = T.let(nil, T.nilable(Prism::SingletonClassNode))
|
451
432
|
|
@@ -473,12 +454,12 @@ module Spoom
|
|
473
454
|
nil
|
474
455
|
end
|
475
456
|
|
476
|
-
|
457
|
+
#: (Prism::Node? node) -> bool
|
477
458
|
def sorbet_signature?(node)
|
478
459
|
node.is_a?(Prism::CallNode) && node.name == :sig
|
479
460
|
end
|
480
461
|
|
481
|
-
|
462
|
+
#: (Prism::Node? node) -> bool
|
482
463
|
def sorbet_extend_sig?(node)
|
483
464
|
return false unless node.is_a?(Prism::CallNode)
|
484
465
|
return false unless node.name == :extend
|
@@ -490,7 +471,7 @@ module Spoom
|
|
490
471
|
args.arguments.first&.slice == "T::Sig"
|
491
472
|
end
|
492
473
|
|
493
|
-
|
474
|
+
#: (Integer start_line, Integer end_line) -> Array[Prism::Comment]
|
494
475
|
def comments_between_lines(start_line, end_line)
|
495
476
|
comments = T.let([], T::Array[Prism::Comment])
|
496
477
|
|
@@ -502,7 +483,7 @@ module Spoom
|
|
502
483
|
comments
|
503
484
|
end
|
504
485
|
|
505
|
-
|
486
|
+
#: (Prism::Node node) -> Array[Prism::Comment]
|
506
487
|
def attached_comments(node)
|
507
488
|
comments = T.let([], T::Array[Prism::Comment])
|
508
489
|
|
@@ -517,7 +498,7 @@ module Spoom
|
|
517
498
|
comments.reverse
|
518
499
|
end
|
519
500
|
|
520
|
-
|
501
|
+
#: -> Array[Prism::Node]
|
521
502
|
def attached_sigs
|
522
503
|
nodes = T.let([], T::Array[Prism::Node])
|
523
504
|
|
@@ -530,7 +511,7 @@ module Spoom
|
|
530
511
|
nodes.reverse
|
531
512
|
end
|
532
513
|
|
533
|
-
|
514
|
+
#: -> Prism::CallNode?
|
534
515
|
def attached_sig
|
535
516
|
previous_nodes.reverse_each do |node|
|
536
517
|
if node.is_a?(Prism::Comment)
|
@@ -547,12 +528,8 @@ module Spoom
|
|
547
528
|
end
|
548
529
|
|
549
530
|
class NodeFinder < Visitor
|
550
|
-
extend T::Sig
|
551
|
-
|
552
531
|
class << self
|
553
|
-
|
554
|
-
|
555
|
-
sig { params(source: String, location: Location, kind: T.nilable(Definition::Kind)).returns(NodeContext) }
|
532
|
+
#: (String source, Location location, Definition::Kind? kind) -> NodeContext
|
556
533
|
def find(source, location, kind)
|
557
534
|
result = Prism.parse(source)
|
558
535
|
|
@@ -586,7 +563,7 @@ module Spoom
|
|
586
563
|
NodeContext.new(source, comments_by_line, node, visitor.nodes_nesting)
|
587
564
|
end
|
588
565
|
|
589
|
-
|
566
|
+
#: (Prism::Node node, Definition::Kind kind) -> bool
|
590
567
|
def node_match_kind?(node, kind)
|
591
568
|
case kind
|
592
569
|
when Definition::Kind::AttrReader, Definition::Kind::AttrWriter
|
@@ -611,13 +588,13 @@ module Spoom
|
|
611
588
|
end
|
612
589
|
end
|
613
590
|
|
614
|
-
|
591
|
+
#: Prism::Node?
|
615
592
|
attr_reader :node
|
616
593
|
|
617
|
-
|
594
|
+
#: Array[Prism::Node]
|
618
595
|
attr_reader :nodes_nesting
|
619
596
|
|
620
|
-
|
597
|
+
#: (Location location, Definition::Kind? kind) -> void
|
621
598
|
def initialize(location, kind)
|
622
599
|
super()
|
623
600
|
@location = location
|
@@ -626,7 +603,8 @@ module Spoom
|
|
626
603
|
@nodes_nesting = T.let([], T::Array[Prism::Node])
|
627
604
|
end
|
628
605
|
|
629
|
-
|
606
|
+
# @override
|
607
|
+
#: (Prism::Node? node) -> void
|
630
608
|
def visit(node)
|
631
609
|
return unless node
|
632
610
|
|
data/lib/spoom/deadcode/send.rb
CHANGED
@@ -5,8 +5,6 @@ module Spoom
|
|
5
5
|
module Deadcode
|
6
6
|
# An abstraction to simplify handling of Prism::CallNode nodes.
|
7
7
|
class Send < T::Struct
|
8
|
-
extend T::Sig
|
9
|
-
|
10
8
|
const :node, Prism::CallNode
|
11
9
|
const :name, String
|
12
10
|
const :recv, T.nilable(Prism::Node), default: nil
|
@@ -14,18 +12,14 @@ module Spoom
|
|
14
12
|
const :block, T.nilable(Prism::Node), default: nil
|
15
13
|
const :location, Location
|
16
14
|
|
17
|
-
|
18
|
-
type_parameters(:T)
|
19
|
-
.params(arg_type: T::Class[T.type_parameter(:T)], block: T.proc.params(arg: T.type_parameter(:T)).void)
|
20
|
-
.void
|
21
|
-
end
|
15
|
+
#: [T] (Class[T] arg_type) { (T arg) -> void } -> void
|
22
16
|
def each_arg(arg_type, &block)
|
23
17
|
args.each do |arg|
|
24
18
|
yield(T.unsafe(arg)) if arg.is_a?(arg_type)
|
25
19
|
end
|
26
20
|
end
|
27
21
|
|
28
|
-
|
22
|
+
#: { (Prism::Node key, Prism::Node? value) -> void } -> void
|
29
23
|
def each_arg_assoc(&block)
|
30
24
|
args.each do |arg|
|
31
25
|
next unless arg.is_a?(Prism::KeywordHashNode) || arg.is_a?(Prism::HashNode)
|
data/lib/spoom/file_collector.rb
CHANGED
@@ -3,9 +3,7 @@
|
|
3
3
|
|
4
4
|
module Spoom
|
5
5
|
class FileCollector
|
6
|
-
|
7
|
-
|
8
|
-
sig { returns(T::Array[String]) }
|
6
|
+
#: Array[String]
|
9
7
|
attr_reader :files
|
10
8
|
|
11
9
|
# Initialize a new file collector
|
@@ -16,13 +14,7 @@ module Spoom
|
|
16
14
|
# If `allow_mime_types` is empty, all files are collected.
|
17
15
|
# If `allow_mime_types` is an array of mimetypes, files without an extension are collected if their mimetype is in
|
18
16
|
# the list.
|
19
|
-
|
20
|
-
params(
|
21
|
-
allow_extensions: T::Array[String],
|
22
|
-
allow_mime_types: T::Array[String],
|
23
|
-
exclude_patterns: T::Array[String],
|
24
|
-
).void
|
25
|
-
end
|
17
|
+
#: (?allow_extensions: Array[String], ?allow_mime_types: Array[String], ?exclude_patterns: Array[String]) -> void
|
26
18
|
def initialize(allow_extensions: [], allow_mime_types: [], exclude_patterns: [])
|
27
19
|
@files = T.let([], T::Array[String])
|
28
20
|
@allow_extensions = allow_extensions
|
@@ -30,12 +22,12 @@ module Spoom
|
|
30
22
|
@exclude_patterns = exclude_patterns
|
31
23
|
end
|
32
24
|
|
33
|
-
|
25
|
+
#: (Array[String] paths) -> void
|
34
26
|
def visit_paths(paths)
|
35
27
|
paths.each { |path| visit_path(path) }
|
36
28
|
end
|
37
29
|
|
38
|
-
|
30
|
+
#: (String path) -> void
|
39
31
|
def visit_path(path)
|
40
32
|
path = clean_path(path)
|
41
33
|
|
@@ -52,24 +44,24 @@ module Spoom
|
|
52
44
|
|
53
45
|
private
|
54
46
|
|
55
|
-
|
47
|
+
#: (String path) -> String
|
56
48
|
def clean_path(path)
|
57
49
|
Pathname.new(path).cleanpath.to_s
|
58
50
|
end
|
59
51
|
|
60
|
-
|
52
|
+
#: (String path) -> void
|
61
53
|
def visit_file(path)
|
62
54
|
return if excluded_file?(path)
|
63
55
|
|
64
56
|
@files << path
|
65
57
|
end
|
66
58
|
|
67
|
-
|
59
|
+
#: (String path) -> void
|
68
60
|
def visit_directory(path)
|
69
61
|
visit_paths(Dir.glob("#{path}/*"))
|
70
62
|
end
|
71
63
|
|
72
|
-
|
64
|
+
#: (String path) -> bool
|
73
65
|
def excluded_file?(path)
|
74
66
|
return false if @allow_extensions.empty?
|
75
67
|
|
@@ -84,7 +76,7 @@ module Spoom
|
|
84
76
|
end
|
85
77
|
end
|
86
78
|
|
87
|
-
|
79
|
+
#: (String path) -> bool
|
88
80
|
def excluded_path?(path)
|
89
81
|
@exclude_patterns.any? do |pattern|
|
90
82
|
# Use `FNM_PATHNAME` so patterns do not match directory separators
|
@@ -93,7 +85,7 @@ module Spoom
|
|
93
85
|
end
|
94
86
|
end
|
95
87
|
|
96
|
-
|
88
|
+
#: (String path) -> String?
|
97
89
|
def mime_type_for(path)
|
98
90
|
# The `file` command appears to be hanging on MacOS for some files so we timeout after 1s.
|
99
91
|
%x{timeout 1s file --mime-type -b '#{path}'}.split("; ").first&.strip
|