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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2110b75c9f1cfaf75e50c901f26af66d149f2a6353a22ee2154062f24f808472
4
- data.tar.gz: 1f8daac5e4e94b7b135ed0b2d7dafb701dcf338302f337982ced3e6a32aa65fa
3
+ metadata.gz: ae2652efb4a3831e59c02c6a8cc5915f316078ab403afb51932592e83cf924ca
4
+ data.tar.gz: ae568069ab7100286b0260ef741616d24c228a8d35a7739b6a9c771ff48d6a6e
5
5
  SHA512:
6
- metadata.gz: bb98e214157092a61b830ce8e5836b1bc9c304d902b2886e10ab52c753febd6c7233142a509b461b26ee9c1c1150bd8346b02370c031bda68a6c555b2bc1c128
7
- data.tar.gz: 6b3b0ec9bbc739161a4c642084aa1edcae82cf9293e20e3997656cfa2108eff1c32369c5d4225c6ebdd0716cb030eaec7c749cfa623273cdb59dce848f5ab039
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
- key = key.slice.delete_suffix(":")
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.slice.delete_prefix(":"), send.location) if 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
- key = key.slice.delete_suffix(":")
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.slice.delete_prefix(":"), send.location) if 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
- key = key.slice.delete_suffix(":")
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&.is_a?(Prism::SymbolNode)
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
- key = key.slice.delete_suffix(":")
97
- @index.reference_method("#{key}=", send.location)
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.slice.delete_suffix(":")
108
- @index.reference_method("#{key}=", send.location)
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
- # method_name = send.args.first.slice.delete_prefix(":")
273
- # @index.reference_method(method_name, send.node, send.loc)
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? && send.name == "field"
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
- key = key.slice.delete_suffix(":")
37
- next unless key == "resolver_method"
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(value.slice.delete_prefix(":"), send.location)
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
- name = send.args[1]&.slice
32
- return unless name
31
+ arg = send.args[1]
32
+ return unless arg.is_a?(Prism::SymbolNode)
33
33
 
34
- @index.reference_method(name.delete_prefix(":"), send.location)
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
@@ -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.slice.delete_prefix(":")].join("::")),
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.slice.delete_prefix(":")].join("::")),
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.slice.delete_prefix(":")].join("::")),
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
- class Signature < Comment; end
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
- res.signatures.prepend(Signature.new(string, location))
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
- #: (Prism::ClassNode | Prism::ModuleNode | Prism::SingletonClassNode) { -> void } -> void
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
- #: (Prism::ClassNode | Prism::ModuleNode | Prism::SingletonClassNode) -> String
219
+ #: (PrismTypes::anyScopeNode) -> String
220
220
  def node_key(node)
221
221
  case node
222
222
  when Prism::ClassNode