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 +4 -4
- data/Gemfile.lock +1 -1
- data/VERSION +1 -1
- data/config/default.yml +7 -7
- data/config/obsoletion.yml +5 -0
- data/lib/rubocop/cop/sorbet/signatures/enforce_signatures.rb +207 -54
- data/lib/rubocop/sorbet/version.rb +1 -1
- data/manual/cops_sorbet.md +8 -7
- 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: '086645a85b87da08958ff04471b05e238fbe14de8bc6108610bd20ca638f5ef7'
|
4
|
+
data.tar.gz: 24b4add23624df28a1019f528b0f24f5f87bf379ecca807d922b0220c65b09d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3fd16f332f9df0642787e8a60baa934611298a1487aabb3b84c05795764ce839dfb37001413f751bcd6a494c068220038941aaf972d36ebe0edf05315daa1fe2
|
7
|
+
data.tar.gz: 2d015fbdc34e4d4fcb8ba3ea60db28ec9a5af2e88efc78f258152b5cf0c1cb748c15f464b6bba2c596f24141bbb98b2a82d57afb49a3ed27a18901d11f68253b
|
data/Gemfile.lock
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.10.
|
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
|
-
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
202
|
+
VersionAdded: 0.10.4
|
203
203
|
|
204
204
|
Sorbet/ForbidTUnsafe:
|
205
205
|
Description: 'Forbid usage of T.unsafe.'
|
data/config/obsoletion.yml
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
101
|
+
ensure
|
102
|
+
sig_checker.clear_signature(scope)
|
79
103
|
end
|
80
104
|
|
81
|
-
def
|
82
|
-
|
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
|
-
|
88
|
-
|
109
|
+
def rbs_checker
|
110
|
+
@rbs_checker ||= RBSSignatureChecker.new(processed_source)
|
111
|
+
end
|
89
112
|
|
90
|
-
|
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
|
94
|
-
|
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
|
98
|
-
|
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
|
-
|
101
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
149
|
-
|
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
|
-
|
159
|
-
|
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
|
-
|
314
|
+
signature
|
162
315
|
end
|
163
316
|
end
|
164
317
|
end
|
data/manual/cops_sorbet.md
CHANGED
@@ -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
|
-
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
796
|
+
Disabled | Yes | No | 0.10.4 | -
|
796
797
|
|
797
798
|
Disallows using `T.type_alias` anywhere.
|
798
799
|
|