rubocop-sorbet 0.10.4 → 0.10.5

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: 79bcbc5d5d2a20065dae6bd132ce89a78f6d55d913253f582d57b26dbc90e3fe
4
- data.tar.gz: fc68fab55a25f15a2689564842768d87b2268ed99e8bdcf503284e47dd82deb2
3
+ metadata.gz: '086645a85b87da08958ff04471b05e238fbe14de8bc6108610bd20ca638f5ef7'
4
+ data.tar.gz: 24b4add23624df28a1019f528b0f24f5f87bf379ecca807d922b0220c65b09d0
5
5
  SHA512:
6
- metadata.gz: 6b67f844675e5ca0c5f229cf8dd55e091f12c068dcaeb0cf289eded28a92367d32ead0f315ca4776bd0acd04e743c3c0420c345ac1ac6a7041f84dc61cc1fa78
7
- data.tar.gz: ca77c4f1a29809ce05da20319309c760dff38fcf4adae09b60e4a588a24ca234a32c6a48226df8baa9eaa169b388953e4f65cb079a47d22f062924efdf3b9c3b
6
+ metadata.gz: 3fd16f332f9df0642787e8a60baa934611298a1487aabb3b84c05795764ce839dfb37001413f751bcd6a494c068220038941aaf972d36ebe0edf05315daa1fe2
7
+ data.tar.gz: 2d015fbdc34e4d4fcb8ba3ea60db28ec9a5af2e88efc78f258152b5cf0c1cb748c15f464b6bba2c596f24141bbb98b2a82d57afb49a3ed27a18901d11f68253b
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rubocop-sorbet (0.10.4)
4
+ rubocop-sorbet (0.10.5)
5
5
  lint_roller
6
6
  rubocop (>= 1.75.2)
7
7
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.10.3
1
+ 0.10.5
data/config/default.yml CHANGED
@@ -82,7 +82,7 @@ Sorbet/EnforceSigilOrder:
82
82
  Sorbet/EnforceSignatures:
83
83
  Description: 'Ensures all methods have a valid signature.'
84
84
  Enabled: false
85
- AllowRBS: false
85
+ Style: sig
86
86
  VersionAdded: 0.3.4
87
87
 
88
88
  Sorbet/EnforceSingleSigil:
@@ -174,32 +174,32 @@ Sorbet/ForbidTStruct:
174
174
  Sorbet/ForbidTAbsurd:
175
175
  Description: 'Forbid usage of T.absurd.'
176
176
  Enabled: false
177
- VersionAdded: <<next>>
177
+ VersionAdded: 0.10.4
178
178
 
179
179
  Sorbet/ForbidTBind:
180
180
  Description: 'Forbid usage of T.bind.'
181
181
  Enabled: false
182
- VersionAdded: <<next>>
182
+ VersionAdded: 0.10.4
183
183
 
184
184
  Sorbet/ForbidTCast:
185
185
  Description: 'Forbid usage of T.cast.'
186
186
  Enabled: false
187
- VersionAdded: <<next>>
187
+ VersionAdded: 0.10.4
188
188
 
189
189
  Sorbet/ForbidTLet:
190
190
  Description: 'Forbid usage of T.let.'
191
191
  Enabled: false
192
- VersionAdded: <<next>>
192
+ VersionAdded: 0.10.4
193
193
 
194
194
  Sorbet/ForbidTMust:
195
195
  Description: 'Forbid usage of T.must.'
196
196
  Enabled: false
197
- VersionAdded: <<next>>
197
+ VersionAdded: 0.10.4
198
198
 
199
199
  Sorbet/ForbidTTypeAlias:
200
200
  Description: 'Forbid usage of T.type_alias.'
201
201
  Enabled: false
202
- VersionAdded: <<next>>
202
+ VersionAdded: 0.10.4
203
203
 
204
204
  Sorbet/ForbidTUnsafe:
205
205
  Description: 'Forbid usage of T.unsafe.'
@@ -6,3 +6,8 @@
6
6
  removed:
7
7
  Sorbet/OneAncestorPerLine:
8
8
  reason: '`require_ancestor` now takes a block instead of arguments'
9
+
10
+ changed_parameters:
11
+ - cops: 'Sorbet/EnforceSignatures'
12
+ parameters: 'AllowRBS'
13
+ alternative: 'Style'
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "stringio"
4
-
5
3
  module RuboCop
6
4
  module Cop
7
5
  module Sorbet
@@ -24,16 +22,12 @@ module RuboCop
24
22
  #
25
23
  # * `ParameterTypePlaceholder`: placeholders used for parameter types (default: 'T.untyped')
26
24
  # * `ReturnTypePlaceholder`: placeholders used for return types (default: 'T.untyped')
