spoom 1.7.16 → 1.8.0
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/deadcode/plugins/actionpack.rb +4 -2
- data/lib/spoom/deadcode/plugins/active_model.rb +44 -2
- data/lib/spoom/deadcode/plugins/active_record.rb +10 -7
- data/lib/spoom/deadcode/plugins/base.rb +4 -3
- data/lib/spoom/deadcode/plugins/graphql.rb +42 -5
- data/lib/spoom/deadcode/plugins/minitest.rb +3 -3
- data/lib/spoom/deadcode/plugins/ruby.rb +1 -1
- data/lib/spoom/ext/prism_types.rb +14 -0
- data/lib/spoom/model/builder.rb +3 -3
- data/lib/spoom/rbs.rb +20 -2
- data/lib/spoom/sorbet/metrics/code_metrics_visitor.rb +3 -3
- data/lib/spoom/sorbet/translate/rbs_comments_to_sorbet_sigs/base_translator.rb +484 -0
- data/lib/spoom/sorbet/translate/rbs_comments_to_sorbet_sigs/human_readable_translator.rb +72 -0
- data/lib/spoom/sorbet/translate/rbs_comments_to_sorbet_sigs/line_matching_translator.rb +115 -0
- data/lib/spoom/sorbet/translate/rbs_comments_to_sorbet_sigs/options.rb +78 -0
- data/lib/spoom/sorbet/translate/rbs_comments_to_sorbet_sigs.rb +35 -419
- data/lib/spoom/sorbet/translate/sorbet_sigs_to_rbs_comments.rb +2 -2
- data/lib/spoom/sorbet/translate/validator.rb +214 -0
- data/lib/spoom/sorbet/translate.rb +1 -0
- data/lib/spoom/version.rb +1 -1
- data/lib/spoom.rb +2 -0
- data/rbi/spoom.rbi +306 -20
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ae2652efb4a3831e59c02c6a8cc5915f316078ab403afb51932592e83cf924ca
|
|
4
|
+
data.tar.gz: ae568069ab7100286b0260ef741616d24c228a8d35a7739b6a9c771ff48d6a6e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3b52cffe4c34d11dbfcb0f43a50b742aa69cc41b5baad2623504d55ab4252fe4ed541fad2952564ff42774586b15198f6eec713840ccce3e3a33df3e0fc14590
|
|
7
|
+
data.tar.gz: cf0d1e04b1c73e5d96ef84bc3ded2e8871bb3cd08dd875e2a108185d5287bd3b75e29e77d34514d522dcfeaf549ff3ce1394c32a66695652cc110de2961cd904
|
|
@@ -43,11 +43,13 @@ module Spoom
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
send.each_arg_assoc do |key, value|
|
|
46
|
-
|
|
46
|
+
next unless key.is_a?(Prism::SymbolNode)
|
|
47
|
+
|
|
48
|
+
key = key.unescaped
|
|
47
49
|
|
|
48
50
|
case key
|
|
49
51
|
when "if", "unless"
|
|
50
|
-
@index.reference_method(value.
|
|
52
|
+
@index.reference_method(value.unescaped, send.location) if value.is_a?(Prism::SymbolNode)
|
|
51
53
|
else
|
|
52
54
|
@index.reference_constant(camelize(key), send.location)
|
|
53
55
|
end
|
|
@@ -23,13 +23,19 @@ module Spoom
|
|
|
23
23
|
@index.reference_method(arg.unescaped, send.location)
|
|
24
24
|
end
|
|
25
25
|
send.each_arg_assoc do |key, value|
|
|
26
|
-
|
|
26
|
+
next unless key.is_a?(Prism::SymbolNode)
|
|
27
|
+
|
|
28
|
+
key = key.unescaped
|
|
27
29
|
|
|
28
30
|
case key
|
|
29
31
|
when "if", "unless"
|
|
30
|
-
@index.reference_method(value.
|
|
32
|
+
@index.reference_method(value.unescaped, send.location) if value.is_a?(Prism::SymbolNode)
|
|
31
33
|
else
|
|
32
34
|
@index.reference_constant(camelize(key), send.location)
|
|
35
|
+
|
|
36
|
+
if value.is_a?(Prism::HashNode)
|
|
37
|
+
reference_nested_symbol_options(value, send.location)
|
|
38
|
+
end
|
|
33
39
|
end
|
|
34
40
|
end
|
|
35
41
|
when "validates_with"
|
|
@@ -39,6 +45,42 @@ module Spoom
|
|
|
39
45
|
end
|
|
40
46
|
end
|
|
41
47
|
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
NESTED_METHOD_REFERENCE_KEYS = T.let(
|
|
52
|
+
[
|
|
53
|
+
"if",
|
|
54
|
+
"unless",
|
|
55
|
+
"in",
|
|
56
|
+
"with",
|
|
57
|
+
"less_than",
|
|
58
|
+
"greater_than",
|
|
59
|
+
"less_than_or_equal_to",
|
|
60
|
+
"greater_than_or_equal_to",
|
|
61
|
+
"equal_to",
|
|
62
|
+
"other_than",
|
|
63
|
+
].freeze,
|
|
64
|
+
T::Array[String],
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
#: (Prism::HashNode hash_node, Location location) -> void
|
|
68
|
+
def reference_nested_symbol_options(hash_node, location)
|
|
69
|
+
hash_node.elements.each do |assoc|
|
|
70
|
+
next unless assoc.is_a?(Prism::AssocNode)
|
|
71
|
+
|
|
72
|
+
key = assoc.key
|
|
73
|
+
next unless key.is_a?(Prism::SymbolNode)
|
|
74
|
+
|
|
75
|
+
nested_key = key.unescaped
|
|
76
|
+
next unless NESTED_METHOD_REFERENCE_KEYS.include?(nested_key)
|
|
77
|
+
|
|
78
|
+
value = assoc.value
|
|
79
|
+
next unless value.is_a?(Prism::SymbolNode)
|
|
80
|
+
|
|
81
|
+
@index.reference_method(value.unescaped, location)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
42
84
|
end
|
|
43
85
|
end
|
|
44
86
|
end
|
|
@@ -75,11 +75,11 @@ module Spoom
|
|
|
75
75
|
|
|
76
76
|
# Process hash arguments for conditions like if: :method_name
|
|
77
77
|
send.each_arg_assoc do |key, value|
|
|
78
|
-
|
|
78
|
+
next unless key.is_a?(Prism::SymbolNode)
|
|
79
79
|
|
|
80
|
-
case key
|
|
80
|
+
case key.unescaped
|
|
81
81
|
when *CALLBACK_CONDITIONS
|
|
82
|
-
if value
|
|
82
|
+
if value.is_a?(Prism::SymbolNode)
|
|
83
83
|
@index.reference_method(value.unescaped, send.location)
|
|
84
84
|
end
|
|
85
85
|
end
|
|
@@ -93,8 +93,9 @@ module Spoom
|
|
|
93
93
|
case send.name
|
|
94
94
|
when *CRUD_METHODS
|
|
95
95
|
send.each_arg_assoc do |key, _value|
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
next unless key.is_a?(Prism::SymbolNode)
|
|
97
|
+
|
|
98
|
+
@index.reference_method("#{key.unescaped}=", send.location)
|
|
98
99
|
end
|
|
99
100
|
when *ARRAY_METHODS
|
|
100
101
|
send.each_arg(Prism::ArrayNode) do |arg|
|
|
@@ -104,8 +105,10 @@ module Spoom
|
|
|
104
105
|
part.elements.each do |assoc|
|
|
105
106
|
next unless assoc.is_a?(Prism::AssocNode)
|
|
106
107
|
|
|
107
|
-
key = assoc.key
|
|
108
|
-
|
|
108
|
+
key = assoc.key
|
|
109
|
+
next unless key.is_a?(Prism::SymbolNode)
|
|
110
|
+
|
|
111
|
+
@index.reference_method("#{key.unescaped}=", send.location)
|
|
109
112
|
end
|
|
110
113
|
end
|
|
111
114
|
end
|
|
@@ -267,10 +267,11 @@ module Spoom
|
|
|
267
267
|
# class MyPlugin < Spoom::Deadcode::Plugins::Base
|
|
268
268
|
# def on_send(send)
|
|
269
269
|
# return unless send.name == "dsl_method"
|
|
270
|
-
# return if send.args.empty?
|
|
271
270
|
#
|
|
272
|
-
#
|
|
273
|
-
#
|
|
271
|
+
# arg = send.args.first
|
|
272
|
+
# return unless arg.is_a?(Prism::SymbolNode)
|
|
273
|
+
#
|
|
274
|
+
# @index.reference_method(arg.unescaped, send.node, send.loc)
|
|
274
275
|
# end
|
|
275
276
|
# end
|
|
276
277
|
# ~~~
|
|
@@ -22,22 +22,59 @@ module Spoom
|
|
|
22
22
|
"unsubscribed",
|
|
23
23
|
)
|
|
24
24
|
|
|
25
|
+
FIELD_SYMBOL_OPTION_KEYS = ["resolver_method", "method"].freeze #: Array[String]
|
|
26
|
+
ARGUMENT_SYMBOL_OPTION_KEYS = ["prepare", "method"].freeze #: Array[String]
|
|
27
|
+
|
|
25
28
|
# @override
|
|
26
29
|
#: (Send send) -> void
|
|
27
30
|
def on_send(send)
|
|
28
|
-
return unless send.recv.nil?
|
|
31
|
+
return unless send.recv.nil?
|
|
32
|
+
|
|
33
|
+
case send.name
|
|
34
|
+
when "field"
|
|
35
|
+
on_field(send)
|
|
36
|
+
when "argument"
|
|
37
|
+
on_argument(send)
|
|
38
|
+
when "builds"
|
|
39
|
+
on_builds(send)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
29
44
|
|
|
45
|
+
#: (Send send) -> void
|
|
46
|
+
def on_field(send)
|
|
30
47
|
arg = send.args.first
|
|
31
48
|
return unless arg.is_a?(Prism::SymbolNode)
|
|
32
49
|
|
|
33
50
|
@index.reference_method(arg.unescaped, send.location)
|
|
34
51
|
|
|
35
52
|
send.each_arg_assoc do |key, value|
|
|
36
|
-
|
|
37
|
-
next unless key
|
|
38
|
-
next unless value
|
|
53
|
+
next unless key.is_a?(Prism::SymbolNode)
|
|
54
|
+
next unless FIELD_SYMBOL_OPTION_KEYS.include?(key.unescaped)
|
|
55
|
+
next unless value.is_a?(Prism::SymbolNode)
|
|
56
|
+
|
|
57
|
+
@index.reference_method(value.unescaped, send.location)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
#: (Send send) -> void
|
|
62
|
+
def on_argument(send)
|
|
63
|
+
send.each_arg_assoc do |key, value|
|
|
64
|
+
next unless key.is_a?(Prism::SymbolNode)
|
|
65
|
+
next unless ARGUMENT_SYMBOL_OPTION_KEYS.include?(key.unescaped)
|
|
66
|
+
next unless value.is_a?(Prism::SymbolNode)
|
|
67
|
+
|
|
68
|
+
@index.reference_method(value.unescaped, send.location)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
#: (Send send) -> void
|
|
73
|
+
def on_builds(send)
|
|
74
|
+
send.args.each do |arg|
|
|
75
|
+
next unless arg.is_a?(Prism::SymbolNode)
|
|
39
76
|
|
|
40
|
-
@index.reference_method(
|
|
77
|
+
@index.reference_method("build_#{arg.unescaped}", send.location)
|
|
41
78
|
end
|
|
42
79
|
end
|
|
43
80
|
end
|
|
@@ -28,10 +28,10 @@ module Spoom
|
|
|
28
28
|
def on_send(send)
|
|
29
29
|
case send.name
|
|
30
30
|
when "assert_predicate", "refute_predicate"
|
|
31
|
-
|
|
32
|
-
return unless
|
|
31
|
+
arg = send.args[1]
|
|
32
|
+
return unless arg.is_a?(Prism::SymbolNode)
|
|
33
33
|
|
|
34
|
-
@index.reference_method(
|
|
34
|
+
@index.reference_method(arg.unescaped, send.location)
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
end
|
|
@@ -24,7 +24,7 @@ module Spoom
|
|
|
24
24
|
case send.name
|
|
25
25
|
when "const_defined?", "const_get", "const_source_location"
|
|
26
26
|
reference_symbol_as_constant(send, T.must(send.args.first))
|
|
27
|
-
when "send", "__send__", "try"
|
|
27
|
+
when "send", "__send__", "public_send", "try"
|
|
28
28
|
arg = send.args.first
|
|
29
29
|
@index.reference_method(arg.unescaped, send.location) if arg.is_a?(Prism::SymbolNode)
|
|
30
30
|
when "alias_method"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module Spoom
|
|
5
|
+
module PrismTypes
|
|
6
|
+
# Ideally this would just be in a shim in `sorbet/rbi/shims/prism.rbi`, but that causes
|
|
7
|
+
# `bundle exec tapioca gem spoom` to fail. It Spoom's translator to rewrite the RBS signature comments into Sigs,
|
|
8
|
+
# which try to access the `Prism::AnyScopeNode` constant.
|
|
9
|
+
# Because shims aren't executed, no such constant exists at runtime, and the Sig raises a NameError.
|
|
10
|
+
#
|
|
11
|
+
# So instead, we define it here, where the translator can reify it into a real Sorbet `T.type_alias` at runtime.
|
|
12
|
+
#: type anyScopeNode = ::Prism::ClassNode | ::Prism::ModuleNode | ::Prism::SingletonClassNode
|
|
13
|
+
end
|
|
14
|
+
end
|
data/lib/spoom/model/builder.rb
CHANGED
|
@@ -168,7 +168,7 @@ module Spoom
|
|
|
168
168
|
next unless arg.is_a?(Prism::SymbolNode)
|
|
169
169
|
|
|
170
170
|
AttrAccessor.new(
|
|
171
|
-
@model.register_symbol([*@names_nesting, arg.
|
|
171
|
+
@model.register_symbol([*@names_nesting, arg.unescaped].join("::")),
|
|
172
172
|
owner: current_namespace,
|
|
173
173
|
location: node_location(arg),
|
|
174
174
|
visibility: current_visibility,
|
|
@@ -182,7 +182,7 @@ module Spoom
|
|
|
182
182
|
next unless arg.is_a?(Prism::SymbolNode)
|
|
183
183
|
|
|
184
184
|
AttrReader.new(
|
|
185
|
-
@model.register_symbol([*@names_nesting, arg.
|
|
185
|
+
@model.register_symbol([*@names_nesting, arg.unescaped].join("::")),
|
|
186
186
|
owner: current_namespace,
|
|
187
187
|
location: node_location(arg),
|
|
188
188
|
visibility: current_visibility,
|
|
@@ -196,7 +196,7 @@ module Spoom
|
|
|
196
196
|
next unless arg.is_a?(Prism::SymbolNode)
|
|
197
197
|
|
|
198
198
|
AttrWriter.new(
|
|
199
|
-
@model.register_symbol([*@names_nesting, arg.
|
|
199
|
+
@model.register_symbol([*@names_nesting, arg.unescaped].join("::")),
|
|
200
200
|
owner: current_namespace,
|
|
201
201
|
location: node_location(arg),
|
|
202
202
|
visibility: current_visibility,
|
data/lib/spoom/rbs.rb
CHANGED
|
@@ -69,7 +69,20 @@ module Spoom
|
|
|
69
69
|
end
|
|
70
70
|
|
|
71
71
|
class Annotation < Comment; end
|
|
72
|
-
|
|
72
|
+
|
|
73
|
+
class Signature < Comment
|
|
74
|
+
# Locations of the `#|` continuation comment lines that make up a multiline signature,
|
|
75
|
+
# in addition to the `#:` line tracked by `location`.
|
|
76
|
+
#: Array[Prism::Location]
|
|
77
|
+
attr_reader :continuation_locations
|
|
78
|
+
|
|
79
|
+
#: (String, Prism::Location, ?continuation_locations: Array[Prism::Location]) -> void
|
|
80
|
+
def initialize(string, location, continuation_locations: [])
|
|
81
|
+
super(string, location)
|
|
82
|
+
@continuation_locations = continuation_locations
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
73
86
|
class TypeAlias < Comment; end
|
|
74
87
|
|
|
75
88
|
module ExtractRBSComments
|
|
@@ -99,13 +112,18 @@ module Spoom
|
|
|
99
112
|
elsif string.start_with?("#: ")
|
|
100
113
|
string = string.delete_prefix("#:").strip
|
|
101
114
|
location = comment.location
|
|
115
|
+
continuation_locations = [] #: Array[Prism::Location]
|
|
102
116
|
|
|
103
117
|
continuation_comments.reverse_each do |continuation_comment|
|
|
104
118
|
string = "#{string}#{continuation_comment.slice.delete_prefix("#|")}"
|
|
105
119
|
location = location.join(continuation_comment.location)
|
|
120
|
+
continuation_locations << continuation_comment.location
|
|
106
121
|
end
|
|
107
122
|
continuation_comments.clear
|
|
108
|
-
|
|
123
|
+
|
|
124
|
+
next if string.start_with?("type ")
|
|
125
|
+
|
|
126
|
+
res.signatures.prepend(Signature.new(string, location, continuation_locations:))
|
|
109
127
|
elsif string.start_with?("#|")
|
|
110
128
|
continuation_comments << comment
|
|
111
129
|
end
|
|
@@ -32,7 +32,7 @@ module Spoom
|
|
|
32
32
|
# On the other hand, the metrics file is a snapshot of the metrics at type checking time and knows about
|
|
33
33
|
# is calls are typed, how many assertions are done, etc.
|
|
34
34
|
class CodeMetricsVisitor < Spoom::Visitor
|
|
35
|
-
include RBS::ExtractRBSComments
|
|
35
|
+
include Spoom::RBS::ExtractRBSComments
|
|
36
36
|
|
|
37
37
|
#: (Spoom::Counters) -> void
|
|
38
38
|
def initialize(counters)
|
|
@@ -147,7 +147,7 @@ module Spoom
|
|
|
147
147
|
|
|
148
148
|
private
|
|
149
149
|
|
|
150
|
-
#: (
|
|
150
|
+
#: (PrismTypes::anyScopeNode) { -> void } -> void
|
|
151
151
|
def visit_scope(node, &block)
|
|
152
152
|
key = node_key(node)
|
|
153
153
|
@counters.increment(key)
|
|
@@ -216,7 +216,7 @@ module Spoom
|
|
|
216
216
|
sigs
|
|
217
217
|
end
|
|
218
218
|
|
|
219
|
-
#: (
|
|
219
|
+
#: (PrismTypes::anyScopeNode) -> String
|
|
220
220
|
def node_key(node)
|
|
221
221
|
case node
|
|
222
222
|
when Prism::ClassNode
|