spoom 1.6.1 → 1.6.2
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/README.md +14 -0
- data/lib/spoom/backtrace_filter/minitest.rb +1 -1
- data/lib/spoom/cli/helper.rb +8 -6
- data/lib/spoom/cli/srb/coverage.rb +1 -1
- data/lib/spoom/cli/srb/sigs.rb +3 -2
- data/lib/spoom/cli/srb/tc.rb +16 -1
- data/lib/spoom/context/exec.rb +1 -1
- data/lib/spoom/context.rb +1 -1
- data/lib/spoom/coverage/report.rb +3 -3
- data/lib/spoom/coverage/snapshot.rb +1 -1
- data/lib/spoom/deadcode/index.rb +3 -3
- data/lib/spoom/deadcode/plugins/actionpack.rb +14 -17
- data/lib/spoom/deadcode/plugins/active_record.rb +41 -50
- data/lib/spoom/deadcode/plugins/active_support.rb +1 -1
- data/lib/spoom/deadcode/plugins/rubocop.rb +1 -1
- data/lib/spoom/deadcode/plugins.rb +21 -27
- data/lib/spoom/deadcode/remover.rb +21 -20
- data/lib/spoom/file_collector.rb +1 -1
- data/lib/spoom/file_tree.rb +5 -5
- data/lib/spoom/model/builder.rb +6 -9
- data/lib/spoom/model/model.rb +6 -6
- data/lib/spoom/model/namespace_visitor.rb +1 -1
- data/lib/spoom/model/references_visitor.rb +1 -1
- data/lib/spoom/poset.rb +5 -5
- data/lib/spoom/sorbet/assertions.rb +3 -3
- data/lib/spoom/sorbet/config.rb +6 -6
- data/lib/spoom/sorbet/errors.rb +71 -14
- data/lib/spoom/sorbet/lsp/base.rb +1 -1
- data/lib/spoom/sorbet/lsp/structures.rb +29 -32
- data/lib/spoom/sorbet/lsp.rb +5 -5
- data/lib/spoom/sorbet/sigils.rb +9 -12
- data/lib/spoom/sorbet/sigs.rb +6 -6
- data/lib/spoom/sorbet.rb +3 -3
- data/lib/spoom/version.rb +1 -1
- data/lib/spoom.rb +1 -1
- data/rbi/spoom.rbi +8 -569
- metadata +20 -6
data/lib/spoom/model/builder.rb
CHANGED
@@ -11,15 +11,12 @@ module Spoom
|
|
11
11
|
|
12
12
|
@model = model
|
13
13
|
@file = file
|
14
|
-
@comments_by_line =
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@namespace_nesting = T.let([], T::Array[Namespace])
|
21
|
-
@visibility_stack = T.let([Visibility::Public], T::Array[Visibility])
|
22
|
-
@last_sigs = T.let([], T::Array[Sig])
|
14
|
+
@comments_by_line = comments.to_h do |c|
|
15
|
+
[c.location.start_line, c]
|
16
|
+
end #: Hash[Integer, Prism::Comment]
|
17
|
+
@namespace_nesting = [] #: Array[Namespace]
|
18
|
+
@visibility_stack = [Visibility::Public] #: Array[Visibility]
|
19
|
+
@last_sigs = [] #: Array[Sig]
|
23
20
|
end
|
24
21
|
|
25
22
|
# Classes
|
data/lib/spoom/model/model.rb
CHANGED
@@ -36,7 +36,7 @@ module Spoom
|
|
36
36
|
#: (String full_name) -> void
|
37
37
|
def initialize(full_name)
|
38
38
|
@full_name = full_name
|
39
|
-
@definitions =
|
39
|
+
@definitions = [] #: Array[SymbolDef]
|
40
40
|
end
|
41
41
|
|
42
42
|
# The short name of this symbol
|
@@ -122,8 +122,8 @@ module Spoom
|
|
122
122
|
def initialize(symbol, owner:, location:, comments: [])
|
123
123
|
super(symbol, owner: owner, location: location, comments: comments)
|
124
124
|
|
125
|
-
@children =
|
126
|
-
@mixins =
|
125
|
+
@children = [] #: Array[SymbolDef]
|
126
|
+
@mixins = [] #: Array[Mixin]
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
@@ -233,8 +233,8 @@ module Spoom
|
|
233
233
|
|
234
234
|
#: -> void
|
235
235
|
def initialize
|
236
|
-
@symbols =
|
237
|
-
@symbols_hierarchy =
|
236
|
+
@symbols = {} #: Hash[String, Symbol]
|
237
|
+
@symbols_hierarchy = Poset[Symbol].new #: Poset[Symbol]
|
238
238
|
end
|
239
239
|
|
240
240
|
# Get a symbol by it's full name
|
@@ -263,7 +263,7 @@ module Spoom
|
|
263
263
|
return @symbols[full_name] ||= UnresolvedSymbol.new(full_name)
|
264
264
|
end
|
265
265
|
|
266
|
-
target =
|
266
|
+
target = @symbols[full_name] #: Symbol?
|
267
267
|
return target if target
|
268
268
|
|
269
269
|
parts = context.full_name.split("::")
|
data/lib/spoom/poset.rb
CHANGED
@@ -15,7 +15,7 @@ module Spoom
|
|
15
15
|
|
16
16
|
#: -> void
|
17
17
|
def initialize
|
18
|
-
@elements =
|
18
|
+
@elements = {} #: Hash[E, Element[E]]
|
19
19
|
end
|
20
20
|
|
21
21
|
# Get the POSet element for a given value
|
@@ -149,10 +149,10 @@ module Spoom
|
|
149
149
|
#: (E value) -> void
|
150
150
|
def initialize(value)
|
151
151
|
@value = value
|
152
|
-
@dtos =
|
153
|
-
@tos =
|
154
|
-
@dfroms =
|
155
|
-
@froms =
|
152
|
+
@dtos = Set.new #: Set[Element[E]]
|
153
|
+
@tos = Set.new #: Set[Element[E]]
|
154
|
+
@dfroms = Set.new #: Set[Element[E]]
|
155
|
+
@froms = Set.new #: Set[Element[E]]
|
156
156
|
end
|
157
157
|
|
158
158
|
#: (untyped other) -> Integer?
|
@@ -144,7 +144,7 @@ module Spoom
|
|
144
144
|
end
|
145
145
|
|
146
146
|
class Locator < Spoom::Visitor
|
147
|
-
ANNOTATION_METHODS =
|
147
|
+
ANNOTATION_METHODS = [:let] #: Array[Symbol]
|
148
148
|
|
149
149
|
#: Array[AssignNode]
|
150
150
|
attr_reader :assigns
|
@@ -152,7 +152,7 @@ module Spoom
|
|
152
152
|
#: -> void
|
153
153
|
def initialize
|
154
154
|
super
|
155
|
-
@assigns =
|
155
|
+
@assigns = [] #: Array[AssignNode]
|
156
156
|
end
|
157
157
|
|
158
158
|
#: (AssignType) -> void
|
@@ -254,7 +254,7 @@ module Spoom
|
|
254
254
|
|
255
255
|
#: -> void
|
256
256
|
def initialize
|
257
|
-
@contains_heredoc =
|
257
|
+
@contains_heredoc = false #: bool
|
258
258
|
|
259
259
|
super
|
260
260
|
end
|
data/lib/spoom/sorbet/config.rb
CHANGED
@@ -24,7 +24,7 @@ module Spoom
|
|
24
24
|
# puts config.ignore # "c"
|
25
25
|
# ```
|
26
26
|
class Config
|
27
|
-
DEFAULT_ALLOWED_EXTENSIONS =
|
27
|
+
DEFAULT_ALLOWED_EXTENSIONS = [".rb", ".rbi"].freeze #: Array[String]
|
28
28
|
|
29
29
|
#: Array[String]
|
30
30
|
attr_accessor :paths, :ignore, :allowed_extensions
|
@@ -34,10 +34,10 @@ module Spoom
|
|
34
34
|
|
35
35
|
#: -> void
|
36
36
|
def initialize
|
37
|
-
@paths =
|
38
|
-
@ignore =
|
39
|
-
@allowed_extensions =
|
40
|
-
@no_stdlib =
|
37
|
+
@paths = [] #: Array[String]
|
38
|
+
@ignore = [] #: Array[String]
|
39
|
+
@allowed_extensions = [] #: Array[String]
|
40
|
+
@no_stdlib = false #: bool
|
41
41
|
end
|
42
42
|
|
43
43
|
#: -> Config
|
@@ -81,7 +81,7 @@ module Spoom
|
|
81
81
|
#: (String sorbet_config) -> Spoom::Sorbet::Config
|
82
82
|
def parse_string(sorbet_config)
|
83
83
|
config = Config.new
|
84
|
-
state =
|
84
|
+
state = nil #: Symbol?
|
85
85
|
sorbet_config.each_line do |line|
|
86
86
|
line = line.strip
|
87
87
|
case line
|
data/lib/spoom/sorbet/errors.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require "rexml/document"
|
5
|
+
|
4
6
|
module Spoom
|
5
7
|
module Sorbet
|
6
8
|
module Errors
|
@@ -11,21 +13,47 @@ module Spoom
|
|
11
13
|
def sort_errors_by_code(errors)
|
12
14
|
errors.sort_by { |e| [e.code, e.file, e.line, e.message] }
|
13
15
|
end
|
16
|
+
|
17
|
+
#: (Array[Error]) -> REXML::Document
|
18
|
+
def to_junit_xml(errors)
|
19
|
+
testsuite_element = REXML::Element.new("testsuite")
|
20
|
+
testsuite_element.add_attributes(
|
21
|
+
"name" => "Sorbet",
|
22
|
+
"failures" => errors.size,
|
23
|
+
)
|
24
|
+
|
25
|
+
if errors.empty?
|
26
|
+
# Avoid creating an empty report when there are no errors so that
|
27
|
+
# reporting tools know that the type checking ran successfully.
|
28
|
+
testcase_element = testsuite_element.add_element("testcase")
|
29
|
+
testcase_element.add_attributes(
|
30
|
+
"name" => "Typecheck",
|
31
|
+
"tests" => 1,
|
32
|
+
)
|
33
|
+
else
|
34
|
+
errors.each do |error|
|
35
|
+
testsuite_element.add_element(error.to_junit_xml_element)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
doc = REXML::Document.new
|
40
|
+
doc << REXML::XMLDecl.new
|
41
|
+
doc.add_element(testsuite_element)
|
42
|
+
|
43
|
+
doc
|
44
|
+
end
|
14
45
|
end
|
15
46
|
# Parse errors from Sorbet output
|
16
47
|
class Parser
|
17
48
|
class ParseError < Spoom::Error; end
|
18
49
|
|
19
|
-
HEADER =
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
],
|
27
|
-
T::Array[String],
|
28
|
-
)
|
50
|
+
HEADER = [
|
51
|
+
"👋 Hey there! Heads up that this is not a release build of sorbet.",
|
52
|
+
"Release builds are faster and more well-supported by the Sorbet team.",
|
53
|
+
"Check out the README to learn how to build Sorbet in release mode.",
|
54
|
+
"To forcibly silence this error, either pass --silence-dev-message,",
|
55
|
+
"or set SORBET_SILENCE_DEV_MESSAGE=1 in your shell environment.",
|
56
|
+
] #: Array[String]
|
29
57
|
|
30
58
|
class << self
|
31
59
|
#: (String output, ?error_url_base: String) -> Array[Error]
|
@@ -37,9 +65,9 @@ module Spoom
|
|
37
65
|
|
38
66
|
#: (?error_url_base: String) -> void
|
39
67
|
def initialize(error_url_base: DEFAULT_ERROR_URL_BASE)
|
40
|
-
@errors =
|
41
|
-
@error_line_match_regex =
|
42
|
-
@current_error =
|
68
|
+
@errors = [] #: Array[Error]
|
69
|
+
@error_line_match_regex = error_line_match_regexp(error_url_base) #: Regexp
|
70
|
+
@current_error = nil #: Error?
|
43
71
|
end
|
44
72
|
|
45
73
|
#: (String output) -> Array[Error]
|
@@ -141,7 +169,7 @@ module Spoom
|
|
141
169
|
@message = message
|
142
170
|
@code = code
|
143
171
|
@more = more
|
144
|
-
@files_from_error_sections =
|
172
|
+
@files_from_error_sections = Set.new #: Set[String]
|
145
173
|
end
|
146
174
|
|
147
175
|
# By default errors are sorted by location
|
@@ -156,6 +184,35 @@ module Spoom
|
|
156
184
|
def to_s
|
157
185
|
"#{file}:#{line}: #{message} (#{code})"
|
158
186
|
end
|
187
|
+
|
188
|
+
#: -> REXML::Element
|
189
|
+
def to_junit_xml_element
|
190
|
+
testcase_element = REXML::Element.new("testcase")
|
191
|
+
# Unlike traditional test suites, we can't report all tests
|
192
|
+
# regardless of outcome; we only have errors to report. As a
|
193
|
+
# result we reinterpret the definitions of the test properties
|
194
|
+
# bit: the error message becomes the test name and the full error
|
195
|
+
# info gets plugged into the failure body along with file/line
|
196
|
+
# information (displayed in Jenkins as the "Stacktrace" for the
|
197
|
+
# error).
|
198
|
+
testcase_element.add_attributes(
|
199
|
+
"name" => message,
|
200
|
+
"file" => file,
|
201
|
+
"line" => line,
|
202
|
+
)
|
203
|
+
failure_element = testcase_element.add_element("failure")
|
204
|
+
failure_element.add_attributes(
|
205
|
+
"type" => code,
|
206
|
+
)
|
207
|
+
explanation_text = [
|
208
|
+
"In file #{file}:\n",
|
209
|
+
*more,
|
210
|
+
].join.chomp
|
211
|
+
# Use CDATA so that parsers know the whitespace is significant.
|
212
|
+
failure_element.add(REXML::CData.new(explanation_text))
|
213
|
+
|
214
|
+
testcase_element
|
215
|
+
end
|
159
216
|
end
|
160
217
|
end
|
161
218
|
end
|
@@ -259,37 +259,34 @@ module Spoom
|
|
259
259
|
SYMBOL_KINDS[kind] || "<unknown:#{kind}>"
|
260
260
|
end
|
261
261
|
|
262
|
-
SYMBOL_KINDS =
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
},
|
291
|
-
T::Hash[Integer, String],
|
292
|
-
)
|
262
|
+
SYMBOL_KINDS = {
|
263
|
+
1 => "file",
|
264
|
+
2 => "module",
|
265
|
+
3 => "namespace",
|
266
|
+
4 => "package",
|
267
|
+
5 => "class",
|
268
|
+
6 => "def",
|
269
|
+
7 => "property",
|
270
|
+
8 => "field",
|
271
|
+
9 => "constructor",
|
272
|
+
10 => "enum",
|
273
|
+
11 => "interface",
|
274
|
+
12 => "function",
|
275
|
+
13 => "variable",
|
276
|
+
14 => "const",
|
277
|
+
15 => "string",
|
278
|
+
16 => "number",
|
279
|
+
17 => "boolean",
|
280
|
+
18 => "array",
|
281
|
+
19 => "object",
|
282
|
+
20 => "key",
|
283
|
+
21 => "null",
|
284
|
+
22 => "enum_member",
|
285
|
+
23 => "struct",
|
286
|
+
24 => "event",
|
287
|
+
25 => "operator",
|
288
|
+
26 => "type_parameter",
|
289
|
+
} #: Hash[Integer, String]
|
293
290
|
end
|
294
291
|
|
295
292
|
class SymbolPrinter < Printer
|
@@ -302,7 +299,7 @@ module Spoom
|
|
302
299
|
#: (?out: (IO | StringIO), ?colors: bool, ?indent_level: Integer, ?prefix: String?) -> void
|
303
300
|
def initialize(out: $stdout, colors: true, indent_level: 0, prefix: nil)
|
304
301
|
super(out: out, colors: colors, indent_level: indent_level)
|
305
|
-
@seen =
|
302
|
+
@seen = Set.new #: Set[Integer]
|
306
303
|
@out = out
|
307
304
|
@colors = colors
|
308
305
|
@indent_level = indent_level
|
data/lib/spoom/sorbet/lsp.rb
CHANGED
@@ -13,12 +13,12 @@ module Spoom
|
|
13
13
|
class Client
|
14
14
|
#: (String sorbet_bin, *String sorbet_args, ?path: String) -> void
|
15
15
|
def initialize(sorbet_bin, *sorbet_args, path: ".")
|
16
|
-
@id =
|
17
|
-
@open =
|
16
|
+
@id = 0 #: Integer
|
17
|
+
@open = false #: bool
|
18
18
|
io_in, io_out, io_err, _status = T.unsafe(Open3).popen3(sorbet_bin, *sorbet_args, chdir: path)
|
19
|
-
@in =
|
20
|
-
@out =
|
21
|
-
@err =
|
19
|
+
@in = io_in #: IO
|
20
|
+
@out = io_out #: IO
|
21
|
+
@err = io_err #: IO
|
22
22
|
end
|
23
23
|
|
24
24
|
#: -> Integer
|
data/lib/spoom/sorbet/sigils.rb
CHANGED
@@ -14,19 +14,16 @@ module Spoom
|
|
14
14
|
STRICTNESS_STRONG = "strong"
|
15
15
|
STRICTNESS_INTERNAL = "__STDLIB_INTERNAL"
|
16
16
|
|
17
|
-
VALID_STRICTNESS =
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
].freeze,
|
26
|
-
T::Array[String],
|
27
|
-
)
|
17
|
+
VALID_STRICTNESS = [
|
18
|
+
STRICTNESS_IGNORE,
|
19
|
+
STRICTNESS_FALSE,
|
20
|
+
STRICTNESS_TRUE,
|
21
|
+
STRICTNESS_STRICT,
|
22
|
+
STRICTNESS_STRONG,
|
23
|
+
STRICTNESS_INTERNAL,
|
24
|
+
].freeze #: Array[String]
|
28
25
|
|
29
|
-
SIGIL_REGEXP =
|
26
|
+
SIGIL_REGEXP = /^#[[:blank:]]*typed:[[:blank:]]*(\S*)/ #: Regexp
|
30
27
|
|
31
28
|
class << self
|
32
29
|
# returns the full sigil comment string for the passed strictness
|
data/lib/spoom/sorbet/sigs.rb
CHANGED
@@ -95,8 +95,8 @@ module Spoom
|
|
95
95
|
#: -> void
|
96
96
|
def initialize
|
97
97
|
super
|
98
|
-
@sigs =
|
99
|
-
@rbs_comments =
|
98
|
+
@sigs = [] #: Array[[RBI::Sig, (RBI::Method | RBI::Attr)]]
|
99
|
+
@rbs_comments = [] #: Array[[RBI::RBSComment, (RBI::Method | RBI::Attr)]]
|
100
100
|
end
|
101
101
|
|
102
102
|
# @override
|
@@ -246,13 +246,13 @@ module Spoom
|
|
246
246
|
|
247
247
|
# From https://github.com/Shopify/ruby-lsp/blob/9154bfc6ef/lib/ruby_lsp/document.rb#L127
|
248
248
|
class Scanner
|
249
|
-
LINE_BREAK =
|
249
|
+
LINE_BREAK = 0x0A #: Integer
|
250
250
|
|
251
251
|
#: (String source) -> void
|
252
252
|
def initialize(source)
|
253
|
-
@current_line =
|
254
|
-
@pos =
|
255
|
-
@source =
|
253
|
+
@current_line = 0 #: Integer
|
254
|
+
@pos = 0 #: Integer
|
255
|
+
@source = source.codepoints #: Array[Integer]
|
256
256
|
end
|
257
257
|
|
258
258
|
# Finds the character index inside the source string for a given line and column
|
data/lib/spoom/sorbet.rb
CHANGED
@@ -28,9 +28,9 @@ module Spoom
|
|
28
28
|
end
|
29
29
|
|
30
30
|
CONFIG_PATH = "sorbet/config"
|
31
|
-
GEM_PATH =
|
32
|
-
GEM_VERSION =
|
33
|
-
BIN_PATH =
|
31
|
+
GEM_PATH = Gem::Specification.find_by_name("sorbet-static").full_gem_path #: String
|
32
|
+
GEM_VERSION = Gem::Specification.find_by_name("sorbet-static-and-runtime").version.to_s #: String
|
33
|
+
BIN_PATH = (Pathname.new(GEM_PATH) / "libexec" / "sorbet").to_s #: String
|
34
34
|
|
35
35
|
KILLED_CODE = 137
|
36
36
|
SEGFAULT_CODE = 139
|
data/lib/spoom/version.rb
CHANGED
data/lib/spoom.rb
CHANGED