25
+ # * `Style`: signature style to enforce - 'sig' for sig blocks, 'rbs' for RBS comments, 'both' to allow either (default: 'sig')
27
26
  class EnforceSignatures < ::RuboCop::Cop::Base
28
27
  extend AutoCorrector
29
28
  include SignatureHelp
30
29
 
31
- RBS_COMMENT_REGEX = /^#:.*$/
32
-
33
- def initialize(config = nil, options = nil)
34
- super(config, options)
35
- @last_sig_for_scope = {}
36
- end
30
+ VALID_STYLES = ["sig", "rbs", "both"].freeze
37
31
 
38
32
  # @!method accessor?(node)
39
33
  def_node_matcher(:accessor?, <<-PATTERN)
@@ -53,7 +47,13 @@ module RuboCop
53
47
  end
54
48
 
55
49
  def on_signature(node)
56
- @last_sig_for_scope[scope(node)] = node
50
+ sig_checker.on_signature(node, scope(node))
51
+ end
52
+
53
+ def on_new_investigation
54
+ super
55
+ @sig_checker = nil
56
+ @rbs_checker = nil
57
57
  end
58
58
 
59
59
  def scope(node)
@@ -67,48 +67,102 @@ module RuboCop
67
67
 
68
68
  def check_node(node)
69
69
  scope = self.scope(node)
70
- unless @last_sig_for_scope[scope] || has_rbs_comment?(node)
71
- add_offense(
72
- node,
73
- message: "Each method is required to have a signature.",
74
- ) do |corrector|
75
- autocorrect(corrector, node)
70
+ sig_node = sig_checker.signature_node(scope)
71
+ rbs_node = rbs_checker.signature_node(node)
72
+
73
+ case signature_style
74
+ when "rbs"
75
+ # RBS style - only RBS signatures allowed
76
+ if sig_node
77
+ add_offense(sig_node, message: "Use RBS signature comments rather than sig blocks.")
78
+ return
79
+ end
80
+
81
+ unless rbs_node
82
+ add_offense(node, message: "Each method is required to have an RBS signature.") do |corrector|
83
+ autocorrect_with_signature_type(corrector, node, "rbs")
84
+ end
85
+ end
86
+ when "both"
87
+ # Both styles allowed - require at least one
88
+ unless sig_node || rbs_node
89
+ add_offense(node, message: "Each method is required to have a signature.") do |corrector|
90
+ autocorrect_with_signature_type(corrector, node, "sig")
91
+ end
92
+ end
93
+ else # "sig" (default)
94
+ # Sig style - only sig signatures allowed
95
+ unless sig_node
96
+ add_offense(node, message: "Each method is required to have a sig block signature.") do |corrector|
97
+ autocorrect_with_signature_type(corrector, node, "sig")
98
+ end
76
99
  end
77
100
  end
78
- @last_sig_for_scope[scope] = nil
101
+ ensure
102
+ sig_checker.clear_signature(scope)
79
103
  end
80
104
 
81
- def has_rbs_comment?(node)
82
- return false unless cop_config["AllowRBS"] == true
83
-
84
- node = node.parent while RuboCop::AST::SendNode === node.parent
85
- return false if (comments = preceeding_comments(node)).empty?
105
+ def sig_checker
106
+ @sig_checker ||= SigSignatureChecker.new(processed_source)
107
+ end
86
108
 
87
- last_comment = comments.last
88
- return false if last_comment.loc.line + 1 < node.loc.line
109
+ def rbs_checker
110
+ @rbs_checker ||= RBSSignatureChecker.new(processed_source)
111
+ end
89
112
 
90
- comments.any? { |comment| RBS_COMMENT_REGEX.match?(comment.text) }
113
+ def autocorrect_with_signature_type(corrector, node, type)
114
+ suggest = create_signature_suggestion(node, type)
115
+ populate_signature_suggestion(suggest, node)
116
+ corrector.insert_before(node, suggest.to_autocorrect)
91
117
  end
92
118
 
93
- def preceeding_comments(node)
94
- processed_source.ast_with_comments[node].select { |comment| comment.loc.line < node.loc.line }
119
+ def create_signature_suggestion(node, type)
120
+ case type
121
+ when "rbs"
122
+ RBSSuggestion.new(node.loc.column)
123
+ else # "sig"
124
+ SigSuggestion.new(node.loc.column, param_type_placeholder, return_type_placeholder)
125
+ end
95
126
  end
96
127
 
97
- def autocorrect(corrector, node)
98
- suggest = SigSuggestion.new(node.loc.column, param_type_placeholder, return_type_placeholder)
128
+ def populate_signature_suggestion(suggest, node)
129
+ if node.any_def_type?
130
+ populate_method_definition_suggestion(suggest, node)
131
+ elsif accessor?(node)
132
+ populate_accessor_suggestion(suggest, node)
133
+ end
134
+ end
99
135
 
