spoom 1.2.3 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +64 -55
- data/lib/spoom/backtrace_filter/minitest.rb +21 -0
- data/lib/spoom/cli/deadcode.rb +172 -0
- data/lib/spoom/cli/helper.rb +20 -0
- data/lib/spoom/cli/srb/bump.rb +200 -0
- data/lib/spoom/cli/srb/coverage.rb +224 -0
- data/lib/spoom/cli/srb/lsp.rb +159 -0
- data/lib/spoom/cli/srb/tc.rb +150 -0
- data/lib/spoom/cli/srb.rb +27 -0
- data/lib/spoom/cli.rb +72 -32
- data/lib/spoom/context/git.rb +2 -2
- data/lib/spoom/context/sorbet.rb +2 -2
- data/lib/spoom/deadcode/definition.rb +11 -0
- data/lib/spoom/deadcode/erb.rb +4 -4
- data/lib/spoom/deadcode/indexer.rb +266 -200
- data/lib/spoom/deadcode/location.rb +30 -2
- data/lib/spoom/deadcode/plugins/action_mailer.rb +21 -0
- data/lib/spoom/deadcode/plugins/action_mailer_preview.rb +19 -0
- data/lib/spoom/deadcode/plugins/actionpack.rb +59 -0
- data/lib/spoom/deadcode/plugins/active_job.rb +13 -0
- data/lib/spoom/deadcode/plugins/active_model.rb +46 -0
- data/lib/spoom/deadcode/plugins/active_record.rb +108 -0
- data/lib/spoom/deadcode/plugins/active_support.rb +32 -0
- data/lib/spoom/deadcode/plugins/base.rb +165 -12
- data/lib/spoom/deadcode/plugins/graphql.rb +47 -0
- data/lib/spoom/deadcode/plugins/minitest.rb +28 -0
- data/lib/spoom/deadcode/plugins/namespaces.rb +32 -0
- data/lib/spoom/deadcode/plugins/rails.rb +31 -0
- data/lib/spoom/deadcode/plugins/rake.rb +12 -0
- data/lib/spoom/deadcode/plugins/rspec.rb +19 -0
- data/lib/spoom/deadcode/plugins/rubocop.rb +41 -0
- data/lib/spoom/deadcode/plugins/ruby.rb +10 -18
- data/lib/spoom/deadcode/plugins/sorbet.rb +40 -0
- data/lib/spoom/deadcode/plugins/thor.rb +21 -0
- data/lib/spoom/deadcode/plugins.rb +91 -0
- data/lib/spoom/deadcode/remover.rb +651 -0
- data/lib/spoom/deadcode/send.rb +27 -6
- data/lib/spoom/deadcode/visitor.rb +755 -0
- data/lib/spoom/deadcode.rb +41 -10
- data/lib/spoom/file_tree.rb +0 -16
- data/lib/spoom/sorbet/errors.rb +1 -1
- data/lib/spoom/sorbet/lsp/structures.rb +2 -2
- data/lib/spoom/version.rb +1 -1
- metadata +36 -15
- data/lib/spoom/cli/bump.rb +0 -198
- data/lib/spoom/cli/coverage.rb +0 -222
- data/lib/spoom/cli/lsp.rb +0 -168
- data/lib/spoom/cli/run.rb +0 -148
@@ -13,8 +13,25 @@ module Spoom
|
|
13
13
|
class << self
|
14
14
|
extend T::Sig
|
15
15
|
|
16
|
-
sig { params(
|
17
|
-
def
|
16
|
+
sig { params(location_string: String).returns(Location) }
|
17
|
+
def from_string(location_string)
|
18
|
+
file, rest = location_string.split(":", 2)
|
19
|
+
raise LocationError, "Invalid location string: #{location_string}" unless file && rest
|
20
|
+
|
21
|
+
start_line, rest = rest.split(":", 2)
|
22
|
+
raise LocationError, "Invalid location string: #{location_string}" unless start_line && rest
|
23
|
+
|
24
|
+
start_column, rest = rest.split("-", 2)
|
25
|
+
raise LocationError, "Invalid location string: #{location_string}" unless start_column && rest
|
26
|
+
|
27
|
+
end_line, end_column = rest.split(":", 2)
|
28
|
+
raise LocationError, "Invalid location string: #{location_string}" unless end_line && end_column
|
29
|
+
|
30
|
+
new(file, start_line.to_i, start_column.to_i, end_line.to_i, end_column.to_i)
|
31
|
+
end
|
32
|
+
|
33
|
+
sig { params(file: String, location: Prism::Location).returns(Location) }
|
34
|
+
def from_prism(file, location)
|
18
35
|
new(file, location.start_line, location.start_column, location.end_line, location.end_column)
|
19
36
|
end
|
20
37
|
end
|
@@ -42,6 +59,17 @@ module Spoom
|
|
42
59
|
@end_column = end_column
|
43
60
|
end
|
44
61
|
|
62
|
+
sig { params(other: Location).returns(T::Boolean) }
|
63
|
+
def include?(other)
|
64
|
+
return false unless @file == other.file
|
65
|
+
return false if @start_line > other.start_line
|
66
|
+
return false if @start_line == other.start_line && @start_column > other.start_column
|
67
|
+
return false if @end_line < other.end_line
|
68
|
+
return false if @end_line == other.end_line && @end_column < other.end_column
|
69
|
+
|
70
|
+
true
|
71
|
+
end
|
72
|
+
|
45
73
|
sig { override.params(other: BasicObject).returns(T.nilable(Integer)) }
|
46
74
|
def <=>(other)
|
47
75
|
return unless Location === other
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Spoom
|
5
|
+
module Deadcode
|
6
|
+
module Plugins
|
7
|
+
class ActionMailer < Base
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { override.params(indexer: Indexer, send: Send).void }
|
11
|
+
def on_send(indexer, send)
|
12
|
+
return unless send.recv.nil? && ActionPack::CALLBACKS.include?(send.name)
|
13
|
+
|
14
|
+
send.each_arg(Prism::SymbolNode) do |arg|
|
15
|
+
indexer.reference_method(arg.unescaped, send.node)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Spoom
|
5
|
+
module Deadcode
|
6
|
+
module Plugins
|
7
|
+
class ActionMailerPreview < Base
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
ignore_classes_inheriting_from("ActionMailer::Preview")
|
11
|
+
|
12
|
+
sig { override.params(indexer: Indexer, definition: Definition).void }
|
13
|
+
def on_define_method(indexer, definition)
|
14
|
+
definition.ignored! if indexer.nesting_class_superclass_name == "ActionMailer::Preview"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Spoom
|
5
|
+
module Deadcode
|
6
|
+
module Plugins
|
7
|
+
class ActionPack < Base
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
CALLBACKS = T.let(
|
11
|
+
[
|
12
|
+
"after_action",
|
13
|
+
"append_after_action",
|
14
|
+
"append_around_action",
|
15
|
+
"append_before_action",
|
16
|
+
"around_action",
|
17
|
+
"before_action",
|
18
|
+
"prepend_after_action",
|
19
|
+
"prepend_around_action",
|
20
|
+
"prepend_before_action",
|
21
|
+
"skip_after_action",
|
22
|
+
"skip_around_action",
|
23
|
+
"skip_before_action",
|
24
|
+
].freeze,
|
25
|
+
T::Array[String],
|
26
|
+
)
|
27
|
+
|
28
|
+
ignore_classes_named(/Controller$/)
|
29
|
+
|
30
|
+
sig { override.params(indexer: Indexer, definition: Definition).void }
|
31
|
+
def on_define_method(indexer, definition)
|
32
|
+
definition.ignored! if ignored_class_name?(indexer.nesting_class_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
sig { override.params(indexer: Indexer, send: Send).void }
|
36
|
+
def on_send(indexer, send)
|
37
|
+
return unless send.recv.nil? && CALLBACKS.include?(send.name)
|
38
|
+
|
39
|
+
arg = send.args.first
|
40
|
+
case arg
|
41
|
+
when Prism::SymbolNode
|
42
|
+
indexer.reference_method(arg.unescaped, send.node)
|
43
|
+
end
|
44
|
+
|
45
|
+
send.each_arg_assoc do |key, value|
|
46
|
+
key = key.slice.delete_suffix(":")
|
47
|
+
|
48
|
+
case key
|
49
|
+
when "if", "unless"
|
50
|
+
indexer.reference_method(value.slice.delete_prefix(":"), send.node) if value
|
51
|
+
else
|
52
|
+
indexer.reference_constant(camelize(key), send.node)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Spoom
|
5
|
+
module Deadcode
|
6
|
+
module Plugins
|
7
|
+
class ActiveJob < Base
|
8
|
+
ignore_classes_named("ApplicationJob")
|
9
|
+
ignore_methods_named("perform", "build_enumerator", "each_iteration")
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Spoom
|
5
|
+
module Deadcode
|
6
|
+
module Plugins
|
7
|
+
class ActiveModel < Base
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
ignore_classes_inheriting_from(/^(::)?ActiveModel::EachValidator$/)
|
11
|
+
ignore_methods_named("validate_each")
|
12
|
+
|
13
|
+
sig { override.params(indexer: Indexer, send: Send).void }
|
14
|
+
def on_send(indexer, send)
|
15
|
+
return if send.recv
|
16
|
+
|
17
|
+
case send.name
|
18
|
+
when "attribute", "attributes"
|
19
|
+
send.each_arg(Prism::SymbolNode) do |arg|
|
20
|
+
indexer.reference_method(arg.unescaped, send.node)
|
21
|
+
end
|
22
|
+
when "validate", "validates", "validates!", "validates_each"
|
23
|
+
send.each_arg(Prism::SymbolNode) do |arg|
|
24
|
+
indexer.reference_method(arg.unescaped, send.node)
|
25
|
+
end
|
26
|
+
send.each_arg_assoc do |key, value|
|
27
|
+
key = key.slice.delete_suffix(":")
|
28
|
+
|
29
|
+
case key
|
30
|
+
when "if", "unless"
|
31
|
+
indexer.reference_method(value.slice.delete_prefix(":"), send.node) if value
|
32
|
+
else
|
33
|
+
indexer.reference_constant(camelize(key), send.node)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
when "validates_with"
|
37
|
+
arg = send.args.first
|
38
|
+
if arg.is_a?(Prism::SymbolNode)
|
39
|
+
indexer.reference_constant(arg.unescaped, send.node)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Spoom
|
5
|
+
module Deadcode
|
6
|
+
module Plugins
|
7
|
+
class ActiveRecord < Base
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
ignore_classes_inheriting_from(/^(::)?ActiveRecord::Migration/)
|
11
|
+
|
12
|
+
ignore_methods_named(
|
13
|
+
"change",
|
14
|
+
"down",
|
15
|
+
"up",
|
16
|
+
"table_name_prefix",
|
17
|
+
"to_param",
|
18
|
+
)
|
19
|
+
|
20
|
+
CALLBACKS = T.let(
|
21
|
+
[
|
22
|
+
"after_commit",
|
23
|
+
"after_create_commit",
|
24
|
+
"after_create",
|
25
|
+
"after_destroy_commit",
|
26
|
+
"after_destroy",
|
27
|
+
"after_find",
|
28
|
+
"after_initialize",
|
29
|
+
"after_rollback",
|
30
|
+
"after_save_commit",
|
31
|
+
"after_save",
|
32
|
+
"after_touch",
|
33
|
+
"after_update_commit",
|
34
|
+
"after_update",
|
35
|
+
"after_validation",
|
36
|
+
"around_create",
|
37
|
+
"around_destroy",
|
38
|
+
"around_save",
|
39
|
+
"around_update",
|
40
|
+
"before_create",
|
41
|
+
"before_destroy",
|
42
|
+
"before_save",
|
43
|
+
"before_update",
|
44
|
+
"before_validation",
|
45
|
+
].freeze,
|
46
|
+
T::Array[String],
|
47
|
+
)
|
48
|
+
|
49
|
+
CRUD_METHODS = T.let(
|
50
|
+
[
|
51
|
+
"assign_attributes",
|
52
|
+
"create",
|
53
|
+
"create!",
|
54
|
+
"insert",
|
55
|
+
"insert!",
|
56
|
+
"new",
|
57
|
+
"update",
|
58
|
+
"update!",
|
59
|
+
"upsert",
|
60
|
+
].freeze,
|
61
|
+
T::Array[String],
|
62
|
+
)
|
63
|
+
|
64
|
+
ARRAY_METHODS = T.let(
|
65
|
+
[
|
66
|
+
"insert_all",
|
67
|
+
"insert_all!",
|
68
|
+
"upsert_all",
|
69
|
+
].freeze,
|
70
|
+
T::Array[String],
|
71
|
+
)
|
72
|
+
|
73
|
+
sig { override.params(indexer: Indexer, send: Send).void }
|
74
|
+
def on_send(indexer, send)
|
75
|
+
if send.recv.nil? && CALLBACKS.include?(send.name)
|
76
|
+
send.each_arg(Prism::SymbolNode) do |arg|
|
77
|
+
indexer.reference_method(arg.unescaped, send.node)
|
78
|
+
end
|
79
|
+
return
|
80
|
+
end
|
81
|
+
|
82
|
+
return unless send.recv
|
83
|
+
|
84
|
+
case send.name
|
85
|
+
when *CRUD_METHODS
|
86
|
+
send.each_arg_assoc do |key, _value|
|
87
|
+
key = key.slice.delete_suffix(":")
|
88
|
+
indexer.reference_method("#{key}=", send.node)
|
89
|
+
end
|
90
|
+
when *ARRAY_METHODS
|
91
|
+
send.each_arg(Prism::ArrayNode) do |arg|
|
92
|
+
arg.elements.each do |part|
|
93
|
+
next unless part.is_a?(Prism::HashNode)
|
94
|
+
|
95
|
+
part.elements.each do |assoc|
|
96
|
+
next unless assoc.is_a?(Prism::AssocNode)
|
97
|
+
|
98
|
+
key = assoc.key.slice.delete_suffix(":")
|
99
|
+
indexer.reference_method("#{key}=", send.node)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Spoom
|
5
|
+
module Deadcode
|
6
|
+
module Plugins
|
7
|
+
class ActiveSupport < Base
|
8
|
+
ignore_classes_inheriting_from(/^(::)?ActiveSupport::TestCase$/)
|
9
|
+
|
10
|
+
ignore_methods_named(
|
11
|
+
"after_all",
|
12
|
+
"after_setup",
|
13
|
+
"after_teardown",
|
14
|
+
"before_all",
|
15
|
+
"before_setup",
|
16
|
+
"before_teardown",
|
17
|
+
)
|
18
|
+
|
19
|
+
SETUP_AND_TEARDOWN_METHODS = T.let(["setup", "teardown"], T::Array[String])
|
20
|
+
|
21
|
+
sig { override.params(indexer: Indexer, send: Send).void }
|
22
|
+
def on_send(indexer, send)
|
23
|
+
return unless send.recv.nil? && SETUP_AND_TEARDOWN_METHODS.include?(send.name)
|
24
|
+
|
25
|
+
send.each_arg(Prism::SymbolNode) do |arg|
|
26
|
+
indexer.reference_method(T.must(arg.value), send.node)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -17,6 +17,60 @@ module Spoom
|
|
17
17
|
|
18
18
|
# Plugins DSL
|
19
19
|
|
20
|
+
# Mark classes matching `names` as ignored.
|
21
|
+
#
|
22
|
+
# Names can be either strings or regexps:
|
23
|
+
#
|
24
|
+
# ~~~rb
|
25
|
+
# class MyPlugin < Spoom::Deadcode::Plugins::Base
|
26
|
+
# ignore_class_names(
|
27
|
+
# "Foo",
|
28
|
+
# "Bar",
|
29
|
+
# /Baz.*/,
|
30
|
+
# )
|
31
|
+
# end
|
32
|
+
# ~~~
|
33
|
+
sig { params(names: T.any(String, Regexp)).void }
|
34
|
+
def ignore_classes_named(*names)
|
35
|
+
save_names_and_patterns(names, :@ignored_class_names, :@ignored_class_patterns)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Mark classes directly subclassing a class matching `names` as ignored.
|
39
|
+
#
|
40
|
+
# Names can be either strings or regexps:
|
41
|
+
#
|
42
|
+
# ~~~rb
|
43
|
+
# class MyPlugin < Spoom::Deadcode::Plugins::Base
|
44
|
+
# ignore_classes_inheriting_from(
|
45
|
+
# "Foo",
|
46
|
+
# "Bar",
|
47
|
+
# /Baz.*/,
|
48
|
+
# )
|
49
|
+
# end
|
50
|
+
# ~~~
|
51
|
+
sig { params(names: T.any(String, Regexp)).void }
|
52
|
+
def ignore_classes_inheriting_from(*names)
|
53
|
+
save_names_and_patterns(names, :@ignored_subclasses_of_names, :@ignored_subclasses_of_patterns)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Mark constants matching `names` as ignored.
|
57
|
+
#
|
58
|
+
# Names can be either strings or regexps:
|
59
|
+
#
|
60
|
+
# ~~~rb
|
61
|
+
# class MyPlugin < Spoom::Deadcode::Plugins::Base
|
62
|
+
# ignore_class_names(
|
63
|
+
# "FOO",
|
64
|
+
# "BAR",
|
65
|
+
# /BAZ.*/,
|
66
|
+
# )
|
67
|
+
# end
|
68
|
+
# ~~~
|
69
|
+
sig { params(names: T.any(String, Regexp)).void }
|
70
|
+
def ignore_constants_named(*names)
|
71
|
+
save_names_and_patterns(names, :@ignored_constant_names, :@ignored_constant_patterns)
|
72
|
+
end
|
73
|
+
|
20
74
|
# Mark methods matching `names` as ignored.
|
21
75
|
#
|
22
76
|
# Names can be either strings or regexps:
|
@@ -31,10 +85,28 @@ module Spoom
|
|
31
85
|
# end
|
32
86
|
# ~~~
|
33
87
|
sig { params(names: T.any(String, Regexp)).void }
|
34
|
-
def
|
88
|
+
def ignore_methods_named(*names)
|
35
89
|
save_names_and_patterns(names, :@ignored_method_names, :@ignored_method_patterns)
|
36
90
|
end
|
37
91
|
|
92
|
+
# Mark modules matching `names` as ignored.
|
93
|
+
#
|
94
|
+
# Names can be either strings or regexps:
|
95
|
+
#
|
96
|
+
# ~~~rb
|
97
|
+
# class MyPlugin < Spoom::Deadcode::Plugins::Base
|
98
|
+
# ignore_class_names(
|
99
|
+
# "Foo",
|
100
|
+
# "Bar",
|
101
|
+
# /Baz.*/,
|
102
|
+
# )
|
103
|
+
# end
|
104
|
+
# ~~~
|
105
|
+
sig { params(names: T.any(String, Regexp)).void }
|
106
|
+
def ignore_modules_named(*names)
|
107
|
+
save_names_and_patterns(names, :@ignored_module_names, :@ignored_module_patterns)
|
108
|
+
end
|
109
|
+
|
38
110
|
private
|
39
111
|
|
40
112
|
sig { params(names: T::Array[T.any(String, Regexp)], names_variable: Symbol, patterns_variable: Symbol).void }
|
@@ -45,7 +117,7 @@ module Spoom
|
|
45
117
|
names.each do |name|
|
46
118
|
case name
|
47
119
|
when String
|
48
|
-
ignored_names << name
|
120
|
+
ignored_names << name.delete_prefix("::")
|
49
121
|
when Regexp
|
50
122
|
ignored_patterns << name
|
51
123
|
end
|
@@ -73,6 +145,12 @@ module Spoom
|
|
73
145
|
# no-op
|
74
146
|
end
|
75
147
|
|
148
|
+
# Do not override this method, use `on_define_accessor` instead.
|
149
|
+
sig { params(indexer: Indexer, definition: Definition).void }
|
150
|
+
def internal_on_define_accessor(indexer, definition)
|
151
|
+
on_define_accessor(indexer, definition)
|
152
|
+
end
|
153
|
+
|
76
154
|
# Called when a class is defined.
|
77
155
|
#
|
78
156
|
# Will be called when the indexer processes a `class` node.
|
@@ -91,6 +169,18 @@ module Spoom
|
|
91
169
|
# no-op
|
92
170
|
end
|
93
171
|
|
172
|
+
# Do not override this method, use `on_define_class` instead.
|
173
|
+
sig { params(indexer: Indexer, definition: Definition).void }
|
174
|
+
def internal_on_define_class(indexer, definition)
|
175
|
+
if ignored_class_name?(definition.name)
|
176
|
+
definition.ignored!
|
177
|
+
elsif ignored_subclass?(indexer.nesting_class_superclass_name)
|
178
|
+
definition.ignored!
|
179
|
+
end
|
180
|
+
|
181
|
+
on_define_class(indexer, definition)
|
182
|
+
end
|
183
|
+
|
94
184
|
# Called when a constant is defined.
|
95
185
|
#
|
96
186
|
# Will be called when the indexer processes a `CONST =` node.
|
@@ -109,6 +199,14 @@ module Spoom
|
|
109
199
|
# no-op
|
110
200
|
end
|
111
201
|
|
202
|
+
# Do not override this method, use `on_define_constant` instead.
|
203
|
+
sig { params(indexer: Indexer, definition: Definition).void }
|
204
|
+
def internal_on_define_constant(indexer, definition)
|
205
|
+
definition.ignored! if ignored_constant_name?(definition.name)
|
206
|
+
|
207
|
+
on_define_constant(indexer, definition)
|
208
|
+
end
|
209
|
+
|
112
210
|
# Called when a method is defined.
|
113
211
|
#
|
114
212
|
# Will be called when the indexer processes a `def` or `defs` node.
|
@@ -126,7 +224,15 @@ module Spoom
|
|
126
224
|
# ~~~
|
127
225
|
sig { params(indexer: Indexer, definition: Definition).void }
|
128
226
|
def on_define_method(indexer, definition)
|
227
|
+
# no-op
|
228
|
+
end
|
229
|
+
|
230
|
+
# Do not override this method, use `on_define_method` instead.
|
231
|
+
sig { params(indexer: Indexer, definition: Definition).void }
|
232
|
+
def internal_on_define_method(indexer, definition)
|
129
233
|
definition.ignored! if ignored_method_name?(definition.name)
|
234
|
+
|
235
|
+
on_define_method(indexer, definition)
|
130
236
|
end
|
131
237
|
|
132
238
|
# Called when a module is defined.
|
@@ -147,6 +253,14 @@ module Spoom
|
|
147
253
|
# no-op
|
148
254
|
end
|
149
255
|
|
256
|
+
# Do not override this method, use `on_define_module` instead.
|
257
|
+
sig { params(indexer: Indexer, definition: Definition).void }
|
258
|
+
def internal_on_define_module(indexer, definition)
|
259
|
+
definition.ignored! if ignored_module_name?(definition.name)
|
260
|
+
|
261
|
+
on_define_module(indexer, definition)
|
262
|
+
end
|
263
|
+
|
150
264
|
# Called when a send is being processed
|
151
265
|
#
|
152
266
|
# ~~~rb
|
@@ -155,7 +269,7 @@ module Spoom
|
|
155
269
|
# return unless send.name == "dsl_method"
|
156
270
|
# return if send.args.empty?
|
157
271
|
#
|
158
|
-
# method_name =
|
272
|
+
# method_name = send.args.first.slice.delete_prefix(":")
|
159
273
|
# indexer.reference_method(method_name, send.node)
|
160
274
|
# end
|
161
275
|
# end
|
@@ -165,16 +279,43 @@ module Spoom
|
|
165
279
|
# no-op
|
166
280
|
end
|
167
281
|
|
282
|
+
# Do not override this method, use `on_send` instead.
|
283
|
+
sig { params(indexer: Indexer, send: Send).void }
|
284
|
+
def internal_on_send(indexer, send)
|
285
|
+
on_send(indexer, send)
|
286
|
+
end
|
287
|
+
|
168
288
|
private
|
169
289
|
|
290
|
+
# DSL support
|
291
|
+
|
292
|
+
sig { params(name: T.nilable(String)).returns(T::Boolean) }
|
293
|
+
def ignored_class_name?(name)
|
294
|
+
return false unless name
|
295
|
+
|
296
|
+
ignored_name?(name, :@ignored_class_names, :@ignored_class_patterns)
|
297
|
+
end
|
298
|
+
|
299
|
+
sig { params(superclass_name: T.nilable(String)).returns(T::Boolean) }
|
300
|
+
def ignored_subclass?(superclass_name)
|
301
|
+
return false unless superclass_name
|
302
|
+
|
303
|
+
ignored_name?(superclass_name, :@ignored_subclasses_of_names, :@ignored_subclasses_of_patterns)
|
304
|
+
end
|
305
|
+
|
306
|
+
sig { params(name: String).returns(T::Boolean) }
|
307
|
+
def ignored_constant_name?(name)
|
308
|
+
ignored_name?(name, :@ignored_constant_names, :@ignored_constant_patterns)
|
309
|
+
end
|
310
|
+
|
170
311
|
sig { params(name: String).returns(T::Boolean) }
|
171
312
|
def ignored_method_name?(name)
|
172
313
|
ignored_name?(name, :@ignored_method_names, :@ignored_method_patterns)
|
173
314
|
end
|
174
315
|
|
175
|
-
sig { params(
|
176
|
-
def
|
177
|
-
|
316
|
+
sig { params(name: String).returns(T::Boolean) }
|
317
|
+
def ignored_module_name?(name)
|
318
|
+
ignored_name?(name, :@ignored_module_names, :@ignored_module_patterns)
|
178
319
|
end
|
179
320
|
|
180
321
|
sig { params(name: String, names_variable: Symbol, patterns_variable: Symbol).returns(T::Boolean) }
|
@@ -182,18 +323,30 @@ module Spoom
|
|
182
323
|
names(names_variable).include?(name) || patterns(patterns_variable).any? { |pattern| pattern.match?(name) }
|
183
324
|
end
|
184
325
|
|
326
|
+
sig { params(const: Symbol).returns(T::Set[String]) }
|
327
|
+
def names(const)
|
328
|
+
self.class.instance_variable_get(const) || Set.new
|
329
|
+
end
|
330
|
+
|
185
331
|
sig { params(const: Symbol).returns(T::Array[Regexp]) }
|
186
332
|
def patterns(const)
|
187
333
|
self.class.instance_variable_get(const) || []
|
188
334
|
end
|
189
335
|
|
190
|
-
|
191
|
-
def reference_send_first_symbol_as_method(indexer, send)
|
192
|
-
first_arg = send.args.first
|
193
|
-
return unless first_arg.is_a?(SyntaxTree::SymbolLiteral)
|
336
|
+
# Plugin utils
|
194
337
|
|
195
|
-
|
196
|
-
|
338
|
+
sig { params(name: String).returns(String) }
|
339
|
+
def camelize(name)
|
340
|
+
name = T.must(name.split("::").last)
|
341
|
+
name = T.must(name.split("/").last)
|
342
|
+
name = name.gsub(/[^a-zA-Z0-9_]/, "")
|
343
|
+
name = name.sub(/^[a-z\d]*/, &:capitalize)
|
344
|
+
name = name.gsub(%r{(?:_|(/))([a-z\d]*)}) do
|
345
|
+
s1 = Regexp.last_match(1)
|
346
|
+
s2 = Regexp.last_match(2)
|
347
|
+
"#{s1}#{s2&.capitalize}"
|
348
|
+
end
|
349
|
+
name
|
197
350
|
end
|
198
351
|
end
|
199
352
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Spoom
|
5
|
+
module Deadcode
|
6
|
+
module Plugins
|
7
|
+
class GraphQL < Base
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
ignore_classes_inheriting_from(
|
11
|
+
/^(::)?GraphQL::Schema::Enum$/,
|
12
|
+
/^(::)?GraphQL::Schema::Object$/,
|
13
|
+
/^(::)?GraphQL::Schema::Scalar$/,
|
14
|
+
/^(::)?GraphQL::Schema::Union$/,
|
15
|
+
)
|
16
|
+
|
17
|
+
ignore_methods_named(
|
18
|
+
"coerce_input",
|
19
|
+
"coerce_result",
|
20
|
+
"graphql_name",
|
21
|
+
"resolve",
|
22
|
+
"resolve_type",
|
23
|
+
"subscribed",
|
24
|
+
"unsubscribed",
|
25
|
+
)
|
26
|
+
|
27
|
+
sig { override.params(indexer: Indexer, send: Send).void }
|
28
|
+
def on_send(indexer, send)
|
29
|
+
return unless send.recv.nil? && send.name == "field"
|
30
|
+
|
31
|
+
arg = send.args.first
|
32
|
+
return unless arg.is_a?(Prism::SymbolNode)
|
33
|
+
|
34
|
+
indexer.reference_method(arg.unescaped, send.node)
|
35
|
+
|
36
|
+
send.each_arg_assoc do |key, value|
|
37
|
+
key = key.slice.delete_suffix(":")
|
38
|
+
next unless key == "resolver_method"
|
39
|
+
next unless value
|
40
|
+
|
41
|
+
indexer.reference_method(value.slice.delete_prefix(":"), send.node)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Spoom
|
5
|
+
module Deadcode
|
6
|
+
module Plugins
|
7
|
+
class Minitest < Base
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
ignore_classes_named(/Test$/)
|
11
|
+
|
12
|
+
ignore_methods_named(
|
13
|
+
"after_all",
|
14
|
+
"around",
|
15
|
+
"around_all",
|
16
|
+
"before_all",
|
17
|
+
"setup",
|
18
|
+
"teardown",
|
19
|
+
)
|
20
|
+
|
21
|
+
sig { override.params(indexer: Indexer, definition: Definition).void }
|
22
|
+
def on_define_method(indexer, definition)
|
23
|
+
definition.ignored! if indexer.path.match?(%r{test/.*test\.rb$}) && definition.name.match?(/^test_/)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|