spoom 1.5.4 → 1.6.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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/lib/spoom/backtrace_filter/minitest.rb +2 -3
  3. data/lib/spoom/cli/deadcode.rb +1 -2
  4. data/lib/spoom/cli/helper.rb +36 -28
  5. data/lib/spoom/cli/srb/assertions.rb +48 -0
  6. data/lib/spoom/cli/srb/bump.rb +1 -2
  7. data/lib/spoom/cli/srb/sigs.rb +133 -18
  8. data/lib/spoom/cli/srb.rb +8 -4
  9. data/lib/spoom/cli.rb +1 -2
  10. data/lib/spoom/colors.rb +2 -6
  11. data/lib/spoom/context/bundle.rb +8 -9
  12. data/lib/spoom/context/exec.rb +2 -5
  13. data/lib/spoom/context/file_system.rb +12 -19
  14. data/lib/spoom/context/git.rb +14 -19
  15. data/lib/spoom/context/sorbet.rb +13 -26
  16. data/lib/spoom/context.rb +3 -7
  17. data/lib/spoom/coverage/d3/base.rb +6 -8
  18. data/lib/spoom/coverage/d3/circle_map.rb +6 -16
  19. data/lib/spoom/coverage/d3/pie.rb +14 -19
  20. data/lib/spoom/coverage/d3/timeline.rb +46 -47
  21. data/lib/spoom/coverage/d3.rb +2 -4
  22. data/lib/spoom/coverage/report.rb +38 -76
  23. data/lib/spoom/coverage/snapshot.rb +7 -13
  24. data/lib/spoom/coverage.rb +3 -5
  25. data/lib/spoom/deadcode/definition.rb +12 -14
  26. data/lib/spoom/deadcode/erb.rb +10 -8
  27. data/lib/spoom/deadcode/index.rb +19 -23
  28. data/lib/spoom/deadcode/indexer.rb +5 -6
  29. data/lib/spoom/deadcode/plugins/action_mailer.rb +2 -3
  30. data/lib/spoom/deadcode/plugins/action_mailer_preview.rb +2 -3
  31. data/lib/spoom/deadcode/plugins/actionpack.rb +4 -4
  32. data/lib/spoom/deadcode/plugins/active_model.rb +2 -3
  33. data/lib/spoom/deadcode/plugins/active_record.rb +2 -3
  34. data/lib/spoom/deadcode/plugins/active_support.rb +2 -1
  35. data/lib/spoom/deadcode/plugins/base.rb +29 -32
  36. data/lib/spoom/deadcode/plugins/graphql.rb +2 -3
  37. data/lib/spoom/deadcode/plugins/minitest.rb +4 -4
  38. data/lib/spoom/deadcode/plugins/namespaces.rb +5 -5
  39. data/lib/spoom/deadcode/plugins/rails.rb +5 -5
  40. data/lib/spoom/deadcode/plugins/rubocop.rb +4 -4
  41. data/lib/spoom/deadcode/plugins/ruby.rb +3 -4
  42. data/lib/spoom/deadcode/plugins/sorbet.rb +12 -6
  43. data/lib/spoom/deadcode/plugins/thor.rb +2 -3
  44. data/lib/spoom/deadcode/plugins.rb +2 -4
  45. data/lib/spoom/deadcode/remover.rb +37 -59
  46. data/lib/spoom/deadcode/send.rb +2 -8
  47. data/lib/spoom/file_collector.rb +10 -18
  48. data/lib/spoom/file_tree.rb +31 -46
  49. data/lib/spoom/location.rb +9 -20
  50. data/lib/spoom/model/builder.rb +60 -15
  51. data/lib/spoom/model/model.rb +65 -68
  52. data/lib/spoom/model/namespace_visitor.rb +3 -2
  53. data/lib/spoom/model/reference.rb +4 -8
  54. data/lib/spoom/model/references_visitor.rb +49 -29
  55. data/lib/spoom/parse.rb +17 -3
  56. data/lib/spoom/poset.rb +17 -19
  57. data/lib/spoom/printer.rb +10 -13
  58. data/lib/spoom/sorbet/assertions.rb +278 -0
  59. data/lib/spoom/sorbet/config.rb +8 -12
  60. data/lib/spoom/sorbet/errors.rb +16 -31
  61. data/lib/spoom/sorbet/lsp/base.rb +9 -15
  62. data/lib/spoom/sorbet/lsp/errors.rb +8 -16
  63. data/lib/spoom/sorbet/lsp/structures.rb +36 -59
  64. data/lib/spoom/sorbet/lsp.rb +15 -17
  65. data/lib/spoom/sorbet/metrics.rb +3 -5
  66. data/lib/spoom/sorbet/sigils.rb +7 -11
  67. data/lib/spoom/sorbet/sigs.rb +118 -25
  68. data/lib/spoom/sorbet.rb +3 -9
  69. data/lib/spoom/timeline.rb +4 -6
  70. data/lib/spoom/version.rb +1 -1
  71. data/lib/spoom/visitor.rb +298 -151
  72. data/lib/spoom.rb +0 -2
  73. data/rbi/spoom.rbi +3963 -0
  74. metadata +6 -3