100
- if node.is_a?(RuboCop::AST::DefNode) # def something
101
- node.arguments.each do |arg|
136
+ def populate_method_definition_suggestion(suggest, node)
137
+ node.arguments.each do |arg|
138
+ if arg.blockarg_type? && suggest.respond_to?(:has_block=)
139
+ suggest.has_block = true
140
+ else
102
141
  suggest.params << arg.children.first
103
142
  end
104
- elsif accessor?(node) # attr reader, writer, accessor
105
- method = node.children[1]
106
- symbol = node.children[2]
107
- suggest.params << symbol.value if symbol && (method == :attr_writer || method == :attr_accessor)
108
- suggest.returns = "void" if method == :attr_writer
109
143
  end
144
+ end
110
145
 
111
- corrector.insert_before(node, suggest.to_autocorrect)
146
+ def populate_accessor_suggestion(suggest, node)
147
+ method = node.children[1]
148
+ symbol = node.children[2]
149
+
150
+ add_accessor_parameter_if_needed(suggest, symbol, method)
151
+ set_void_return_for_writer(suggest, method)
152
+ end
153
+
154
+ def add_accessor_parameter_if_needed(suggest, symbol, method)
155
+ return unless symbol && writer_or_accessor?(method)
156
+
157
+ suggest.params << symbol.value
158
+ end
159
+
160
+ def set_void_return_for_writer(suggest, method)
161
+ suggest.returns = "void" if method == :attr_writer
162
+ end
163
+
164
+ def writer_or_accessor?(method)
165
+ method == :attr_writer || method == :attr_accessor
112
166
  end
113
167
 
114
168
  def param_type_placeholder
@@ -119,6 +173,80 @@ module RuboCop
119
173
  cop_config["ReturnTypePlaceholder"] || "T.untyped"
120
174
  end
121
175
 
176
+ def allow_rbs?
177
+ cop_config["AllowRBS"] == true
178
+ end
179
+
180
+ def signature_style
181
+ config_value = cop_config["Style"]
182
+ if config_value
183
+ unless VALID_STYLES.include?(config_value)
184
+ raise ArgumentError, "Invalid Style option: '#{config_value}'. Valid options are: #{VALID_STYLES.join(", ")}"
185
+ end
186
+
187
+ return config_value
188
+ end
189
+
190
+ return "both" if allow_rbs?
191
+
192
+ "sig"
193
+ end
194
+
195
+ class SignatureChecker
196
+ def initialize(processed_source)
197
+ @processed_source = processed_source
198
+ end
199
+
200
+ protected
201
+
202
+ attr_reader :processed_source
203
+
204
+ def preceding_comments(node)
205
+ processed_source.ast_with_comments[node].select { |comment| comment.loc.line < node.loc.line }
206
+ end
207
+ end
208
+
209
+ class RBSSignatureChecker < SignatureChecker
210
+ RBS_COMMENT_REGEX = /^#\s*:.*$/
211
+
212
+ def signature_node(node)
213
+ node = find_non_send_ancestor(node)
214
+ comments = preceding_comments(node)
215
+ return if comments.empty?
216
+
217
+ last_comment = comments.last
218
+ return if last_comment.loc.line + 1 < node.loc.line
219
+
220
+ comments.find { |comment| RBS_COMMENT_REGEX.match?(comment.text) }
221
+ end
222
+
223
+ private
224
+
225
+ def find_non_send_ancestor(node)
226
+ node = node.parent while node.parent&.send_type?
227
+ node
228
+ end
229
+ end
230
+
231
+ class SigSignatureChecker < SignatureChecker
232
+ def initialize(processed_source)
233
+ super(processed_source)
234
+ @last_sig_for_scope = {}
235
+ end
236
+
237
+ def signature_node(scope)
238
+ @last_sig_for_scope[scope]
239
+ end
240
+
241
+ def on_signature(node, scope)
242
+ @last_sig_for_scope[scope] = node
243
+ end
244
+
245
+ def clear_signature(scope)
246
+ @last_sig_for_scope[scope] = nil
247
+ end
248
+ end
249
+
122
250
  class SigSuggestion
123
251
  attr_accessor :params, :returns
124
252
 
@@ -131,34 +259,59 @@ module RuboCop
131
259
  end
132
260
 
133
261
  def to_autocorrect
134
- out = StringIO.new
135
- out << "sig { "
136
- out << generate_params
137
- out << generate_return
138
- out << " }\n"
139
- out << " " * @indent # preserve indent for the next line
140
- out.string
262
+ "sig { #{generate_params}#{generate_return} }\n#{" " * @indent}"
141
263
  end
142
264
 
143
265
  private
144
266
 
145
267
  def generate_params
146
- return if @params.empty?
268
+ return "" if @params.empty?
147
269
 
