spoom 1.7.7 → 1.7.9
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/context/file_system.rb +5 -1
- data/lib/spoom/coverage/d3/circle_map.rb +6 -1
- data/lib/spoom/coverage/report.rb +16 -2
- data/lib/spoom/deadcode/index.rb +1 -1
- data/lib/spoom/model/model.rb +15 -2
- data/lib/spoom/parse.rb +5 -6
- data/lib/spoom/rbs.rb +8 -1
- data/lib/spoom/sorbet/metrics/code_metrics_visitor.rb +1 -1
- data/lib/spoom/sorbet/translate/rbs_comments_to_sorbet_sigs.rb +85 -0
- data/lib/spoom/sorbet/translate/sorbet_assertions_to_rbs_comments.rb +4 -2
- data/lib/spoom/sorbet/translate/sorbet_sigs_to_rbs_comments.rb +6 -1
- data/lib/spoom/sorbet/translate/translator.rb +2 -1
- data/lib/spoom/source/rewriter.rb +4 -2
- data/lib/spoom/version.rb +1 -1
- data/rbi/spoom.rbi +12 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 24a0283b0a0b16cef6e3547725768b9d3d63673efec428dee7859e6bb398add0
|
|
4
|
+
data.tar.gz: 2e3037e4cf693ac776412e9817d2a1309a39b50bc58bff639dccce5fc9790a9e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1168d181aa2f2d7512dfae7f01b8845a247bfecc5b8ea85441c9377a7bd33e51ab288d4078f3b86bbceef129c81721ba68a386095dd8feb215d0afea93bb1788
|
|
7
|
+
data.tar.gz: f9e7e65da944d81186ca772535b84befff429ce4c0cb9406ce2d0f6b9f0b5b46ba2efc67ab942782e635dd339139f9d902e71092a86c1ea28f221a0e219af0be
|
|
@@ -39,7 +39,11 @@ module Spoom
|
|
|
39
39
|
glob("*")
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
-
#: (
|
|
42
|
+
#: (
|
|
43
|
+
#| ?allow_extensions: Array[String],
|
|
44
|
+
#| ?allow_mime_types: Array[String],
|
|
45
|
+
#| ?exclude_patterns: Array[String]
|
|
46
|
+
#| ) -> Array[String]
|
|
43
47
|
def collect_files(allow_extensions: [], allow_mime_types: [], exclude_patterns: [])
|
|
44
48
|
collector = FileCollector.new(
|
|
45
49
|
allow_extensions: allow_extensions,
|
|
@@ -145,7 +145,12 @@ module Spoom
|
|
|
145
145
|
end
|
|
146
146
|
|
|
147
147
|
class Sigils < CircleMap
|
|
148
|
-
#: (
|
|
148
|
+
#: (
|
|
149
|
+
#| String id,
|
|
150
|
+
#| FileTree file_tree,
|
|
151
|
+
#| Hash[FileTree::Node, String?] nodes_strictnesses,
|
|
152
|
+
#| Hash[FileTree::Node, Float] nodes_scores
|
|
153
|
+
#| ) -> void
|
|
149
154
|
def initialize(id, file_tree, nodes_strictnesses, nodes_scores)
|
|
150
155
|
@nodes_strictnesses = nodes_strictnesses
|
|
151
156
|
@nodes_scores = nodes_scores
|
|
@@ -138,7 +138,12 @@ module Spoom
|
|
|
138
138
|
end
|
|
139
139
|
|
|
140
140
|
class Map < Card
|
|
141
|
-
#: (
|
|
141
|
+
#: (
|
|
142
|
+
#| file_tree: FileTree,
|
|
143
|
+
#| nodes_strictnesses: Hash[FileTree::Node, String?],
|
|
144
|
+
#| nodes_strictness_scores: Hash[FileTree::Node, Float],
|
|
145
|
+
#| ?title: String
|
|
146
|
+
#| ) -> void
|
|
142
147
|
def initialize(file_tree:, nodes_strictnesses:, nodes_strictness_scores:, title: "Strictness Map")
|
|
143
148
|
super(
|
|
144
149
|
title: title,
|
|
@@ -222,7 +227,16 @@ module Spoom
|
|
|
222
227
|
end
|
|
223
228
|
|
|
224
229
|
class Report < Page
|
|
225
|
-
#: (
|
|
230
|
+
#: (
|
|
231
|
+
#| project_name: String,
|
|
232
|
+
#| palette: D3::ColorPalette,
|
|
233
|
+
#| snapshots: Array[Snapshot],
|
|
234
|
+
#| file_tree: FileTree,
|
|
235
|
+
#| nodes_strictnesses: Hash[FileTree::Node, String?],
|
|
236
|
+
#| nodes_strictness_scores: Hash[FileTree::Node, Float],
|
|
237
|
+
#| ?sorbet_intro_commit: String?,
|
|
238
|
+
#| ?sorbet_intro_date: Time?
|
|
239
|
+
#| ) -> void
|
|
226
240
|
def initialize(
|
|
227
241
|
project_name:,
|
|
228
242
|
palette:,
|
data/lib/spoom/deadcode/index.rb
CHANGED
|
@@ -49,7 +49,7 @@ module Spoom
|
|
|
49
49
|
|
|
50
50
|
#: (String rb, file: String, ?plugins: Array[Plugins::Base]) -> void
|
|
51
51
|
def index_ruby(rb, file:, plugins: [])
|
|
52
|
-
node = Spoom.parse_ruby(rb, file: file
|
|
52
|
+
node, _ = Spoom.parse_ruby(rb, file: file)
|
|
53
53
|
|
|
54
54
|
# Index definitions
|
|
55
55
|
model_builder = Model::Builder.new(@model, file)
|
data/lib/spoom/model/model.rb
CHANGED
|
@@ -129,7 +129,13 @@ module Spoom
|
|
|
129
129
|
#: String?
|
|
130
130
|
attr_accessor :superclass_name
|
|
131
131
|
|
|
132
|
-
#: (
|
|
132
|
+
#: (
|
|
133
|
+
#| Symbol symbol,
|
|
134
|
+
#| owner: Namespace?,
|
|
135
|
+
#| location: Location,
|
|
136
|
+
#| ?superclass_name: String?,
|
|
137
|
+
#| ?comments: Array[Comment]
|
|
138
|
+
#| ) -> void
|
|
133
139
|
def initialize(symbol, owner:, location:, superclass_name: nil, comments: [])
|
|
134
140
|
super(symbol, owner: owner, location: location, comments: comments)
|
|
135
141
|
|
|
@@ -160,7 +166,14 @@ module Spoom
|
|
|
160
166
|
#: Array[Sig]
|
|
161
167
|
attr_reader :sigs
|
|
162
168
|
|
|
163
|
-
#: (
|
|
169
|
+
#: (
|
|
170
|
+
#| Symbol symbol,
|
|
171
|
+
#| owner: Namespace?,
|
|
172
|
+
#| location: Location,
|
|
173
|
+
#| visibility: Visibility,
|
|
174
|
+
#| ?sigs: Array[Sig],
|
|
175
|
+
#| ?comments: Array[Comment]
|
|
176
|
+
#| ) -> void
|
|
164
177
|
def initialize(symbol, owner:, location:, visibility:, sigs: [], comments: [])
|
|
165
178
|
super(symbol, owner: owner, location: location, comments: comments)
|
|
166
179
|
|
data/lib/spoom/parse.rb
CHANGED
|
@@ -7,22 +7,21 @@ module Spoom
|
|
|
7
7
|
class ParseError < Error; end
|
|
8
8
|
|
|
9
9
|
class << self
|
|
10
|
-
#: (String ruby, file: String
|
|
11
|
-
def parse_ruby(ruby, file
|
|
10
|
+
#: (String ruby, file: String) -> [Prism::Node, Array[Prism::Comment]]
|
|
11
|
+
def parse_ruby(ruby, file:)
|
|
12
12
|
result = Prism.parse(ruby)
|
|
13
|
+
|
|
13
14
|
unless result.success?
|
|
14
15
|
message = +"Error while parsing #{file}:\n"
|
|
15
|
-
|
|
16
16
|
result.errors.each do |e|
|
|
17
17
|
message << "- #{e.message} (at #{e.location.start_line}:#{e.location.start_column})\n"
|
|
18
18
|
end
|
|
19
|
-
|
|
20
19
|
raise ParseError, message
|
|
21
20
|
end
|
|
22
21
|
|
|
23
|
-
result.attach_comments!
|
|
22
|
+
result.attach_comments!
|
|
24
23
|
|
|
25
|
-
result.value
|
|
24
|
+
[result.value, result.comments]
|
|
26
25
|
end
|
|
27
26
|
end
|
|
28
27
|
end
|
data/lib/spoom/rbs.rb
CHANGED
|
@@ -39,7 +39,13 @@ module Spoom
|
|
|
39
39
|
def method_annotations
|
|
40
40
|
@annotations.select do |annotation|
|
|
41
41
|
case annotation.string
|
|
42
|
-
when "@abstract",
|
|
42
|
+
when "@abstract",
|
|
43
|
+
"@final",
|
|
44
|
+
"@override",
|
|
45
|
+
"@override(allow_incompatible: true)",
|
|
46
|
+
"@override(allow_incompatible: :visibility)",
|
|
47
|
+
"@overridable",
|
|
48
|
+
"@without_runtime"
|
|
43
49
|
true
|
|
44
50
|
else
|
|
45
51
|
false
|
|
@@ -64,6 +70,7 @@ module Spoom
|
|
|
64
70
|
|
|
65
71
|
class Annotation < Comment; end
|
|
66
72
|
class Signature < Comment; end
|
|
73
|
+
class TypeAlias < Comment; end
|
|
67
74
|
|
|
68
75
|
module ExtractRBSComments
|
|
69
76
|
#: (Prism::Node) -> Comments
|
|
@@ -13,7 +13,7 @@ module Spoom
|
|
|
13
13
|
counters.increment("files")
|
|
14
14
|
|
|
15
15
|
content = File.read(file)
|
|
16
|
-
node = Spoom.parse_ruby(content, file: file
|
|
16
|
+
node, _ = Spoom.parse_ruby(content, file: file)
|
|
17
17
|
visitor = CodeMetricsVisitor.new(counters)
|
|
18
18
|
visitor.visit(node)
|
|
19
19
|
end
|
|
@@ -14,6 +14,16 @@ module Spoom
|
|
|
14
14
|
@max_line_length = max_line_length
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
+
# @override
|
|
18
|
+
#: (Prism::ProgramNode node) -> void
|
|
19
|
+
def visit_program_node(node)
|
|
20
|
+
# Process all type aliases from the entire file first
|
|
21
|
+
apply_type_aliases(@comments)
|
|
22
|
+
|
|
23
|
+
# Now process the rest of the file with type aliases available
|
|
24
|
+
super
|
|
25
|
+
end
|
|
26
|
+
|
|
17
27
|
# @override
|
|
18
28
|
#: (Prism::ClassNode node) -> void
|
|
19
29
|
def visit_class_node(node)
|
|
@@ -248,6 +258,9 @@ module Spoom
|
|
|
248
258
|
when "@override(allow_incompatible: true)"
|
|
249
259
|
sig.is_override = true
|
|
250
260
|
sig.allow_incompatible_override = true
|
|
261
|
+
when "@override(allow_incompatible: :visibility)"
|
|
262
|
+
sig.is_override = true
|
|
263
|
+
sig.allow_incompatible_override_visibility = true
|
|
251
264
|
when "@overridable"
|
|
252
265
|
sig.is_overridable = true
|
|
253
266
|
when "@without_runtime"
|
|
@@ -271,6 +284,78 @@ module Spoom
|
|
|
271
284
|
true
|
|
272
285
|
end
|
|
273
286
|
end
|
|
287
|
+
|
|
288
|
+
#: (Array[Prism::Comment]) -> Array[RBS::TypeAlias]
|
|
289
|
+
def collect_type_aliases(comments)
|
|
290
|
+
type_aliases = [] #: Array[RBS::TypeAlias]
|
|
291
|
+
|
|
292
|
+
return type_aliases if comments.empty?
|
|
293
|
+
|
|
294
|
+
continuation_comments = [] #: Array[Prism::Comment]
|
|
295
|
+
|
|
296
|
+
comments.reverse_each do |comment|
|
|
297
|
+
string = comment.slice
|
|
298
|
+
|
|
299
|
+
if string.start_with?("#:")
|
|
300
|
+
string = string.delete_prefix("#:").strip
|
|
301
|
+
location = comment.location
|
|
302
|
+
|
|
303
|
+
if string.start_with?("type ")
|
|
304
|
+
continuation_comments.reverse_each do |continuation_comment|
|
|
305
|
+
string = "#{string}#{continuation_comment.slice.delete_prefix("#|")}"
|
|
306
|
+
location = location.join(continuation_comment.location)
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
type_aliases << Spoom::RBS::TypeAlias.new(string, location)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Clear the continuation comments regardless of whether we found a type alias or not
|
|
313
|
+
continuation_comments.clear
|
|
314
|
+
elsif string.start_with?("#|")
|
|
315
|
+
continuation_comments << comment
|
|
316
|
+
else
|
|
317
|
+
continuation_comments.clear
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
type_aliases
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
#: (Array[Prism::Comment]) -> void
|
|
325
|
+
def apply_type_aliases(comments)
|
|
326
|
+
type_aliases = collect_type_aliases(comments)
|
|
327
|
+
|
|
328
|
+
type_aliases.each do |type_alias|
|
|
329
|
+
indent = " " * type_alias.location.start_column
|
|
330
|
+
insert_pos = adjust_to_line_start(type_alias.location.start_offset)
|
|
331
|
+
|
|
332
|
+
from = insert_pos
|
|
333
|
+
to = adjust_to_line_end(type_alias.location.end_offset)
|
|
334
|
+
|
|
335
|
+
*, decls = ::RBS::Parser.parse_signature(type_alias.string)
|
|
336
|
+
|
|
337
|
+
# We only expect there to be a single type alias declaration
|
|
338
|
+
next unless decls.size == 1 && decls.first.is_a?(::RBS::AST::Declarations::TypeAlias)
|
|
339
|
+
|
|
340
|
+
rbs_type = decls.first
|
|
341
|
+
sorbet_type = RBI::RBS::TypeTranslator.translate(rbs_type.type)
|
|
342
|
+
|
|
343
|
+
alias_name = ::RBS::TypeName.new(
|
|
344
|
+
namespace: rbs_type.name.namespace,
|
|
345
|
+
name: rbs_type.name.name.to_s.gsub(/(?:^|_)([a-z\d]*)/i) do |match|
|
|
346
|
+
match = match.delete_prefix("_")
|
|
347
|
+
!match.empty? ? match[0].upcase.concat(match[1..-1]) : +""
|
|
348
|
+
end,
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
@rewriter << Source::Delete.new(from, to)
|
|
352
|
+
content = "#{indent}#{alias_name} = T.type_alias { #{sorbet_type.to_rbi} }\n"
|
|
353
|
+
@rewriter << Source::Insert.new(insert_pos, content)
|
|
354
|
+
rescue ::RBS::ParsingError, ::RBI::Error
|
|
355
|
+
# Ignore type aliases with errors
|
|
356
|
+
next
|
|
357
|
+
end
|
|
358
|
+
end
|
|
274
359
|
end
|
|
275
360
|
end
|
|
276
361
|
end
|
|
@@ -74,8 +74,10 @@ module Spoom
|
|
|
74
74
|
Prism::ClassVariableWriteNode, Prism::ClassVariableAndWriteNode, Prism::ClassVariableOperatorWriteNode, Prism::ClassVariableOrWriteNode,
|
|
75
75
|
Prism::ConstantWriteNode, Prism::ConstantAndWriteNode, Prism::ConstantOperatorWriteNode, Prism::ConstantOrWriteNode,
|
|
76
76
|
Prism::ConstantPathWriteNode, Prism::ConstantPathAndWriteNode, Prism::ConstantPathOperatorWriteNode, Prism::ConstantPathOrWriteNode,
|
|
77
|
-
Prism::GlobalVariableWriteNode, Prism::GlobalVariableAndWriteNode,
|
|
78
|
-
Prism::
|
|
77
|
+
Prism::GlobalVariableWriteNode, Prism::GlobalVariableAndWriteNode,
|
|
78
|
+
Prism::GlobalVariableOperatorWriteNode, Prism::GlobalVariableOrWriteNode,
|
|
79
|
+
Prism::InstanceVariableWriteNode, Prism::InstanceVariableAndWriteNode,
|
|
80
|
+
Prism::InstanceVariableOperatorWriteNode, Prism::InstanceVariableOrWriteNode,
|
|
79
81
|
Prism::LocalVariableWriteNode, Prism::LocalVariableAndWriteNode, Prism::LocalVariableOperatorWriteNode, Prism::LocalVariableOrWriteNode,
|
|
80
82
|
Prism::CallAndWriteNode, Prism::CallOperatorWriteNode, Prism::CallOrWriteNode
|
|
81
83
|
node.value
|
|
@@ -260,7 +260,10 @@ module Spoom
|
|
|
260
260
|
|
|
261
261
|
arg = body.body.first #: as Prism::Node
|
|
262
262
|
srb_type = RBI::Type.parse_node(arg)
|
|
263
|
-
@rewriter << Source::Insert.new(
|
|
263
|
+
@rewriter << Source::Insert.new(
|
|
264
|
+
parent.location.start_offset,
|
|
265
|
+
"# @requires_ancestor: #{srb_type.rbs_string}\n#{indent}",
|
|
266
|
+
)
|
|
264
267
|
end
|
|
265
268
|
|
|
266
269
|
from = adjust_to_line_start(node.location.start_offset)
|
|
@@ -294,6 +297,8 @@ module Spoom
|
|
|
294
297
|
if sigs.any? { |_, sig| sig.is_override }
|
|
295
298
|
@rewriter << if sigs.any? { |_, sig| sig.allow_incompatible_override }
|
|
296
299
|
Source::Insert.new(insert_pos, "# @override(allow_incompatible: true)\n#{indent}")
|
|
300
|
+
elsif sigs.any? { |_, sig| sig.allow_incompatible_override_visibility }
|
|
301
|
+
Source::Insert.new(insert_pos, "# @override(allow_incompatible: :visibility)\n#{indent}")
|
|
297
302
|
else
|
|
298
303
|
Source::Insert.new(insert_pos, "# @override\n#{indent}")
|
|
299
304
|
end
|
|
@@ -19,8 +19,9 @@ module Spoom
|
|
|
19
19
|
ruby_contents.encode("UTF-8")
|
|
20
20
|
end #: String
|
|
21
21
|
|
|
22
|
-
node = Spoom.parse_ruby(ruby_contents, file: file
|
|
22
|
+
node, comments = Spoom.parse_ruby(ruby_contents, file: file)
|
|
23
23
|
@node = node #: Prism::Node
|
|
24
|
+
@comments = comments #: Array[Prism::Comment]
|
|
24
25
|
@ruby_bytes = ruby_contents.bytes #: Array[Integer]
|
|
25
26
|
@rewriter = Spoom::Source::Rewriter.new #: Source::Rewriter
|
|
26
27
|
end
|
|
@@ -92,7 +92,8 @@ module Spoom
|
|
|
92
92
|
# @override
|
|
93
93
|
#: (Array[Integer]) -> void
|
|
94
94
|
def apply(bytes)
|
|
95
|
-
raise PositionError,
|
|
95
|
+
raise PositionError,
|
|
96
|
+
"Position is out of bounds" if from < 0 || to < 0 || from > bytes.size || to > bytes.size || from > to
|
|
96
97
|
|
|
97
98
|
bytes[from..to] = *text.bytes
|
|
98
99
|
end
|
|
@@ -125,7 +126,8 @@ module Spoom
|
|
|
125
126
|
# @override
|
|
126
127
|
#: (Array[untyped]) -> void
|
|
127
128
|
def apply(bytes)
|
|
128
|
-
raise PositionError,
|
|
129
|
+
raise PositionError,
|
|
130
|
+
"Position is out of bounds" if from < 0 || to < 0 || from > bytes.size || to > bytes.size || from > to
|
|
129
131
|
|
|
130
132
|
bytes[from..to] = "".bytes
|
|
131
133
|
end
|
data/lib/spoom/version.rb
CHANGED
data/rbi/spoom.rbi
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
module Spoom
|
|
8
8
|
class << self
|
|
9
|
-
sig { params(ruby: ::String, file: ::String
|
|
10
|
-
def parse_ruby(ruby, file
|
|
9
|
+
sig { params(ruby: ::String, file: ::String).returns([::Prism::Node, T::Array[::Prism::Comment]]) }
|
|
10
|
+
def parse_ruby(ruby, file:); end
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
|
|
@@ -2584,6 +2584,7 @@ module Spoom::RBS::ExtractRBSComments
|
|
|
2584
2584
|
end
|
|
2585
2585
|
|
|
2586
2586
|
class Spoom::RBS::Signature < ::Spoom::RBS::Comment; end
|
|
2587
|
+
class Spoom::RBS::TypeAlias < ::Spoom::RBS::Comment; end
|
|
2587
2588
|
Spoom::SPOOM_PATH = T.let(T.unsafe(nil), String)
|
|
2588
2589
|
module Spoom::Sorbet; end
|
|
2589
2590
|
Spoom::Sorbet::BIN_PATH = T.let(T.unsafe(nil), String)
|
|
@@ -2893,6 +2894,9 @@ class Spoom::Sorbet::Translate::RBSCommentsToSorbetSigs < ::Spoom::Sorbet::Trans
|
|
|
2893
2894
|
sig { override.params(node: ::Prism::ModuleNode).void }
|
|
2894
2895
|
def visit_module_node(node); end
|
|
2895
2896
|
|
|
2897
|
+
sig { override.params(node: ::Prism::ProgramNode).void }
|
|
2898
|
+
def visit_program_node(node); end
|
|
2899
|
+
|
|
2896
2900
|
sig { override.params(node: ::Prism::SingletonClassNode).void }
|
|
2897
2901
|
def visit_singleton_class_node(node); end
|
|
2898
2902
|
|
|
@@ -2912,6 +2916,12 @@ class Spoom::Sorbet::Translate::RBSCommentsToSorbetSigs < ::Spoom::Sorbet::Trans
|
|
|
2912
2916
|
sig { params(annotations: T::Array[::Spoom::RBS::Annotation], sig: ::RBI::Sig).void }
|
|
2913
2917
|
def apply_member_annotations(annotations, sig); end
|
|
2914
2918
|
|
|
2919
|
+
sig { params(comments: T::Array[::Prism::Comment]).void }
|
|
2920
|
+
def apply_type_aliases(comments); end
|
|
2921
|
+
|
|
2922
|
+
sig { params(comments: T::Array[::Prism::Comment]).returns(T::Array[::Spoom::RBS::TypeAlias]) }
|
|
2923
|
+
def collect_type_aliases(comments); end
|
|
2924
|
+
|
|
2915
2925
|
sig { params(def_node: ::Prism::DefNode, comments: ::Spoom::RBS::Comments).void }
|
|
2916
2926
|
def rewrite_def(def_node, comments); end
|
|
2917
2927
|
|