data/lib/spoom/poset.rb CHANGED
@@ -7,14 +7,13 @@ module Spoom
7
7
  # The partial order relation is a binary relation that is reflexive, antisymmetric, and transitive.
8
8
  # It can be used to represent a hierarchy of classes or modules, the dependencies between gems, etc.
9
9
  class Poset
10
- extend T::Sig
11
10
  extend T::Generic
12
11
 
13
12
  class Error < Spoom::Error; end
14
13
 
15
14
  E = type_member { { upper: Object } }
16
15
 
17
- sig { void }
16
+ #: -> void
18
17
  def initialize
19
18
  @elements = T.let({}, T::Hash[E, Element[E]])
20
19
  end
@@ -22,7 +21,7 @@ module Spoom
22
21
  # Get the POSet element for a given value
23
22
  #
24
23
  # Raises if the element is not found
25
- sig { params(value: E).returns(Element[E]) }
24
+ #: (E value) -> Element[E]
26
25
  def [](value)
27
26
  element = @elements[value]
28
27
  raise Error, "POSet::Element not found for #{value}" unless element
@@ -31,7 +30,7 @@ module Spoom
31
30
  end
32
31
 
33
32
  # Add an element to the POSet
34
- sig { params(value: E).returns(Element[E]) }
33
+ #: (E value) -> Element[E]
35
34
  def add_element(value)
36
35
  element = @elements[value]
37
36
  return element if element
@@ -40,7 +39,7 @@ module Spoom
40
39
  end
41
40
 
42
41
  # Is the given value a element in the POSet?
43
- sig { params(value: E).returns(T::Boolean) }
42
+ #: (E value) -> bool
44
43
  def element?(value)
45
44
  @elements.key?(value)
46
45
  end
@@ -50,7 +49,7 @@ module Spoom
50
49
  # Transitive edges (transitive closure) are automatically computed.
51
50
  # Adds the elements if they don't exist.
52
51
  # If the direct edge already exists, nothing is done.
53
- sig { params(from: E, to: E).void }
52
+ #: (E from, E to) -> void
54
53
  def add_direct_edge(from, to)
55
54
  from_element = add_element(from)
56
55
  to_element = add_element(to)
@@ -88,7 +87,7 @@ module Spoom
88
87
  end
89
88
 
90
89
  # Is there an edge (direct or indirect) from `from` to `to`?
91
- sig { params(from: E, to: E).returns(T::Boolean) }
90
+ #: (E from, E to) -> bool
92
91
  def edge?(from, to)
93
92
  from_element = @elements[from]
94
93
  return false unless from_element
@@ -97,13 +96,13 @@ module Spoom
97
96
  end
98
97
 
99
98
  # Is there a direct edge from `from` to `to`?
