spoom 1.3.2 → 1.3.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|