rubocop-sorbet 0.7.7 → 0.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/Gemfile +1 -1
- data/Gemfile.lock +3 -7
- data/README.md +0 -7
- data/config/default.yml +17 -3
- data/lib/rubocop/cop/sorbet/forbid_t_struct.rb +35 -3
- data/lib/rubocop/cop/sorbet/signatures/signature_build_order.rb +52 -88
- data/lib/rubocop/sorbet/version.rb +1 -1
- data/manual/cops_sorbet.md +11 -6
- data/rubocop-sorbet.gemspec +0 -3
- metadata +2 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ddc9a9caf478aaa31d5b985064aadc77e9e2d199927b7db01eb5823ea2190c28
|
4
|
+
data.tar.gz: d440f179cff018d9ff54d68c01fafa9265d4020ca738f5311fe552b44c7d2074
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b046329f103125db498e57acb613d387b780c6ec00c3d433cdb88e90ed1110091f5c6a1f59f0d3907abc98e1755ef332046b545b8dd54665939d997716ecf057
|
7
|
+
data.tar.gz: 68348b3e0ad4a8a6141526fb6c5406b42e7ea12661256f83258551b2a11c3a6c04ded7843b7398a4903fa9877cbd16c1acb4bf7f3ccce69c600fca5bc51b18bc
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rubocop-sorbet (0.
|
4
|
+
rubocop-sorbet (0.8.0)
|
5
5
|
rubocop (>= 0.90.0)
|
6
6
|
|
7
7
|
GEM
|
@@ -51,10 +51,7 @@ GEM
|
|
51
51
|
rubocop (~> 1.51)
|
52
52
|
ruby-progressbar (1.13.0)
|
53
53
|
unicode-display_width (2.4.2)
|
54
|
-
|
55
|
-
diff-lcs (~> 1.3)
|
56
|
-
parser (>= 3.0.0)
|
57
|
-
yard (0.9.25)
|
54
|
+
yard (0.9.36)
|
58
55
|
|
59
56
|
PLATFORMS
|
60
57
|
ruby
|
@@ -62,10 +59,9 @@ PLATFORMS
|
|
62
59
|
DEPENDENCIES
|
63
60
|
byebug
|
64
61
|
rake (>= 12.3.3)
|
65
|
-
rspec
|
62
|
+
rspec (~> 3.7)
|
66
63
|
rubocop-shopify
|
67
64
|
rubocop-sorbet!
|
68
|
-
unparser (~> 0.6)
|
69
65
|
yard (~> 0.9)
|
70
66
|
|
71
67
|
BUNDLED WITH
|
data/README.md
CHANGED
@@ -15,13 +15,6 @@ or, if you use `Bundler`, add this line your application's `Gemfile`:
|
|
15
15
|
gem 'rubocop-sorbet', require: false
|
16
16
|
```
|
17
17
|
|
18
|
-
Note: in order to use the [Sorbet/SignatureBuildOrder](https://github.com/Shopify/rubocop-sorbet/blob/main/manual/cops_sorbet.md#sorbetsignaturebuildorder) cop autocorrect feature, it is necessary
|
19
|
-
to install `unparser` in addition to `rubocop-sorbet`.
|
20
|
-
|
21
|
-
```ruby
|
22
|
-
gem "unparser", require: false
|
23
|
-
```
|
24
|
-
|
25
18
|
## Usage
|
26
19
|
|
27
20
|
You need to tell RuboCop to load the Sorbet extension. There are three ways to do this:
|
data/config/default.yml
CHANGED
@@ -188,12 +188,12 @@ Sorbet/BuggyObsoleteStrictMemoization:
|
|
188
188
|
Checks for the a mistaken variant of the "obsolete memoization pattern" that used to be required
|
189
189
|
for older Sorbet versions in `#typed: strict` files. The mistaken variant would overwrite the ivar with `nil`
|
190
190
|
on every call, causing the memoized value to be discarded and recomputed on every call.
|
191
|
-
|
191
|
+
|
192
192
|
This cop will correct it to read from the ivar instead of `nil`, which will memoize it correctly.
|
193
|
-
|
193
|
+
|
194
194
|
The result of this correction will be the "obsolete memoization pattern", which can further be corrected by
|
195
195
|
the `Sorbet/ObsoleteStrictMemoization` cop.
|
196
|
-
|
196
|
+
|
197
197
|
See `Sorbet/ObsoleteStrictMemoization` for more details.
|
198
198
|
Enabled: true
|
199
199
|
VersionAdded: '0.7.3'
|
@@ -223,6 +223,20 @@ Sorbet/SignatureBuildOrder:
|
|
223
223
|
then params, then return and finally the modifier
|
224
224
|
such as: `abstract.params(...).returns(...).soft`.'
|
225
225
|
Enabled: true
|
226
|
+
Order:
|
227
|
+
- final
|
228
|
+
- abstract
|
229
|
+
- implementation
|
230
|
+
- override
|
231
|
+
- overridable
|
232
|
+
- type_parameters
|
233
|
+
- params
|
234
|
+
- bind
|
235
|
+
- returns
|
236
|
+
- void
|
237
|
+
- soft
|
238
|
+
- checked
|
239
|
+
- on_failure
|
226
240
|
VersionAdded: 0.3.0
|
227
241
|
|
228
242
|
Sorbet/SingleLineRbiClassModuleDefinitions:
|
@@ -102,7 +102,7 @@ module RuboCop
|
|
102
102
|
end
|
103
103
|
|
104
104
|
class Property
|
105
|
-
attr_reader :node, :kind, :name, :
|
105
|
+
attr_reader :node, :kind, :name, :default, :factory
|
106
106
|
|
107
107
|
def initialize(node, kind, name, type, default:, factory:)
|
108
108
|
@node = node
|
@@ -151,6 +151,11 @@ module RuboCop
|
|
151
151
|
def nilable?
|
152
152
|
type.start_with?("T.nilable(")
|
153
153
|
end
|
154
|
+
|
155
|
+
def type
|
156
|
+
copy = @type.gsub(/[[:space:]]+/, "").strip # Remove newlines and spaces
|
157
|
+
copy.gsub(",", ", ") # Add a space after each comma
|
158
|
+
end
|
154
159
|
end
|
155
160
|
|
156
161
|
# @!method t_struct?(node)
|
@@ -215,8 +220,35 @@ module RuboCop
|
|
215
220
|
sorted_props = props.sort_by { |prop| prop.default || prop.factory || prop.nilable? ? 1 : 0 }
|
216
221
|
|
217
222
|
string = +"\n"
|
218
|
-
|
219
|
-
|
223
|
+
|
224
|
+
line = "#{indent}sig { params(#{sorted_props.map(&:initialize_sig_param).join(", ")}).void }\n"
|
225
|
+
if line.length <= max_line_length
|
226
|
+
string << line
|
227
|
+
else
|
228
|
+
string << "#{indent}sig do\n"
|
229
|
+
string << "#{indent} params(\n"
|
230
|
+
sorted_props.each do |prop|
|
231
|
+
string << "#{indent} #{prop.initialize_sig_param}"
|
232
|
+
string << "," if prop != sorted_props.last
|
233
|
+
string << "\n"
|
234
|
+
end
|
235
|
+
string << "#{indent} ).void\n"
|
236
|
+
string << "#{indent}end\n"
|
237
|
+
end
|
238
|
+
|
239
|
+
line = "#{indent}def initialize(#{sorted_props.map(&:initialize_param).join(", ")})\n"
|
240
|
+
if line.length <= max_line_length
|
241
|
+
string << line
|
242
|
+
else
|
243
|
+
string << "#{indent}def initialize(\n"
|
244
|
+
sorted_props.each do |prop|
|
245
|
+
string << "#{indent} #{prop.initialize_param}"
|
246
|
+
string << "," if prop != sorted_props.last
|
247
|
+
string << "\n"
|
248
|
+
end
|
249
|
+
string << "#{indent})\n"
|
250
|
+
end
|
251
|
+
|
220
252
|
props.each do |prop|
|
221
253
|
string << "#{indent} #{prop.initialize_assign}\n"
|
222
254
|
end
|
@@ -1,20 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
begin
|
4
|
-
require "unparser"
|
5
|
-
rescue LoadError
|
6
|
-
nil
|
7
|
-
end
|
8
|
-
|
9
3
|
module RuboCop
|
10
4
|
module Cop
|
11
5
|
module Sorbet
|
12
|
-
# Checks for the correct order of sig builder methods
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
# - soft, checked, or on_failure
|
6
|
+
# Checks for the correct order of `sig` builder methods.
|
7
|
+
#
|
8
|
+
# Options:
|
9
|
+
#
|
10
|
+
# * `Order`: The order in which to enforce the builder methods are called.
|
18
11
|
#
|
19
12
|
# @example
|
20
13
|
# # bad
|
@@ -28,109 +21,80 @@ module RuboCop
|
|
28
21
|
#
|
29
22
|
# # good
|
30
23
|
# sig { params(x: Integer).returns(Integer) }
|
31
|
-
class SignatureBuildOrder < ::RuboCop::Cop::
|
24
|
+
class SignatureBuildOrder < ::RuboCop::Cop::Base
|
25
|
+
extend AutoCorrector
|
32
26
|
include SignatureHelp
|
33
27
|
|
34
|
-
ORDER =
|
35
|
-
[
|
36
|
-
:abstract,
|
37
|
-
:override,
|
38
|
-
:overridable,
|
39
|
-
:type_parameters,
|
40
|
-
:params,
|
41
|
-
:returns,
|
42
|
-
:void,
|
43
|
-
:soft,
|
44
|
-
:checked,
|
45
|
-
:on_failure,
|
46
|
-
].each_with_index.to_h.freeze
|
47
|
-
|
48
28
|
# @!method root_call(node)
|
49
29
|
def_node_search(:root_call, <<~PATTERN)
|
50
|
-
(send nil?
|
30
|
+
(send nil? #builder? ...)
|
51
31
|
PATTERN
|
52
32
|
|
53
33
|
def on_signature(node)
|
54
|
-
|
55
|
-
return if calls.empty?
|
34
|
+
body = node.body
|
56
35
|
|
57
|
-
|
58
|
-
|
59
|
-
|
36
|
+
actual_calls_and_indexes = call_chain(body).map.with_index do |send_node, actual_index|
|
37
|
+
# The index this method call appears at in the configured Order.
|
38
|
+
expected_index = builder_method_indexes[send_node.method_name]
|
60
39
|
|
61
|
-
|
62
|
-
return if expected_order == calls
|
63
|
-
|
64
|
-
message = "Sig builders must be invoked in the following order: #{expected_order.join(", ")}."
|
65
|
-
|
66
|
-
unless can_autocorrect?
|
67
|
-
message += " For autocorrection, add the `unparser` gem to your project."
|
40
|
+
[send_node, actual_index, expected_index]
|
68
41
|
end
|
69
42
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
)
|
74
|
-
node
|
75
|
-
end
|
43
|
+
# Temporarily extract unknown method calls
|
44
|
+
expected_calls_and_indexes, unknown_calls_and_indexes = actual_calls_and_indexes
|
45
|
+
.partition { |_, _, expected_index| expected_index }
|
76
46
|
|
77
|
-
|
78
|
-
|
47
|
+
# Sort known method calls by expected index
|
48
|
+
expected_calls_and_indexes.sort_by! { |_, _, expected_index| expected_index }
|
79
49
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
.reduce(nil) do |receiver, caller|
|
84
|
-
caller.updated(nil, [receiver] + caller.children.drop(1))
|
85
|
-
end
|
50
|
+
# Re-insert unknown method calls in their positions
|
51
|
+
unknown_calls_and_indexes.each do |entry|
|
52
|
+
_, original_index, _ = entry
|
86
53
|
|
87
|
-
|
88
|
-
node,
|
89
|
-
Unparser.unparse(tree),
|
90
|
-
)
|
54
|
+
expected_calls_and_indexes.insert(original_index, entry)
|
91
55
|
end
|
92
|
-
end
|
93
56
|
|
94
|
-
|
95
|
-
|
96
|
-
|
57
|
+
# Compare expected and actual ordering
|
58
|
+
expected_method_names = expected_calls_and_indexes.map { |send_node, _, _| send_node.method_name }
|
59
|
+
actual_method_names = actual_calls_and_indexes.map { |send_node, _, _| send_node.method_name }
|
60
|
+
return if expected_method_names == actual_method_names
|
61
|
+
|
62
|
+
add_offense(
|
63
|
+
body,
|
64
|
+
message: "Sig builders must be invoked in the following order: #{expected_method_names.join(", ")}.",
|
65
|
+
) { |corrector| corrector.replace(body, expected_source(expected_calls_and_indexes)) }
|
97
66
|
end
|
98
|
-
private_constant :ModernBuilder
|
99
67
|
|
100
68
|
private
|
101
69
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
# Create a new source buffer with the node source
|
110
|
-
buffer = Parser::Source::Buffer.new(processed_source.path, source: node.source)
|
111
|
-
# Re-parse the buffer
|
112
|
-
parser.parse(buffer)
|
113
|
-
end
|
70
|
+
def expected_source(expected_calls_and_indexes)
|
71
|
+
expected_calls_and_indexes.reduce(nil) do |receiver_source, (send_node, _, _)|
|
72
|
+
send_source = if send_node.arguments?
|
73
|
+
"#{send_node.method_name}(#{send_node.arguments.map(&:source).join(", ")})"
|
74
|
+
else
|
75
|
+
send_node.method_name.to_s
|
76
|
+
end
|
114
77
|
|
115
|
-
|
116
|
-
|
78
|
+
receiver_source ? "#{receiver_source}.#{send_source}" : send_source
|
79
|
+
end
|
117
80
|
end
|
118
81
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
call_node = root_call(sig_child_node).first
|
123
|
-
return [] unless call_node
|
82
|
+
# Split foo.bar.baz into [foo, foo.bar, foo.bar.baz]
|
83
|
+
def call_chain(node)
|
84
|
+
chain = []
|
124
85
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
call_node = call_node.parent
|
86
|
+
while node&.send_type?
|
87
|
+
chain << node
|
88
|
+
node = node.receiver
|
129
89
|
end
|
130
90
|
|
131
|
-
|
91
|
+
chain.reverse!
|
92
|
+
|
93
|
+
chain
|
94
|
+
end
|
132
95
|
|
133
|
-
|
96
|
+
def builder_method_indexes
|
97
|
+
@configured_order ||= cop_config.fetch("Order").map(&:to_sym).each_with_index.to_h.freeze
|
134
98
|
end
|
135
99
|
end
|
136
100
|
end
|
data/manual/cops_sorbet.md
CHANGED
@@ -733,12 +733,11 @@ Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChan
|
|
733
733
|
--- | --- | --- | --- | ---
|
734
734
|
Enabled | Yes | Yes | 0.3.0 | -
|
735
735
|
|
736
|
-
Checks for the correct order of sig builder methods
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
- soft, checked, or on_failure
|
736
|
+
Checks for the correct order of `sig` builder methods.
|
737
|
+
|
738
|
+
Options:
|
739
|
+
|
740
|
+
* `Order`: The order in which to enforce the builder methods are called.
|
742
741
|
|
743
742
|
### Examples
|
744
743
|
|
@@ -756,6 +755,12 @@ sig { returns(Integer).params(x: Integer) }
|
|
756
755
|
sig { params(x: Integer).returns(Integer) }
|
757
756
|
```
|
758
757
|
|
758
|
+
### Configurable attributes
|
759
|
+
|
760
|
+
Name | Default value | Configurable values
|
761
|
+
--- | --- | ---
|
762
|
+
Order | `final`, `abstract`, `implementation`, `override`, `overridable`, `type_parameters`, `params`, `bind`, `returns`, `void`, `soft`, `checked`, `on_failure` | Array
|
763
|
+
|
759
764
|
## Sorbet/SingleLineRbiClassModuleDefinitions
|
760
765
|
|
761
766
|
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
data/rubocop-sorbet.gemspec
CHANGED
@@ -25,8 +25,5 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
26
26
|
spec.require_paths = ["lib"]
|
27
27
|
|
28
|
-
spec.add_development_dependency("rspec", "~> 3.7")
|
29
|
-
spec.add_development_dependency("unparser", "~> 0.6")
|
30
|
-
|
31
28
|
spec.add_runtime_dependency("rubocop", ">= 0.90.0")
|
32
29
|
end
|
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.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ufuk Kayserilioglu
|
@@ -11,36 +11,8 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: exe
|
13
13
|
cert_chain: []
|
14
|
-
date: 2024-
|
14
|
+
date: 2024-03-18 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
|
-
- !ruby/object:Gem::Dependency
|
17
|
-
name: rspec
|
18
|
-
requirement: !ruby/object:Gem::Requirement
|
19
|
-
requirements:
|
20
|
-
- - "~>"
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: '3.7'
|
23
|
-
type: :development
|
24
|
-
prerelease: false
|
25
|
-
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
requirements:
|
27
|
-
- - "~>"
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '3.7'
|
30
|
-
- !ruby/object:Gem::Dependency
|
31
|
-
name: unparser
|
32
|
-
requirement: !ruby/object:Gem::Requirement
|
33
|
-
requirements:
|
34
|
-
- - "~>"
|
35
|
-
- !ruby/object:Gem::Version
|
36
|
-
version: '0.6'
|
37
|
-
type: :development
|
38
|
-
prerelease: false
|
39
|
-
version_requirements: !ruby/object:Gem::Requirement
|
40
|
-
requirements:
|
41
|
-
- - "~>"
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
version: '0.6'
|
44
16
|
- !ruby/object:Gem::Dependency
|
45
17
|
name: rubocop
|
46
18
|
requirement: !ruby/object:Gem::Requirement
|