100
- sig { params(from: E, to: E).returns(T::Boolean) }
99
+ #: (E from, E to) -> bool
101
100
  def direct_edge?(from, to)
102
101
  self[from].parents.include?(to)
103
102
  end
104
103
 
105
104
  # Show the POSet as a DOT graph using xdot (used for debugging)
106
- sig { params(direct: T::Boolean, transitive: T::Boolean).void }
105
+ #: (?direct: bool, ?transitive: bool) -> void
107
106
  def show_dot(direct: true, transitive: true)
108
107
  Open3.popen3("xdot -") do |stdin, _stdout, _stderr, _thread|
109
108
  stdin.write(to_dot(direct: direct, transitive: transitive))
@@ -112,7 +111,7 @@ module Spoom
112
111
  end
113
112
 
114
113
  # Return the POSet as a DOT graph
115
- sig { params(direct: T::Boolean, transitive: T::Boolean).returns(String) }
114
+ #: (?direct: bool, ?transitive: bool) -> String
116
115
  def to_dot(direct: true, transitive: true)
117
116
  dot = +"digraph {\n"
118
117
  dot << " rankdir=BT;\n"
@@ -134,21 +133,20 @@ module Spoom
134
133
 
135
134
  # An element in a POSet
136
135
  class Element
137
- extend T::Sig
138
136
  extend T::Generic
139
137
  include Comparable
140
138
 
141
139
  E = type_member { { upper: Object } }
142
140
 
143
141
  # The value held by this element
144
- sig { returns(E) }
142
+ #: E
145
143
  attr_reader :value
146
144
 
147
145
  # Edges (direct and indirect) from this element to other elements in the same POSet
148
- sig { returns(T::Set[Element[E]]) }
146
+ #: Set[Element[E]]
149
147
  attr_reader :dtos, :tos, :dfroms, :froms
150
148
 
151
- sig { params(value: E).void }
149
+ #: (E value) -> void
152
150
  def initialize(value)
153
151
  @value = value
154
152
  @dtos = T.let(Set.new, T::Set[Element[E]])
@@ -157,7 +155,7 @@ module Spoom
157
155
  @froms = T.let(Set.new, T::Set[Element[E]])
158
156
  end
159
157
 
160
- sig { params(other: T.untyped).returns(T.nilable(Integer)) }
158
+ #: (untyped other) -> Integer?
161
159
  def <=>(other)
162
160
  return unless other.is_a?(Element)
163
161
  return 0 if self == other
@@ -170,25 +168,25 @@ module Spoom
170
168
  end
171
169
 
172
170
  # Direct parents of this element
173
- sig { returns(T::Array[E]) }
171
+ #: -> Array[E]
174
172
  def parents
175
173
  @dtos.map(&:value)
176
174
  end
177
175
 
178
176
  # Direct and indirect ancestors of this element
179
- sig { returns(T::Array[E]) }
177
+ #: -> Array[E]
180
178
  def ancestors
181
179
  @tos.map(&:value)
182
180
  end
183
181
 
184
182
  # Direct children of this element
185
- sig { returns(T::Array[E]) }
183
+ #: -> Array[E]
186
184
  def children
187
185
  @dfroms.map(&:value)
188
186
  end
189
187
 
190
188
  # Direct and indirect descendants of this element
191
- sig { returns(T::Array[E]) }
189
+ #: -> Array[E]
192
190
  def descendants
193
191
  @froms.map(&:value)
194
192
  end
data/lib/spoom/printer.rb CHANGED
@@ -5,15 +5,12 @@ require "stringio"
5
5
 
6
6
  module Spoom
7
7
  class Printer
8
- extend T::Sig
9
- extend T::Helpers
10
-
11
8
  include Colorize
12
9
 
13
- sig { returns(T.any(IO, StringIO)) }
10
+ #: (IO | StringIO)
14
11
  attr_accessor :out
15
12
 
