rubocop-sorbet 0.10.2 → 0.10.3
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 +5 -5
- data/Rakefile +4 -4
- data/VERSION +1 -1
- data/config/default.yml +7 -0
- data/lib/rubocop/cop/sorbet/block_method_definition.rb +127 -6
- data/lib/rubocop/cop/sorbet/signatures/capitalized_type_parameters.rb +70 -0
- data/lib/rubocop/cop/sorbet_cops.rb +1 -0
- data/lib/rubocop/sorbet/version.rb +1 -1
- data/manual/cops.md +1 -0
- data/manual/cops_sorbet.md +39 -1
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9ebae9ccd4d5ac64cdcede02bbad10ffe8bd68b31a99d2e3c2914de447dae24
|
4
|
+
data.tar.gz: bbf3c32b940424369bde19fabcd6ec5cab543c265ce810d288a947dbae1d7ea7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5cd6ef4d4435d430c26ed33d4267de84fb962fff6d564083428fb3153e7c0486cebe4791080dd22d6312acc8faa8b4154642c5c2eee69d80f5cdde927bacf520
|
7
|
+
data.tar.gz: 1eedabd82415a5c626eb33e297772ffb057d322b298e6504c63437918154e624e2c39c1667a99b703a0d642a9c840f01805a2878349165e16d72720d4656db4b
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rubocop-sorbet (0.10.
|
4
|
+
rubocop-sorbet (0.10.3)
|
5
5
|
lint_roller
|
6
6
|
rubocop (>= 1.75.2)
|
7
7
|
|
@@ -39,7 +39,7 @@ GEM
|
|
39
39
|
regexp_parser (2.10.0)
|
40
40
|
reline (0.6.0)
|
41
41
|
io-console (~> 0.5)
|
42
|
-
rubocop (1.
|
42
|
+
rubocop (1.76.0)
|
43
43
|
json (~> 2.3)
|
44
44
|
language_server-protocol (~> 3.17.0.2)
|
45
45
|
lint_roller (~> 1.1.0)
|
@@ -47,17 +47,17 @@ GEM
|
|
47
47
|
parser (>= 3.3.0.2)
|
48
48
|
rainbow (>= 2.2.2, < 4.0)
|
49
49
|
regexp_parser (>= 2.9.3, < 3.0)
|
50
|
-
rubocop-ast (>= 1.
|
50
|
+
rubocop-ast (>= 1.45.0, < 2.0)
|
51
51
|
ruby-progressbar (~> 1.7)
|
52
52
|
unicode-display_width (>= 2.4.0, < 4.0)
|
53
|
-
rubocop-ast (1.
|
53
|
+
rubocop-ast (1.45.1)
|
54
54
|
parser (>= 3.3.7.2)
|
55
55
|
prism (~> 1.4)
|
56
56
|
rubocop-minitest (0.38.1)
|
57
57
|
lint_roller (~> 1.1)
|
58
58
|
rubocop (>= 1.75.0, < 2.0)
|
59
59
|
rubocop-ast (>= 1.38.0, < 2.0)
|
60
|
-
rubocop-shopify (2.17.
|
60
|
+
rubocop-shopify (2.17.1)
|
61
61
|
rubocop (~> 1.62)
|
62
62
|
ruby-progressbar (1.13.0)
|
63
63
|
ruby2_keywords (0.0.5)
|
data/Rakefile
CHANGED
@@ -60,10 +60,10 @@ module Releaser
|
|
60
60
|
|
61
61
|
sh "git add lib/rubocop/sorbet/version.rb config/default.yml Gemfile.lock VERSION manual"
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
63
|
+
sh "git commit -m 'Release v#{version}'"
|
64
|
+
sh "git push origin main"
|
65
|
+
sh "git tag -a v#{version} -m 'Release v#{version}'"
|
66
|
+
sh "git push origin v#{version}"
|
67
67
|
end
|
68
68
|
|
69
69
|
private
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.10.
|
1
|
+
0.10.3
|
data/config/default.yml
CHANGED
@@ -23,6 +23,7 @@ Sorbet/BlockMethodDefinition:
|
|
23
23
|
Safe: true
|
24
24
|
SafeAutoCorrect: false
|
25
25
|
VersionAdded: '0.10.1'
|
26
|
+
VersionChanged: '0.10.3'
|
26
27
|
|
27
28
|
Sorbet/BuggyObsoleteStrictMemoization:
|
28
29
|
Description: >-
|
@@ -47,6 +48,12 @@ Sorbet/CallbackConditionalsBinding:
|
|
47
48
|
Safe: false
|
48
49
|
VersionAdded: 0.7.0
|
49
50
|
|
51
|
+
Sorbet/CapitalizedTypeParameters:
|
52
|
+
Description: 'Ensures that type parameters are capitalized.'
|
53
|
+
Enabled: true
|
54
|
+
SafeAutoCorrect: false
|
55
|
+
VersionAdded: '0.10.3'
|
56
|
+
|
50
57
|
Sorbet/CheckedTrueInSignature:
|
51
58
|
Description: 'Disallows the usage of `checked(true)` in signatures.'
|
52
59
|
Enabled: true
|
@@ -10,7 +10,8 @@ module RuboCop
|
|
10
10
|
#
|
11
11
|
# The one exception is for `Class.new` blocks, as long as the result is
|
12
12
|
# assigned to a constant (i.e. as long as it is not an anonymous class).
|
13
|
-
|
13
|
+
# Another exception is for ActiveSupport::Concern `class_methods` blocks.
|
14
|
+
#
|
14
15
|
# @example
|
15
16
|
# # bad
|
16
17
|
# yielding_method do
|
@@ -40,17 +41,50 @@ module RuboCop
|
|
40
41
|
# end
|
41
42
|
# end
|
42
43
|
#
|
44
|
+
# # good
|
45
|
+
# module SomeConcern
|
46
|
+
# extend ActiveSupport::Concern
|
47
|
+
#
|
48
|
+
# class_methods do
|
49
|
+
# def good(args)
|
50
|
+
# # ...
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
#
|
43
55
|
class BlockMethodDefinition < Base
|
44
56
|
include RuboCop::Cop::Alignment
|
45
57
|
extend AutoCorrector
|
46
58
|
|
47
59
|
MSG = "Do not define methods in blocks (use `define_method` as a workaround)."
|
48
60
|
|
61
|
+
# @!method activesupport_concern_class_methods_block?(node)
|
62
|
+
def_node_matcher :activesupport_concern_class_methods_block?, <<~PATTERN
|
63
|
+
(block
|
64
|
+
(send nil? :class_methods)
|
65
|
+
_
|
66
|
+
_
|
67
|
+
)
|
68
|
+
PATTERN
|
69
|
+
|
70
|
+
# @!method module_extends_activesupport_concern?(node)
|
71
|
+
def_node_matcher :module_extends_activesupport_concern?, <<~PATTERN
|
72
|
+
(module _
|
73
|
+
(begin
|
74
|
+
<(send nil? :extend (const (const {nil? cbase} :ActiveSupport) :Concern)) ...>
|
75
|
+
...
|
76
|
+
)
|
77
|
+
)
|
78
|
+
PATTERN
|
79
|
+
|
49
80
|
def on_block(node)
|
50
81
|
if (parent = node.parent)
|
51
82
|
return if parent.casgn_type?
|
52
83
|
end
|
53
84
|
|
85
|
+
# Check if this is a class_methods block inside an ActiveSupport::Concern
|
86
|
+
return if in_activesupport_concern_class_methods_block?(node)
|
87
|
+
|
54
88
|
node.each_descendant(:any_def) do |def_node|
|
55
89
|
add_offense(def_node) do |corrector|
|
56
90
|
autocorrect_method_in_block(corrector, def_node)
|
@@ -61,21 +95,108 @@ module RuboCop
|
|
61
95
|
|
62
96
|
private
|
63
97
|
|
98
|
+
def in_activesupport_concern_class_methods_block?(node)
|
99
|
+
return false unless activesupport_concern_class_methods_block?(node)
|
100
|
+
|
101
|
+
immediate_module = node.each_ancestor(:module).first
|
102
|
+
|
103
|
+
module_extends_activesupport_concern?(immediate_module)
|
104
|
+
end
|
105
|
+
|
64
106
|
def autocorrect_method_in_block(corrector, node)
|
65
107
|
indent = offset(node)
|
66
108
|
|
67
109
|
method_name = node.method_name
|
68
|
-
args = node
|
69
|
-
body = node.body&.source&.prepend("\n#{indent} ")
|
110
|
+
args = transform_args_to_block_args(node)
|
70
111
|
|
112
|
+
# Build the method signature replacement
|
71
113
|
if node.def_type?
|
72
|
-
|
114
|
+
signature_replacement = "define_method(:#{method_name}) do#{args}"
|
73
115
|
elsif node.defs_type?
|
74
116
|
receiver = node.receiver.source
|
75
|
-
|
117
|
+
signature_replacement = "#{receiver}.define_singleton_method(:#{method_name}) do#{args}"
|
118
|
+
end
|
119
|
+
|
120
|
+
if node.body
|
121
|
+
end_pos = node.body.source_range.begin_pos
|
122
|
+
indentation = "\n#{indent} "
|
123
|
+
else
|
124
|
+
end_pos, indentation = handle_method_without_body(node, indent)
|
76
125
|
end
|
77
126
|
|
78
|
-
|
127
|
+
signature_range = node.source_range.with(end_pos: end_pos)
|
128
|
+
|
129
|
+
corrector.replace(signature_range, signature_replacement + indentation)
|
130
|
+
end
|
131
|
+
|
132
|
+
def transform_args_to_block_args(node)
|
133
|
+
args = node.arguments
|
134
|
+
|
135
|
+
if args.empty?
|
136
|
+
""
|
137
|
+
else
|
138
|
+
args_string = args.map(&:source).join(", ")
|
139
|
+
" |#{args_string}|"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def handle_method_without_body(node, indent)
|
144
|
+
if single_line_method?(node)
|
145
|
+
handle_single_line_method(node, indent)
|
146
|
+
else
|
147
|
+
handle_multiline_method_without_body(node)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def single_line_method?(node)
|
152
|
+
!node.source.include?("\n")
|
153
|
+
end
|
154
|
+
|
155
|
+
def handle_single_line_method(node, indent)
|
156
|
+
end_pos = node.source_range.end_pos
|
157
|
+
indentation = "\n#{indent}end"
|
158
|
+
[end_pos, indentation]
|
159
|
+
end
|
160
|
+
|
161
|
+
def handle_multiline_method_without_body(node)
|
162
|
+
end_pos = find_method_signature_end_position(node)
|
163
|
+
indentation = ""
|
164
|
+
[end_pos, indentation]
|
165
|
+
end
|
166
|
+
|
167
|
+
def find_method_signature_end_position(node)
|
168
|
+
if node.arguments.any?
|
169
|
+
find_end_position_with_arguments(node)
|
170
|
+
else
|
171
|
+
find_end_position_without_arguments(node)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def find_end_position_with_arguments(node)
|
176
|
+
last_arg = node.last_argument
|
177
|
+
end_pos = last_arg.source_range.end_pos
|
178
|
+
|
179
|
+
adjust_for_closing_parenthesis(end_pos)
|
180
|
+
end
|
181
|
+
|
182
|
+
def find_end_position_without_arguments(node)
|
183
|
+
node.loc.name.end_pos
|
184
|
+
end
|
185
|
+
|
186
|
+
def adjust_for_closing_parenthesis(end_pos)
|
187
|
+
source_after_last_arg = processed_source.buffer.source[end_pos..-1]
|
188
|
+
|
189
|
+
match = closing_parenthesis_follows(source_after_last_arg)
|
190
|
+
|
191
|
+
if match
|
192
|
+
end_pos + match.end(0)
|
193
|
+
else
|
194
|
+
end_pos
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def closing_parenthesis_follows(source)
|
199
|
+
source.match(/\A\s*\)/)
|
79
200
|
end
|
80
201
|
end
|
81
202
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Sorbet
|
6
|
+
# Ensure type parameters used in generic methods are always capitalized.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# # bad
|
11
|
+
# sig { type_parameters(:x).params(a: T.type_parameter(:x)).void }
|
12
|
+
# def foo(a); end
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# sig { type_parameters(:X).params(a: T.type_parameter(:X)).void }
|
16
|
+
# def foo(a: 1); end
|
17
|
+
class CapitalizedTypeParameters < ::RuboCop::Cop::Base
|
18
|
+
extend AutoCorrector
|
19
|
+
include SignatureHelp
|
20
|
+
|
21
|
+
MSG = "Type parameters must be capitalized."
|
22
|
+
|
23
|
+
RESTRICT_ON_SEND = [:type_parameter].freeze
|
24
|
+
|
25
|
+
# @!method type_parameters?(node)
|
26
|
+
def_node_matcher(:type_parameters?, <<-PATTERN)
|
27
|
+
(send nil? :type_parameters ...)
|
28
|
+
PATTERN
|
29
|
+
|
30
|
+
# @!method t_type_parameter?(node)
|
31
|
+
def_node_matcher(:t_type_parameter?, <<-PATTERN)
|
32
|
+
(send (const {nil? | cbase} :T) :type_parameter ...)
|
33
|
+
PATTERN
|
34
|
+
|
35
|
+
def on_signature(node)
|
36
|
+
send = node.children[2]
|
37
|
+
|
38
|
+
while send&.send_type?
|
39
|
+
if type_parameters?(send)
|
40
|
+
check_type_parameters_case(send)
|
41
|
+
end
|
42
|
+
|
43
|
+
send = send.children[0]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_send(node)
|
48
|
+
check_type_parameters_case(node) if t_type_parameter?(node)
|
49
|
+
end
|
50
|
+
|
51
|
+
def on_csend(node)
|
52
|
+
check_type_parameters_case(node) if t_type_parameter?(node)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def check_type_parameters_case(node)
|
58
|
+
node.children[2..].each do |arg|
|
59
|
+
next unless arg.is_a?(RuboCop::AST::SymbolNode)
|
60
|
+
next if arg.value =~ /^[A-Z]/
|
61
|
+
|
62
|
+
add_offense(arg) do |corrector|
|
63
|
+
corrector.replace(arg, arg.value.capitalize.to_sym.inspect)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -33,6 +33,7 @@ require_relative "sorbet/rbi_versioning/gem_version_annotation_helper"
|
|
33
33
|
require_relative "sorbet/rbi_versioning/valid_gem_version_annotations"
|
34
34
|
|
35
35
|
require_relative "sorbet/signatures/allow_incompatible_override"
|
36
|
+
require_relative "sorbet/signatures/capitalized_type_parameters"
|
36
37
|
require_relative "sorbet/signatures/checked_true_in_signature"
|
37
38
|
require_relative "sorbet/signatures/empty_line_after_sig"
|
38
39
|
require_relative "sorbet/signatures/enforce_signatures"
|
data/manual/cops.md
CHANGED
@@ -10,6 +10,7 @@ In the following section you find all available cops:
|
|
10
10
|
* [Sorbet/BlockMethodDefinition](cops_sorbet.md#sorbetblockmethoddefinition)
|
11
11
|
* [Sorbet/BuggyObsoleteStrictMemoization](cops_sorbet.md#sorbetbuggyobsoletestrictmemoization)
|
12
12
|
* [Sorbet/CallbackConditionalsBinding](cops_sorbet.md#sorbetcallbackconditionalsbinding)
|
13
|
+
* [Sorbet/CapitalizedTypeParameters](cops_sorbet.md#sorbetcapitalizedtypeparameters)
|
13
14
|
* [Sorbet/CheckedTrueInSignature](cops_sorbet.md#sorbetcheckedtrueinsignature)
|
14
15
|
* [Sorbet/ConstantsFromStrings](cops_sorbet.md#sorbetconstantsfromstrings)
|
15
16
|
* [Sorbet/EmptyLineAfterSig](cops_sorbet.md#sorbetemptylineaftersig)
|
data/manual/cops_sorbet.md
CHANGED
@@ -45,9 +45,16 @@ FooOrBar = T.type_alias { T.any(Foo, Bar) }
|
|
45
45
|
|
46
46
|
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
47
47
|
--- | --- | --- | --- | ---
|
48
|
-
Enabled | Yes | Yes (Unsafe) | 0.10.1 |
|
48
|
+
Enabled | Yes | Yes (Unsafe) | 0.10.1 | 0.10.3
|
49
49
|
|
50
|
+
Disallow defining methods in blocks, to prevent running into issues
|
51
|
+
caused by https://github.com/sorbet/sorbet/issues/3609.
|
50
52
|
|
53
|
+
As a workaround, use `define_method` instead.
|
54
|
+
|
55
|
+
The one exception is for `Class.new` blocks, as long as the result is
|
56
|
+
assigned to a constant (i.e. as long as it is not an anonymous class).
|
57
|
+
Another exception is for ActiveSupport::Concern `class_methods` blocks.
|
51
58
|
|
52
59
|
### Examples
|
53
60
|
|
@@ -79,6 +86,17 @@ MyClass = Class.new do
|
|
79
86
|
# ...
|
80
87
|
end
|
81
88
|
end
|
89
|
+
|
90
|
+
# good
|
91
|
+
module SomeConcern
|
92
|
+
extend ActiveSupport::Concern
|
93
|
+
|
94
|
+
class_methods do
|
95
|
+
def good(args)
|
96
|
+
# ...
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
82
100
|
```
|
83
101
|
|
84
102
|
## Sorbet/BuggyObsoleteStrictMemoization
|
@@ -157,6 +175,26 @@ class Post < ApplicationRecord
|
|
157
175
|
end
|
158
176
|
```
|
159
177
|
|
178
|
+
## Sorbet/CapitalizedTypeParameters
|
179
|
+
|
180
|
+
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
181
|
+
--- | --- | --- | --- | ---
|
182
|
+
Enabled | Yes | Yes (Unsafe) | 0.10.3 | -
|
183
|
+
|
184
|
+
Ensure type parameters used in generic methods are always capitalized.
|
185
|
+
|
186
|
+
### Examples
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
# bad
|
190
|
+
sig { type_parameters(:x).params(a: T.type_parameter(:x)).void }
|
191
|
+
def foo(a); end
|
192
|
+
|
193
|
+
# good
|
194
|
+
sig { type_parameters(:X).params(a: T.type_parameter(:X)).void }
|
195
|
+
def foo(a: 1); end
|
196
|
+
```
|
197
|
+
|
160
198
|
## Sorbet/CheckedTrueInSignature
|
161
199
|
|
162
200
|
Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
|
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
|
+
version: 0.10.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ufuk Kayserilioglu
|
@@ -114,6 +114,7 @@ files:
|
|
114
114
|
- lib/rubocop/cop/sorbet/sigils/true_sigil.rb
|
115
115
|
- lib/rubocop/cop/sorbet/sigils/valid_sigil.rb
|
116
116
|
- lib/rubocop/cop/sorbet/signatures/allow_incompatible_override.rb
|
117
|
+
- lib/rubocop/cop/sorbet/signatures/capitalized_type_parameters.rb
|
117
118
|
- lib/rubocop/cop/sorbet/signatures/checked_true_in_signature.rb
|
118
119
|
- lib/rubocop/cop/sorbet/signatures/empty_line_after_sig.rb
|
119
120
|
- lib/rubocop/cop/sorbet/signatures/enforce_signatures.rb
|