spoom 1.3.2 → 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 +7 -7
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e839292b23e8582e1e7b4317c6431de9b7d59e68b2c4dbb4015f75619a995601
|
4
|
+
data.tar.gz: 4853e7e2df4398e635e616df0f7354b8d37c67bef76ed12a0ec6efc16de1edc0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 940d6b9ec69c6eb767a1ccf2fc33767e552080010f94b14a77301f66b57a113f0619efb1e3ca357b04af782b3d9cbe07ff81f349adb814aef8c4d3fc8ea65de1
|
7
|
+
data.tar.gz: 7de4600f896698ea578887b0f417fd9a7f31d3689e6b69e514905b671197c62abf5f232c9471b364f0e4c3509c81848a7ca08d09c73c4cba4cba036963e34192
|
data/lib/spoom/cli/deadcode.rb
CHANGED
@@ -24,7 +24,7 @@ module Spoom
|
|
24
24
|
desc: "Allowed mime types"
|
25
25
|
option :exclude,
|
26
26
|
type: :array,
|
27
|
-
default: ["vendor/", "sorbet/"],
|
27
|
+
default: ["vendor/", "sorbet/", "tmp/", "log/", "node_modules/"],
|
28
28
|
aliases: :x,
|
29
29
|
desc: "Exclude paths"
|
30
30
|
option :show_files,
|
@@ -58,7 +58,9 @@ module Spoom
|
|
58
58
|
collector = FileCollector.new(
|
59
59
|
allow_extensions: options[:allowed_extensions],
|
60
60
|
allow_mime_types: options[:allowed_mime_types],
|
61
|
-
exclude_patterns:
|
61
|
+
exclude_patterns: paths.flat_map do |path|
|
62
|
+
options[:exclude].map { |excluded| Pathname.new(File.join(path, excluded, "**")).cleanpath.to_s }
|
63
|
+
end,
|
62
64
|
)
|
63
65
|
collector.visit_paths(paths)
|
64
66
|
files = collector.files.sort
|
@@ -71,32 +73,35 @@ module Spoom
|
|
71
73
|
$stderr.puts
|
72
74
|
end
|
73
75
|
|
74
|
-
|
76
|
+
plugin_classes = Spoom::Deadcode.plugins_from_gemfile_lock(context)
|
77
|
+
plugin_classes.merge(Spoom::Deadcode.load_custom_plugins(context))
|
75
78
|
if options[:show_plugins]
|
76
|
-
$stderr.puts "\nLoaded #{blue(
|
77
|
-
|
78
|
-
$stderr.puts " #{gray(plugin.
|
79
|
+
$stderr.puts "\nLoaded #{blue(plugin_classes.size.to_s)} plugins\n"
|
80
|
+
plugin_classes.each do |plugin|
|
81
|
+
$stderr.puts " #{gray(plugin.to_s)}"
|
79
82
|
end
|
80
83
|
$stderr.puts
|
81
84
|
end
|
82
85
|
|
83
|
-
|
86
|
+
model = Spoom::Model.new
|
87
|
+
index = Spoom::Deadcode::Index.new(model)
|
88
|
+
plugins = plugin_classes.map { |plugin| plugin.new(index) }
|
84
89
|
|
85
90
|
$stderr.puts "Indexing #{blue(files.size.to_s)} files..."
|
86
91
|
files.each do |file|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
tree = Spoom::Deadcode.parse_ruby(content, file: file)
|
91
|
-
Spoom::Deadcode.index_node(index, tree, content, file: file, plugins: plugins)
|
92
|
-
rescue Spoom::Deadcode::ParserError => e
|
92
|
+
index.index_file(file, plugins: plugins)
|
93
|
+
rescue ParseError => e
|
93
94
|
say_error("Error parsing #{file}: #{e.message}")
|
94
95
|
next
|
95
|
-
rescue Spoom::Deadcode::
|
96
|
+
rescue Spoom::Deadcode::Index::Error => e
|
96
97
|
say_error("Error indexing #{file}: #{e.message}")
|
97
98
|
next
|
98
99
|
end
|
99
100
|
|
101
|
+
model.finalize!
|
102
|
+
index.apply_plugins!(plugins)
|
103
|
+
index.finalize!
|
104
|
+
|
100
105
|
if options[:show_defs]
|
101
106
|
$stderr.puts "\nDefinitions:"
|
102
107
|
index.definitions.each do |name, definitions|
|
@@ -123,7 +128,6 @@ module Spoom
|
|
123
128
|
references_count = index.references.size.to_s
|
124
129
|
$stderr.puts "Analyzing #{blue(definitions_count)} definitions against #{blue(references_count)} references..."
|
125
130
|
|
126
|
-
index.finalize!
|
127
131
|
dead = index.definitions.values.flatten.select(&:dead?)
|
128
132
|
|
129
133
|
if options[:sort] == "name"
|
@@ -148,7 +152,7 @@ module Spoom
|
|
148
152
|
|
149
153
|
desc "remove LOCATION", "Remove dead code at LOCATION"
|
150
154
|
def remove(location_string)
|
151
|
-
location =
|
155
|
+
location = Location.from_string(location_string)
|
152
156
|
context = self.context
|
153
157
|
remover = Spoom::Deadcode::Remover.new(context)
|
154
158
|
|
@@ -163,7 +167,7 @@ module Spoom
|
|
163
167
|
rescue Spoom::Deadcode::Remover::Error => e
|
164
168
|
say_error("Can't remove code at #{location_string}: #{e.message}")
|
165
169
|
exit(1)
|
166
|
-
rescue
|
170
|
+
rescue Location::LocationError => e
|
167
171
|
say_error(e.message)
|
168
172
|
exit(1)
|
169
173
|
end
|
data/lib/spoom/deadcode/index.rb
CHANGED
@@ -6,28 +6,113 @@ module Spoom
|
|
6
6
|
class Index
|
7
7
|
extend T::Sig
|
8
8
|
|
9
|
+
class Error < Spoom::Error
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
sig { params(message: String, parent: Exception).void }
|
13
|
+
def initialize(message, parent:)
|
14
|
+
super(message)
|
15
|
+
set_backtrace(parent.backtrace)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
sig { returns(Model) }
|
20
|
+
attr_reader :model
|
21
|
+
|
9
22
|
sig { returns(T::Hash[String, T::Array[Definition]]) }
|
10
23
|
attr_reader :definitions
|
11
24
|
|
12
|
-
sig { returns(T::Hash[String, T::Array[Reference]]) }
|
25
|
+
sig { returns(T::Hash[String, T::Array[Model::Reference]]) }
|
13
26
|
attr_reader :references
|
14
27
|
|
15
|
-
sig { void }
|
16
|
-
def initialize
|
28
|
+
sig { params(model: Model).void }
|
29
|
+
def initialize(model)
|
30
|
+
@model = model
|
17
31
|
@definitions = T.let({}, T::Hash[String, T::Array[Definition]])
|
18
|
-
@references = T.let({}, T::Hash[String, T::Array[Reference]])
|
32
|
+
@references = T.let({}, T::Hash[String, T::Array[Model::Reference]])
|
33
|
+
@ignored = T.let(Set.new, T::Set[Model::SymbolDef])
|
19
34
|
end
|
20
35
|
|
21
36
|
# Indexing
|
22
37
|
|
38
|
+
sig { params(file: String, plugins: T::Array[Plugins::Base]).void }
|
39
|
+
def index_file(file, plugins: [])
|
40
|
+
if file.end_with?(".erb")
|
41
|
+
erb = File.read(file)
|
42
|
+
index_erb(erb, file: file, plugins: plugins)
|
43
|
+
else
|
44
|
+
rb = File.read(file)
|
45
|
+
index_ruby(rb, file: file, plugins: plugins)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
sig { params(erb: String, file: String, plugins: T::Array[Plugins::Base]).void }
|
50
|
+
def index_erb(erb, file:, plugins: [])
|
51
|
+
index_ruby(Deadcode::ERB.new(erb).src, file: file, plugins: plugins)
|
52
|
+
end
|
53
|
+
|
54
|
+
sig { params(rb: String, file: String, plugins: T::Array[Plugins::Base]).void }
|
55
|
+
def index_ruby(rb, file:, plugins: [])
|
56
|
+
node = Spoom.parse_ruby(rb, file: file)
|
57
|
+
|
58
|
+
# Index definitions
|
59
|
+
model_builder = Model::Builder.new(@model, file)
|
60
|
+
model_builder.visit(node)
|
61
|
+
|
62
|
+
# Index references
|
63
|
+
refs_visitor = Model::ReferencesVisitor.new(file)
|
64
|
+
refs_visitor.visit(node)
|
65
|
+
refs_visitor.references.each do |ref|
|
66
|
+
(@references[ref.name] ||= []) << ref
|
67
|
+
end
|
68
|
+
|
69
|
+
# Index references and sends
|
70
|
+
indexer = Indexer.new(file, self, plugins: plugins)
|
71
|
+
indexer.visit(node)
|
72
|
+
rescue ParseError => e
|
73
|
+
raise e
|
74
|
+
rescue => e
|
75
|
+
raise Error.new("Error while indexing #{file} (#{e.message})", parent: e)
|
76
|
+
end
|
77
|
+
|
23
78
|
sig { params(definition: Definition).void }
|
24
79
|
def define(definition)
|
25
80
|
(@definitions[definition.name] ||= []) << definition
|
26
81
|
end
|
27
82
|
|
28
|
-
sig { params(
|
29
|
-
def
|
30
|
-
(@references[
|
83
|
+
sig { params(name: String, location: Location).void }
|
84
|
+
def reference_constant(name, location)
|
85
|
+
(@references[name] ||= []) << Model::Reference.constant(name, location)
|
86
|
+
end
|
87
|
+
|
88
|
+
sig { params(name: String, location: Location).void }
|
89
|
+
def reference_method(name, location)
|
90
|
+
(@references[name] ||= []) << Model::Reference.method(name, location)
|
91
|
+
end
|
92
|
+
|
93
|
+
sig { params(symbol_def: Model::SymbolDef).void }
|
94
|
+
def ignore(symbol_def)
|
95
|
+
@ignored << symbol_def
|
96
|
+
end
|
97
|
+
|
98
|
+
sig { params(plugins: T::Array[Plugins::Base]).void }
|
99
|
+
def apply_plugins!(plugins)
|
100
|
+
@model.symbols.each do |_full_name, symbol|
|
101
|
+
symbol.definitions.each do |symbol_def|
|
102
|
+
case symbol_def
|
103
|
+
when Model::Class
|
104
|
+
plugins.each { |plugin| plugin.internal_on_define_class(symbol_def) }
|
105
|
+
when Model::Module
|
106
|
+
plugins.each { |plugin| plugin.internal_on_define_module(symbol_def) }
|
107
|
+
when Model::Constant
|
108
|
+
plugins.each { |plugin| plugin.internal_on_define_constant(symbol_def) }
|
109
|
+
when Model::Method
|
110
|
+
plugins.each { |plugin| plugin.internal_on_define_method(symbol_def) }
|
111
|
+
when Model::Attr
|
112
|
+
plugins.each { |plugin| plugin.internal_on_define_accessor(symbol_def) }
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
31
116
|
end
|
32
117
|
|
33
118
|
# Mark all definitions having a reference of the same name as `alive`
|
@@ -35,8 +120,91 @@ module Spoom
|
|
35
120
|
# To be called once all the files have been indexed and all the definitions and references discovered.
|
36
121
|
sig { void }
|
37
122
|
def finalize!
|
38
|
-
@
|
39
|
-
|
123
|
+
@model.symbols.each do |_full_name, symbol|
|
124
|
+
symbol.definitions.each do |symbol_def|
|
125
|
+
case symbol_def
|
126
|
+
when Model::Class
|
127
|
+
definition = Definition.new(
|
128
|
+
kind: Definition::Kind::Class,
|
129
|
+
name: symbol.name,
|
130
|
+
full_name: symbol.full_name,
|
131
|
+
location: symbol_def.location,
|
132
|
+
)
|
133
|
+
definition.ignored! if @ignored.include?(symbol_def)
|
134
|
+
definition.alive! if @references.key?(symbol.name)
|
135
|
+
define(definition)
|
136
|
+
when Model::Module
|
137
|
+
definition = Definition.new(
|
138
|
+
kind: Definition::Kind::Module,
|
139
|
+
name: symbol.name,
|
140
|
+
full_name: symbol.full_name,
|
141
|
+
location: symbol_def.location,
|
142
|
+
)
|
143
|
+
definition.ignored! if @ignored.include?(symbol_def)
|
144
|
+
definition.alive! if @references.key?(symbol.name)
|
145
|
+
define(definition)
|
146
|
+
when Model::Constant
|
147
|
+
definition = Definition.new(
|
148
|
+
kind: Definition::Kind::Constant,
|
149
|
+
name: symbol.name,
|
150
|
+
full_name: symbol.full_name,
|
151
|
+
location: symbol_def.location,
|
152
|
+
)
|
153
|
+
definition.ignored! if @ignored.include?(symbol_def)
|
154
|
+
definition.alive! if @references.key?(symbol.name)
|
155
|
+
define(definition)
|
156
|
+
when Model::Method
|
157
|
+
definition = Definition.new(
|
158
|
+
kind: Definition::Kind::Method,
|
159
|
+
name: symbol.name,
|
160
|
+
full_name: symbol.full_name,
|
161
|
+
location: symbol_def.location,
|
162
|
+
)
|
163
|
+
definition.ignored! if @ignored.include?(symbol_def)
|
164
|
+
definition.alive! if @references.key?(symbol.name)
|
165
|
+
define(definition)
|
166
|
+
when Model::AttrAccessor
|
167
|
+
definition = Definition.new(
|
168
|
+
kind: Definition::Kind::AttrReader,
|
169
|
+
name: symbol.name,
|
170
|
+
full_name: symbol.full_name,
|
171
|
+
location: symbol_def.location,
|
172
|
+
)
|
173
|
+
definition.ignored! if @ignored.include?(symbol_def)
|
174
|
+
definition.alive! if @references.key?(symbol.name)
|
175
|
+
define(definition)
|
176
|
+
|
177
|
+
definition = Definition.new(
|
178
|
+
kind: Definition::Kind::AttrWriter,
|
179
|
+
name: "#{symbol.name}=",
|
180
|
+
full_name: "#{symbol.full_name}=",
|
181
|
+
location: symbol_def.location,
|
182
|
+
)
|
183
|
+
definition.ignored! if @ignored.include?(symbol_def)
|
184
|
+
definition.alive! if @references.key?(symbol.name)
|
185
|
+
define(definition)
|
186
|
+
when Model::AttrReader
|
187
|
+
definition = Definition.new(
|
188
|
+
kind: Definition::Kind::AttrReader,
|
189
|
+
name: symbol.name,
|
190
|
+
full_name: symbol.full_name,
|
191
|
+
location: symbol_def.location,
|
192
|
+
)
|
193
|
+
definition.ignored! if @ignored.include?(symbol_def)
|
194
|
+
definition.alive! if @references.key?(symbol.name)
|
195
|
+
define(definition)
|
196
|
+
when Model::AttrWriter
|
197
|
+
definition = Definition.new(
|
198
|
+
kind: Definition::Kind::AttrWriter,
|
199
|
+
name: "#{symbol.name}=",
|
200
|
+
full_name: "#{symbol.full_name}=",
|
201
|
+
location: symbol_def.location,
|
202
|
+
)
|
203
|
+
definition.ignored! if @ignored.include?(symbol_def)
|
204
|
+
definition.alive! if @references.key?(symbol.name)
|
205
|
+
define(definition)
|
206
|
+
end
|
207
|
+
end
|
40
208
|
end
|
41
209
|
end
|
42
210
|
|
@@ -52,7 +220,7 @@ module Spoom
|
|
52
220
|
@definitions.values.flatten
|
53
221
|
end
|
54
222
|
|
55
|
-
sig { returns(T::Array[Reference]) }
|
223
|
+
sig { returns(T::Array[Model::Reference]) }
|
56
224
|
def all_references
|
57
225
|
@references.values.flatten
|
58
226
|
end
|