16
- sig { params(out: T.any(IO, StringIO), colors: T::Boolean, indent_level: Integer).void }
13
+ #: (?out: (IO | StringIO), ?colors: bool, ?indent_level: Integer) -> void
17
14
  def initialize(out: $stdout, colors: true, indent_level: 0)
18
15
  @out = out
19
16
  @colors = colors
@@ -21,19 +18,19 @@ module Spoom
21
18
  end
22
19
 
23
20
  # Increase indent level
24
- sig { void }
21
+ #: -> void
25
22
  def indent
26
23
  @indent_level += 2
27
24
  end
28
25
 
29
26
  # Decrease indent level
30
- sig { void }
27
+ #: -> void
31
28
  def dedent
32
29
  @indent_level -= 2
33
30
  end
34
31
 
35
32
  # Print `string` into `out`
36
- sig { params(string: T.nilable(String)).void }
33
+ #: (String? string) -> void
37
34
  def print(string)
38
35
  return unless string
39
36
 
@@ -43,7 +40,7 @@ module Spoom
43
40
  # Print `string` colored with `color` into `out`
44
41
  #
45
42
  # Does not use colors unless `@colors`.
46
- sig { params(string: T.nilable(String), color: Color).void }
43
+ #: (String? string, *Color color) -> void
47
44
  def print_colored(string, *color)
48
45
  return unless string
49
46
 
@@ -52,13 +49,13 @@ module Spoom
52
49
  end
53
50
 
54
51
  # Print a new line into `out`
55
- sig { void }
52
+ #: -> void
56
53
  def printn
57
54
  print("\n")
58
55
  end
59
56
 
60
57
  # Print `string` with indent and newline
61
- sig { params(string: T.nilable(String)).void }
58
+ #: (String? string) -> void
62
59
  def printl(string)
63
60
  return unless string
64
61
 
@@ -68,13 +65,13 @@ module Spoom
68
65
  end
69
66
 
70
67
  # Print an indent space into `out`
71
- sig { void }
68
+ #: -> void
72
69
  def printt
73
70
  print(" " * @indent_level)
74
71
  end
75
72
 
76
73
  # Colorize `string` with color if `@colors`
77
- sig { params(string: String, color: Spoom::Color).returns(String) }
74
+ #: (String string, *Spoom::Color color) -> String
78
75
  def colorize(string, *color)
79
76
  return string unless @colors
80
77
 