148
- out = StringIO.new
149
- out << "params("
150
- out << @params.map do |param|
151
- "#{param}: #{@param_placeholder}"
152
- end.join(", ")
153
- out << ")."
154
- out.string
270
+ param_list = @params.map { |param| "#{param}: #{@param_placeholder}" }.join(", ")
271
+ "params(#{param_list})."
155
272
  end
156
273
 
157
274
  def generate_return
158
- return "returns(#{@return_placeholder})" if @returns.nil?
159
- return @returns if @returns == "void"
275
+ if @returns.nil?
276
+ "returns(#{@return_placeholder})"
277
+ elsif @returns == "void"
278
+ "void"
279
+ else
280
+ "returns(#{@returns})"
281
+ end
282
+ end
283
+ end
284
+
285
+ class RBSSuggestion
286
+ attr_accessor :params, :returns, :has_block
287
+
288
+ def initialize(indent)
289
+ @params = []
290
+ @returns = nil
291
+ @has_block = false
292
+ @indent = indent
293
+ end
294
+
295
+ def to_autocorrect
296
+ "#: #{generate_signature}\n#{" " * @indent}"
297
+ end
298
+
299
+ private
300
+
301
+ def generate_signature
302
+ param_types = @params.map { "untyped" }.join(", ")
303
+ return_type = @returns || "untyped"
304
+
305
+ signature = if @params.empty?
306
+ "()"
307
+ else
308
+ "(#{param_types})"
309
+ end
310
+
311
+ signature += " { (?) -> untyped }" if @has_block
312
+ signature += " -> #{return_type}"
160
313
 
161
- "returns(#{@returns})"
314
+ signature
162
315
  end
163
316
  end
164
317
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module Sorbet
5
- VERSION = "0.10.4"
5
+ VERSION = "0.10.5"
6
6
  end
7
7
  end
@@ -331,12 +331,13 @@ You can configure the placeholders used by changing the following options:
331
331
 
332
332
  * `ParameterTypePlaceholder`: placeholders used for parameter types (default: 'T.untyped')
333
333
  * `ReturnTypePlaceholder`: placeholders used for return types (default: 'T.untyped')
334
+ * `Style`: signature style to enforce - 'sig' for sig blocks, 'rbs' for RBS comments, 'both' to allow either (default: 'sig')
334
335
 
335
336
  ### Configurable attributes
336
337
 
337
338
  Name | Default value | Configurable values
338
339
  --- | --- | ---
339
- AllowRBS | `false` | Boolean
340
+ Style | `sig` | String
340
341
 
341
342
  ## Sorbet/EnforceSingleSigil
342
343
 
@@ -636,7 +637,7 @@ Exclude | `db/migrate/*.rb` | Array
636
637
 
637
638
  Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
638
639
  --- | --- | --- | --- | ---
639
- Disabled | Yes | No | <<next>> | -
640
+ Disabled | Yes | No | 0.10.4 | -
640
641
 
641
642
  Disallows using `T.absurd` anywhere.
642
643
 
@@ -654,7 +655,7 @@ x #: absurd
654
655
 
655
656
  Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
656
657
  --- | --- | --- | --- | ---
657
- Disabled | Yes | No | <<next>> | -
658
+ Disabled | Yes | No | 0.10.4 | -
658
659
 
659
660
  Disallows using `T.bind` anywhere.
660
661
 
@@ -672,7 +673,7 @@ T.bind(self, Integer)
672
673
 
673
674
  Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
674
675
  --- | --- | --- | --- | ---
675
- Disabled | Yes | No | <<next>> | -
676
+ Disabled | Yes | No | 0.10.4 | -
676
677
 
677
678
  Disallows using `T.cast` anywhere.
678
679
 
@@ -717,7 +718,7 @@ end
717
718
 
718
719
  Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
719
720
  --- | --- | --- | --- | ---
720
- Disabled | Yes | No | <<next>> | -
721
+ Disabled | Yes | No | 0.10.4 | -
721
722
 
722
723
  Disallows using `T.let` anywhere.
723
724
 
@@ -735,7 +736,7 @@ foo #: Integer
735
736
 
736
737
  Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
737
738
  --- | --- | --- | --- | ---
738
- Disabled | Yes | No | <<next>> | -
739
+ Disabled | Yes | No | 0.10.4 | -
739
740
 
740
741
  Disallows using `T.must` anywhere.
741
742
 
@@ -792,7 +793,7 @@ end
792
793
 
793
794
  Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
794
795
  --- | --- | --- | --- | ---
795
- Disabled | Yes | No | <<next>> | -
796
+ Disabled | Yes | No | 0.10.4 | -
796
797
 
797
798
  Disallows using `T.type_alias` anywhere.
798
799
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-sorbet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.4
4
+ version: 0.10.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ufuk Kayserilioglu