@@ -0,0 +1,278 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "rbi"
5
+
6
+ module Spoom
7
+ module Sorbet
8
+ class Assertions
9
+ class << self
10
+ #: (String, file: String) -> String
11
+ def rbi_to_rbs(ruby_contents, file:)
12
+ old_encoding = ruby_contents.encoding
13
+ ruby_contents = ruby_contents.encode("UTF-8") unless old_encoding == "UTF-8"
14
+ ruby_bytes = ruby_contents.bytes
15
+
16
+ assigns = collect_assigns(ruby_contents, file: file)
17
+
18
+ assigns.reverse.each do |assign|
19
+ # Adjust the end offset to locate the end of the line:
20
+ #
21
+ # So this:
22
+ #
23
+ # (a = T.let(nil, T.nilable(String)))
24
+ #
25
+ # properly becomes:
26
+ #
27
+ # (a = nil) #: String?
28
+ #
29
+ # This is important to avoid translating the `nil` as `nil` instead of `nil #: String?`
30
+ end_offset = assign.node.location.end_offset
31
+ end_offset += 1 while (ruby_bytes[end_offset] != "\n".ord) && (end_offset < ruby_bytes.size)
32
+ T.unsafe(ruby_bytes).insert(end_offset, *" #: #{assign.rbs_type}".bytes)
33
+
34
+ # Rewrite the value
35
+ start_offset = assign.operator_loc.end_offset
36
+ end_offset = assign.node.value.location.start_offset + assign.node.value.location.length
37
+ ruby_bytes[start_offset...end_offset] = " #{dedent_value(assign)}".bytes
38
+ end
39
+
40
+ ruby_bytes.pack("C*").force_encoding(old_encoding)
41
+ end
42
+
43
+ private
44
+
45
+ #: (String, file: String) -> Array[AssignNode]
46
+ def collect_assigns(ruby_contents, file:)
47
+ node = Spoom.parse_ruby(ruby_contents, file: file)
48
+ visitor = Locator.new
49
+ visitor.visit(node)
50
+ visitor.assigns
51
+ end
52
+
53
+ #: (AssignNode) -> String
54
+ def dedent_value(assign)
55
+ if assign.value.location.start_line == assign.node.location.start_line
56
+ # The value is on the same line as the assign, so we can just return the slice as is:
57
+ # ```rb
58
+ # a = T.let(nil, T.nilable(String))
59
+ # ```
60
+ # becomes
61
+ # ```rb
62
+ # a = nil #: String?
63
+ # ```
64
+ return assign.value.slice
65
+ end
66
+
67
+ # The value is on a different line, so we need to dedent it:
68
+ # ```rb
69
+ # a = T.let(
70
+ # [
71
+ # 1, 2, 3,
72
+ # ],
73
+ # T::Array[Integer],
74
+ # )
75
+ # ```
76
+ # becomes
77
+ # ```rb
78
+ # a = [
79
+ # 1, 2, 3,
80
+ # ] #: Array[Integer]
81
+ # ```
82
+ indent = assign.value.location.start_line - assign.node.location.start_line
83
+ lines = assign.value.slice.lines
84
+ if lines.size > 1
85
+ lines[1..]&.each_with_index do |line, i|
86
+ lines[i + 1] = line.delete_prefix(" " * indent)
87
+ end
88
+ end
89
+ lines.join
90
+ end
91
+ end
92
+
93
+ AssignType = T.type_alias do
94
+ T.any(
95
+ Prism::ClassVariableAndWriteNode,
96
+ Prism::ClassVariableOrWriteNode,
97
+ Prism::ClassVariableOperatorWriteNode,
98
+ Prism::ClassVariableWriteNode,
99
+ Prism::ConstantAndWriteNode,
100
+ Prism::ConstantOrWriteNode,
101
+ Prism::ConstantOperatorWriteNode,
102
+ Prism::ConstantWriteNode,
103
+ Prism::ConstantPathAndWriteNode,
104
+ Prism::ConstantPathOrWriteNode,
105
+ Prism::ConstantPathOperatorWriteNode,
106
+ Prism::ConstantPathWriteNode,
107
+ Prism::GlobalVariableAndWriteNode,
108
+ Prism::GlobalVariableOrWriteNode,
109
+ Prism::GlobalVariableOperatorWriteNode,
110
+ Prism::GlobalVariableWriteNode,
111
+ Prism::InstanceVariableAndWriteNode,
112
+ Prism::InstanceVariableOperatorWriteNode,
113
+ Prism::InstanceVariableOrWriteNode,
114
+ Prism::InstanceVariableWriteNode,
115
+ Prism::LocalVariableAndWriteNode,
116
+ Prism::LocalVariableOperatorWriteNode,
117
+ Prism::LocalVariableOrWriteNode,
118
+ Prism::LocalVariableWriteNode,
119
+ )
120
+ end
121
+
122
+ class AssignNode
123
+ #: AssignType
124
+ attr_reader :node
125
+
126
+ #: Prism::Location
127
+ attr_reader :operator_loc
128
+
129
+ #: Prism::Node
130
+ attr_reader :value, :type
131
+
132
+ #: (AssignType, Prism::Location, Prism::Node, Prism::Node) -> void
133
+ def initialize(node, operator_loc, value, type)
134
+ @node = node
135
+ @operator_loc = operator_loc
136
+ @value = value
137
+ @type = type
138
+ end
139
+
140
+ #: -> String
141
+ def rbs_type
142
+ RBI::Type.parse_node(type).rbs_string
143
+ end
144
+ end
145
+
146
+ class Locator < Spoom::Visitor
147
+ ANNOTATION_METHODS = T.let([:let], T::Array[Symbol])
148
+
149
+ #: Array[AssignNode]
150
+ attr_reader :assigns
151
+
152
+ #: -> void
153
+ def initialize
154
+ super
155
+ @assigns = T.let([], T::Array[AssignNode])
156
+ end
157
+
158
+ #: (AssignType) -> void
159
+ def visit_assign(node)
160
+ call = node.value
161
+ return unless call.is_a?(Prism::CallNode) && t_annotation?(call)
162
+
163
+ # We do not support translating heredocs yet because the `#: ` would need to be added to the first line
164
+ # and it will requires us to adapt the annotation detection in Sorbet. But Sorbet desugars them into bare
165
+ # strings making them impossible to detect.
166
+ value = T.must(call.arguments&.arguments&.first)
167
+ return if contains_heredoc?(value)
168
+
169
+ operator_loc = case node
170
+ when Prism::ClassVariableOperatorWriteNode,
171
+ Prism::ConstantOperatorWriteNode,
172
+ Prism::ConstantPathOperatorWriteNode,
173
+ Prism::GlobalVariableOperatorWriteNode,
174
+ Prism::InstanceVariableOperatorWriteNode,
175
+ Prism::LocalVariableOperatorWriteNode
176
+ node.binary_operator_loc
177
+ else
178
+ node.operator_loc
179
+ end
180
+
181
+ @assigns << AssignNode.new(
182
+ node,
183
+ operator_loc,
184
+ value,
185
+ T.must(call.arguments&.arguments&.last),
186
+ )
187
+ end
188
+
189
+ alias_method(:visit_class_variable_and_write_node, :visit_assign)
190
+ alias_method(:visit_class_variable_operator_write_node, :visit_assign)
191
+ alias_method(:visit_class_variable_or_write_node, :visit_assign)
192
+ alias_method(:visit_class_variable_write_node, :visit_assign)
193
+
194
+ alias_method(:visit_constant_and_write_node, :visit_assign)
195
+ alias_method(:visit_constant_operator_write_node, :visit_assign)
196
+ alias_method(:visit_constant_or_write_node, :visit_assign)
197
+ alias_method(:visit_constant_write_node, :visit_assign)
198
+
199
+ alias_method(:visit_constant_path_and_write_node, :visit_assign)
200
+ alias_method(:visit_constant_path_operator_write_node, :visit_assign)
201
+ alias_method(:visit_constant_path_or_write_node, :visit_assign)
202
+ alias_method(:visit_constant_path_write_node, :visit_assign)
203
+
204
+ alias_method(:visit_global_variable_and_write_node, :visit_assign)
205
+ alias_method(:visit_global_variable_operator_write_node, :visit_assign)
206
+ alias_method(:visit_global_variable_or_write_node, :visit_assign)
207
+ alias_method(:visit_global_variable_write_node, :visit_assign)
208
+
209
+ alias_method(:visit_instance_variable_and_write_node, :visit_assign)
210
+ alias_method(:visit_instance_variable_operator_write_node, :visit_assign)
211
+ alias_method(:visit_instance_variable_or_write_node, :visit_assign)
212
+ alias_method(:visit_instance_variable_write_node, :visit_assign)
213
+
214
+ alias_method(:visit_local_variable_and_write_node, :visit_assign)
215
+ alias_method(:visit_local_variable_operator_write_node, :visit_assign)
216
+ alias_method(:visit_local_variable_or_write_node, :visit_assign)
217
+ alias_method(:visit_local_variable_write_node, :visit_assign)
218
+
219
+ alias_method(:visit_multi_write_node, :visit_assign)
220
+
221
+ # Is this node a `T` or `::T` constant?
222
+ #: (Prism::Node?) -> bool
223
+ def t?(node)
224
+ case node
225
+ when Prism::ConstantReadNode
226
+ node.name == :T
227
+ when Prism::ConstantPathNode
228
+ node.parent.nil? && node.name == :T
229
+ else
230
+ false
231
+ end
232
+ end
233
+
234
+ # Is this node a `T.let` or `T.cast`?
235
+ #: (Prism::CallNode) -> bool
236
+ def t_annotation?(node)
237
+ return false unless t?(node.receiver)
238
+ return false unless ANNOTATION_METHODS.include?(node.name)
239
+ return false unless node.arguments&.arguments&.size == 2
240
+
241
+ true
242
+ end
243
+
244
+ #: (Prism::Node) -> bool
245
+ def contains_heredoc?(node)
246
+ visitor = HeredocVisitor.new
247
+ visitor.visit(node)
248
+ visitor.contains_heredoc
249
+ end
250
+
251
+ class HeredocVisitor < Spoom::Visitor
252
+ #: bool
253
+ attr_reader :contains_heredoc
254
+
255
+ #: -> void
256
+ def initialize
257
+ @contains_heredoc = T.let(false, T::Boolean)
258
+
259
+ super
260
+ end
261
+
262
+ # @override
263
+ #: (Prism::Node?) -> void
264
+ def visit(node)
265
+ return if node.nil?
266
+
267
+ case node
268
+ when Prism::StringNode, Prism::InterpolatedStringNode
269
+ return @contains_heredoc = !!node.opening_loc&.slice&.match?(/<<~|<<-/)
270
+ end
271
+
272
+ super
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end
278
+ end
@@ -24,17 +24,15 @@ module Spoom
24
24
  # puts config.ignore # "c"
25
25
  # ```
26
26
  class Config
27
- extend T::Sig
28
-
29
27
  DEFAULT_ALLOWED_EXTENSIONS = T.let([".rb", ".rbi"].freeze, T::Array[String])
30
28
 
31
- sig { returns(T::Array[String]) }
29
+ #: Array[String]
32
30
  attr_accessor :paths, :ignore, :allowed_extensions
33
31
 
34
- sig { returns(T::Boolean) }
32
+ #: bool
35
33
  attr_accessor :no_stdlib
36
34
 
37
- sig { void }
35
+ #: -> void
38
36
  def initialize
39
37
  @paths = T.let([], T::Array[String])
40
38
  @ignore = T.let([], T::Array[String])
@@ -42,7 +40,7 @@ module Spoom
42
40
  @no_stdlib = T.let(false, T::Boolean)
43
41
  end
44
42
 
45
- sig { returns(Config) }
43
+ #: -> Config
46
44
  def copy
47
45
  new_config = Sorbet::Config.new
48
46
  new_config.paths.concat(@paths)
@@ -64,7 +62,7 @@ module Spoom
64
62
  #
65
63
  # puts config.options_string # "/foo /bar --ignore /baz --allowed-extension .rb"
66
64
  # ~~~
67
- sig { returns(String) }
65
+ #: -> String
68
66
  def options_string
69
67
  opts = []
70
68
  opts.concat(paths.map { |p| "'#{p}'" })
@@ -75,14 +73,12 @@ module Spoom
75
73
  end
76
74
 
77
75
  class << self
78
- extend T::Sig
79
-
80
- sig { params(sorbet_config_path: String).returns(Spoom::Sorbet::Config) }
76
+ #: (String sorbet_config_path) -> Spoom::Sorbet::Config
81
77
  def parse_file(sorbet_config_path)
82
78
  parse_string(File.read(sorbet_config_path))
83
79
  end
84
80
 
85
- sig { params(sorbet_config: String).returns(Spoom::Sorbet::Config) }
81
+ #: (String sorbet_config) -> Spoom::Sorbet::Config
86
82
  def parse_string(sorbet_config)
87
83
  config = Config.new
88
84
  state = T.let(nil, T.nilable(Symbol))
@@ -143,7 +139,7 @@ module Spoom
143
139
 
144
140
  private
145
141
 
146
- sig { params(line: String).returns(String) }
142
+ #: (String line) -> String
147
143
  def parse_option(line)
148
144
  T.must(line.split("=").last).strip
149
145
  end