rubocop-sorbet 0.5.1 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/release.yml +23 -0
- data/.github/workflows/ci.yml +26 -0
- data/.gitignore +1 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +25 -41
- data/README.md +22 -0
- data/Rakefile +12 -16
- data/bin/console +3 -3
- data/bin/rspec +6 -6
- data/bin/rubocop +29 -0
- data/config/default.yml +94 -10
- data/config/rbi.yml +262 -0
- data/dev.yml +1 -1
- data/lib/rubocop/cop/sorbet/binding_constants_without_type_alias.rb +4 -4
- data/lib/rubocop/cop/sorbet/callback_conditionals_binding.rb +142 -0
- data/lib/rubocop/cop/sorbet/constants_from_strings.rb +1 -1
- data/lib/rubocop/cop/sorbet/forbid_include_const_literal.rb +11 -2
- data/lib/rubocop/cop/sorbet/forbid_superclass_const_literal.rb +2 -2
- data/lib/rubocop/cop/sorbet/forbid_t_unsafe.rb +26 -0
- data/lib/rubocop/cop/sorbet/forbid_untyped_struct_props.rb +2 -2
- data/lib/rubocop/cop/sorbet/one_ancestor_per_line.rb +75 -0
- data/lib/rubocop/cop/sorbet/rbi/forbid_extend_t_sig_helpers_in_shims.rb +53 -0
- data/lib/rubocop/cop/sorbet/rbi/forbid_rbi_outside_of_allowed_paths.rb +47 -0
- data/lib/rubocop/cop/sorbet/rbi/single_line_rbi_class_module_definitions.rb +46 -0
- data/lib/rubocop/cop/sorbet/sigils/enforce_sigil_order.rb +6 -6
- data/lib/rubocop/cop/sorbet/sigils/enforce_single_sigil.rb +63 -0
- data/lib/rubocop/cop/sorbet/sigils/false_sigil.rb +3 -3
- data/lib/rubocop/cop/sorbet/sigils/has_sigil.rb +2 -2
- data/lib/rubocop/cop/sorbet/sigils/ignore_sigil.rb +3 -3
- data/lib/rubocop/cop/sorbet/sigils/strict_sigil.rb +3 -3
- data/lib/rubocop/cop/sorbet/sigils/strong_sigil.rb +3 -3
- data/lib/rubocop/cop/sorbet/sigils/true_sigil.rb +3 -3
- data/lib/rubocop/cop/sorbet/sigils/valid_sigil.rb +10 -8
- data/lib/rubocop/cop/sorbet/signatures/allow_incompatible_override.rb +3 -3
- data/lib/rubocop/cop/sorbet/signatures/checked_true_in_signature.rb +6 -6
- data/lib/rubocop/cop/sorbet/signatures/enforce_signatures.rb +30 -21
- data/lib/rubocop/cop/sorbet/signatures/keyword_argument_ordering.rb +3 -3
- data/lib/rubocop/cop/sorbet/signatures/signature_build_order.rb +24 -15
- data/lib/rubocop/cop/sorbet/signatures/signature_cop.rb +17 -2
- data/lib/rubocop/cop/sorbet_cops.rb +26 -19
- data/lib/rubocop/sorbet/version.rb +1 -1
- data/lib/rubocop/sorbet.rb +1 -1
- data/lib/rubocop-sorbet.rb +5 -5
- data/manual/cops.md +7 -1
- data/manual/cops_sorbet.md +247 -14
- data/rubocop-sorbet.gemspec +2 -2
- data/service.yml +0 -5
- data/tasks/cops_documentation.rake +60 -62
- metadata +20 -12
- data/.shopify-build/VERSION +0 -1
- data/.shopify-build/rubocop-sorbet.yml +0 -16
- data/lib/rubocop/cop/sorbet/signatures/parameters_ordering_in_signature.rb +0 -70
data/config/rbi.yml
ADDED
@@ -0,0 +1,262 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-sorbet
|
3
|
+
|
4
|
+
AllCops:
|
5
|
+
TargetRubyVersion: 3.0
|
6
|
+
DisabledByDefault: true
|
7
|
+
Include:
|
8
|
+
- '**/*.rbi'
|
9
|
+
|
10
|
+
## Layout
|
11
|
+
|
12
|
+
Layout/AccessModifierIndentation:
|
13
|
+
Enabled: true
|
14
|
+
|
15
|
+
Layout/ArgumentAlignment:
|
16
|
+
Enabled: true
|
17
|
+
|
18
|
+
Layout/BlockAlignment:
|
19
|
+
Enabled: true
|
20
|
+
|
21
|
+
Layout/BlockEndNewline:
|
22
|
+
Enabled: true
|
23
|
+
|
24
|
+
Layout/ClassStructure:
|
25
|
+
Enabled: true
|
26
|
+
ExpectedOrder:
|
27
|
+
- module_inclusion
|
28
|
+
- macros
|
29
|
+
- public_attribute_macros
|
30
|
+
- protected_attribute_macros
|
31
|
+
- private_attribute_macros
|
32
|
+
- initializer
|
33
|
+
- public_methods
|
34
|
+
- protected_methods
|
35
|
+
- private_methods
|
36
|
+
- public_class_methods
|
37
|
+
- constants
|
38
|
+
|
39
|
+
Layout/ClosingParenthesisIndentation:
|
40
|
+
Enabled: true
|
41
|
+
|
42
|
+
Layout/CommentIndentation:
|
43
|
+
Enabled: true
|
44
|
+
|
45
|
+
Layout/EmptyComment:
|
46
|
+
Enabled: true
|
47
|
+
|
48
|
+
Layout/EmptyLineAfterMagicComment:
|
49
|
+
Enabled: true
|
50
|
+
|
51
|
+
Layout/EmptyLineBetweenDefs:
|
52
|
+
Enabled: true
|
53
|
+
AllowAdjacentOneLineDefs: true
|
54
|
+
|
55
|
+
Layout/EmptyLines:
|
56
|
+
Enabled: true
|
57
|
+
|
58
|
+
Layout/EmptyLinesAroundAccessModifier:
|
59
|
+
Enabled: true
|
60
|
+
|
61
|
+
Layout/EmptyLinesAroundArguments:
|
62
|
+
Enabled: true
|
63
|
+
|
64
|
+
Layout/EmptyLinesAroundAttributeAccessor:
|
65
|
+
Enabled: true
|
66
|
+
|
67
|
+
Layout/EmptyLinesAroundBlockBody:
|
68
|
+
Enabled: true
|
69
|
+
|
70
|
+
Layout/EmptyLinesAroundClassBody:
|
71
|
+
Enabled: true
|
72
|
+
|
73
|
+
Layout/EmptyLinesAroundModuleBody:
|
74
|
+
Enabled: true
|
75
|
+
|
76
|
+
Layout/EndOfLine:
|
77
|
+
Enabled: true
|
78
|
+
EnforcedStyle: lf
|
79
|
+
|
80
|
+
Layout/ExtraSpacing:
|
81
|
+
Enabled: true
|
82
|
+
|
83
|
+
Layout/FirstMethodArgumentLineBreak:
|
84
|
+
Enabled: true
|
85
|
+
|
86
|
+
Layout/FirstMethodParameterLineBreak:
|
87
|
+
Enabled: true
|
88
|
+
|
89
|
+
Layout/FirstParameterIndentation:
|
90
|
+
Enabled: true
|
91
|
+
|
92
|
+
Layout/IndentationConsistency:
|
93
|
+
Enabled: true
|
94
|
+
|
95
|
+
Layout/IndentationStyle:
|
96
|
+
Enabled: true
|
97
|
+
EnforcedStyle: spaces
|
98
|
+
|
99
|
+
Layout/IndentationWidth:
|
100
|
+
Enabled: true
|
101
|
+
|
102
|
+
Layout/InitialIndentation:
|
103
|
+
Enabled: true
|
104
|
+
|
105
|
+
Layout/LeadingCommentSpace:
|
106
|
+
Enabled: true
|
107
|
+
|
108
|
+
Layout/LeadingEmptyLines:
|
109
|
+
Enabled: true
|
110
|
+
|
111
|
+
# TODO: make Tapioca break long lines?
|
112
|
+
Layout/LineLength:
|
113
|
+
Enabled: false
|
114
|
+
|
115
|
+
Layout/MultilineBlockLayout:
|
116
|
+
Enabled: true
|
117
|
+
|
118
|
+
Layout/MultilineMethodArgumentLineBreaks:
|
119
|
+
Enabled: true
|
120
|
+
|
121
|
+
Layout/MultilineMethodCallBraceLayout:
|
122
|
+
Enabled: true
|
123
|
+
|
124
|
+
Layout/MultilineMethodCallIndentation:
|
125
|
+
Enabled: true
|
126
|
+
|
127
|
+
Layout/ParameterAlignment:
|
128
|
+
Enabled: true
|
129
|
+
|
130
|
+
Layout/RedundantLineBreak:
|
131
|
+
Enabled: true
|
132
|
+
|
133
|
+
Layout/SpaceAfterColon:
|
134
|
+
Enabled: true
|
135
|
+
|
136
|
+
Layout/SpaceAfterComma:
|
137
|
+
Enabled: true
|
138
|
+
|
139
|
+
Layout/SpaceAfterMethodName:
|
140
|
+
Enabled: true
|
141
|
+
|
142
|
+
Layout/SpaceAfterSemicolon:
|
143
|
+
Enabled: true
|
144
|
+
|
145
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
146
|
+
Enabled: true
|
147
|
+
|
148
|
+
Layout/SpaceAroundKeyword:
|
149
|
+
Enabled: true
|
150
|
+
|
151
|
+
Layout/SpaceAroundMethodCallOperator:
|
152
|
+
Enabled: true
|
153
|
+
|
154
|
+
Layout/SpaceBeforeBlockBraces:
|
155
|
+
Enabled: true
|
156
|
+
|
157
|
+
Layout/SpaceBeforeBrackets:
|
158
|
+
Enabled: true
|
159
|
+
|
160
|
+
Layout/SpaceBeforeComma:
|
161
|
+
Enabled: true
|
162
|
+
|
163
|
+
Layout/SpaceBeforeComment:
|
164
|
+
Enabled: true
|
165
|
+
|
166
|
+
Layout/SpaceBeforeFirstArg:
|
167
|
+
Enabled: true
|
168
|
+
|
169
|
+
Layout/SpaceBeforeSemicolon:
|
170
|
+
Enabled: true
|
171
|
+
|
172
|
+
Layout/SpaceInsideParens:
|
173
|
+
Enabled: true
|
174
|
+
|
175
|
+
Layout/TrailingEmptyLines:
|
176
|
+
Enabled: true
|
177
|
+
EnforcedStyle: final_newline
|
178
|
+
|
179
|
+
Layout/TrailingWhitespace:
|
180
|
+
Enabled: true
|
181
|
+
|
182
|
+
## Lint
|
183
|
+
|
184
|
+
Lint/DuplicateMethods:
|
185
|
+
Enabled: true
|
186
|
+
|
187
|
+
Lint/EmptyFile:
|
188
|
+
Enabled: true
|
189
|
+
AllowComments: false
|
190
|
+
|
191
|
+
Lint/SyntaxError:
|
192
|
+
Enabled: true
|
193
|
+
|
194
|
+
## Sorbet
|
195
|
+
|
196
|
+
Sorbet:
|
197
|
+
DisabledByDefault: true
|
198
|
+
|
199
|
+
Sorbet/EnforceSigilOrder:
|
200
|
+
Enabled: true
|
201
|
+
|
202
|
+
Sorbet/ForbidExtendTSigHelpersInShims:
|
203
|
+
Enabled: true
|
204
|
+
|
205
|
+
Sorbet/ForbidIncludeConstLiteral:
|
206
|
+
Enabled: true
|
207
|
+
|
208
|
+
Sorbet/ForbidSuperclassConstLiteral:
|
209
|
+
Enabled: true
|
210
|
+
|
211
|
+
Sorbet/OneAncestorPerLine:
|
212
|
+
Enabled: true
|
213
|
+
|
214
|
+
Sorbet/SignatureBuildOrder:
|
215
|
+
Enabled: true
|
216
|
+
|
217
|
+
Sorbet/SingleLineRbiClassModuleDefinitions:
|
218
|
+
Enabled: true
|
219
|
+
|
220
|
+
Sorbet/ValidSigil:
|
221
|
+
Enabled: true
|
222
|
+
RequireSigilOnAllFiles: true
|
223
|
+
MinimumStrictness: "false"
|
224
|
+
SuggestedStrictness: "true"
|
225
|
+
|
226
|
+
## Style
|
227
|
+
|
228
|
+
Style/ClassAndModuleChildren:
|
229
|
+
Enabled: true
|
230
|
+
EnforcedStyle: compact
|
231
|
+
|
232
|
+
Style/DefWithParentheses:
|
233
|
+
Enabled: true
|
234
|
+
|
235
|
+
Style/EmptyMethod:
|
236
|
+
Enabled: true
|
237
|
+
EnforcedStyle: compact
|
238
|
+
|
239
|
+
Style/FrozenStringLiteralComment:
|
240
|
+
Enabled: true
|
241
|
+
EnforcedStyle: never
|
242
|
+
|
243
|
+
Style/MethodCallWithArgsParentheses:
|
244
|
+
Enabled: true
|
245
|
+
IgnoreMacros: true
|
246
|
+
|
247
|
+
Style/MethodCallWithoutArgsParentheses:
|
248
|
+
Enabled: true
|
249
|
+
|
250
|
+
Style/MethodDefParentheses:
|
251
|
+
Enabled: true
|
252
|
+
|
253
|
+
Style/MixinGrouping:
|
254
|
+
Enabled: true
|
255
|
+
EnforcedStyle: separated
|
256
|
+
|
257
|
+
Style/MixinUsage:
|
258
|
+
Enabled: true
|
259
|
+
|
260
|
+
Style/ModuleFunction:
|
261
|
+
Enabled: true
|
262
|
+
EnforcedStyle: forbidden
|
data/dev.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "rubocop"
|
4
4
|
|
5
5
|
module RuboCop
|
6
6
|
module Cop
|
@@ -94,16 +94,16 @@ module RuboCop
|
|
94
94
|
add_offense(
|
95
95
|
node.children[2],
|
96
96
|
message: "It looks like you're using the old `T.type_alias` syntax. " \
|
97
|
-
|
97
|
+
"`T.type_alias` now expects a block." \
|
98
98
|
'Run Sorbet with the options "--autocorrect --error-white-list=5043" ' \
|
99
|
-
|
99
|
+
"to automatically upgrade to the new syntax."
|
100
100
|
)
|
101
101
|
return
|
102
102
|
end
|
103
103
|
add_offense(
|
104
104
|
node.children[2],
|
105
105
|
message: "It looks like you're trying to bind a type to a constant. " \
|
106
|
-
|
106
|
+
"To do this, you must alias the type using `T.type_alias`."
|
107
107
|
)
|
108
108
|
end
|
109
109
|
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Sorbet
|
6
|
+
# This cop ensures that callback conditionals are bound to the right type
|
7
|
+
# so that they are type checked properly.
|
8
|
+
#
|
9
|
+
# Auto-correction is unsafe because other libraries define similar style callbacks as Rails, but don't always need
|
10
|
+
# binding to the attached class. Auto-correcting those usages can lead to false positives and auto-correction
|
11
|
+
# introduces new typing errors.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# class Post < ApplicationRecord
|
17
|
+
# before_create :do_it, if: -> { should_do_it? }
|
18
|
+
#
|
19
|
+
# def should_do_it?
|
20
|
+
# true
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# # good
|
25
|
+
# class Post < ApplicationRecord
|
26
|
+
# before_create :do_it, if: -> {
|
27
|
+
# T.bind(self, Post)
|
28
|
+
# should_do_it?
|
29
|
+
# }
|
30
|
+
#
|
31
|
+
# def should_do_it?
|
32
|
+
# true
|
33
|
+
# end
|
34
|
+
# end
|
35
|
+
class CallbackConditionalsBinding < RuboCop::Cop::Cop
|
36
|
+
CALLBACKS = [
|
37
|
+
:validate, :validates, :validates_with, :before_validation, :around_validation, :before_create,
|
38
|
+
:before_save, :before_destroy, :before_update, :after_create, :after_save, :after_destroy,
|
39
|
+
:after_update, :after_touch, :after_initialize, :after_find, :around_create, :around_save,
|
40
|
+
:around_destroy, :around_update, :before_commit, :after_commit, :after_create_commit,
|
41
|
+
:after_destroy_commit, :after_rollback, :after_save_commit, :after_update_commit,
|
42
|
+
:before_action, :prepend_before_action, :append_before_action, :around_action,
|
43
|
+
:prepend_around_action, :append_around_action, :after_action, :prepend_after_action,
|
44
|
+
:append_after_action
|
45
|
+
].freeze
|
46
|
+
|
47
|
+
def autocorrect(node)
|
48
|
+
lambda do |corrector|
|
49
|
+
options = node.each_child_node.find(&:hash_type?)
|
50
|
+
|
51
|
+
conditional = nil
|
52
|
+
options.each_pair do |keyword, block|
|
53
|
+
if keyword.value == :if || keyword.value == :unless
|
54
|
+
conditional = block
|
55
|
+
break
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
_, _, block = conditional.child_nodes
|
60
|
+
|
61
|
+
# Find the class node and check if it includes a namespace on the
|
62
|
+
# same line e.g.: Namespace::Class, which will require the fully
|
63
|
+
# qualified name
|
64
|
+
|
65
|
+
klass = node.ancestors.find(&:class_type?)
|
66
|
+
|
67
|
+
expected_class = if klass.children.first.children.first.nil?
|
68
|
+
node.parent_module_name.split("::").last
|
69
|
+
else
|
70
|
+
klass.identifier.source
|
71
|
+
end
|
72
|
+
|
73
|
+
do_end_lambda = conditional.source.include?("do") && conditional.source.include?("end")
|
74
|
+
|
75
|
+
unless do_end_lambda
|
76
|
+
# We are converting a one line lambda into a multiline
|
77
|
+
# Remove the space after the `{`
|
78
|
+
if /{\s/.match?(conditional.source)
|
79
|
+
corrector.remove_preceding(block, 1)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Remove the last space and `}` and re-add it with a line break
|
83
|
+
# and the correct indentation
|
84
|
+
base_indentation = " " * node.loc.column
|
85
|
+
chars_to_remove = /\s}/.match?(conditional.source) ? 2 : 1
|
86
|
+
corrector.remove_trailing(conditional, chars_to_remove)
|
87
|
+
corrector.insert_after(block, "\n#{base_indentation}}")
|
88
|
+
end
|
89
|
+
|
90
|
+
# Add the T.bind
|
91
|
+
indentation = " " * (node.loc.column + 2)
|
92
|
+
line_start = do_end_lambda ? "" : "\n#{indentation}"
|
93
|
+
bind = "#{line_start}T.bind(self, #{expected_class})\n#{indentation}"
|
94
|
+
|
95
|
+
corrector.insert_before(block, bind)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def on_send(node)
|
100
|
+
return unless CALLBACKS.include?(node.method_name)
|
101
|
+
|
102
|
+
options = node.each_child_node.find(&:hash_type?)
|
103
|
+
return if options.nil?
|
104
|
+
|
105
|
+
conditional = nil
|
106
|
+
options.each_pair do |keyword, block|
|
107
|
+
next unless keyword.sym_type?
|
108
|
+
|
109
|
+
if keyword.value == :if || keyword.value == :unless
|
110
|
+
conditional = block
|
111
|
+
break
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
return if conditional.nil? || conditional.array_type? || conditional.child_nodes.empty?
|
116
|
+
|
117
|
+
return unless conditional.arguments.empty?
|
118
|
+
|
119
|
+
type, _, block = conditional.child_nodes
|
120
|
+
return unless type.lambda_or_proc? || type.block_literal?
|
121
|
+
|
122
|
+
klass = node.ancestors.find(&:class_type?)
|
123
|
+
|
124
|
+
expected_class = if klass&.children&.first&.children&.first.nil?
|
125
|
+
node.parent_module_name&.split("::")&.last
|
126
|
+
else
|
127
|
+
klass.identifier.source
|
128
|
+
end
|
129
|
+
|
130
|
+
return if expected_class.nil?
|
131
|
+
|
132
|
+
unless block.source.include?("T.bind(self")
|
133
|
+
add_offense(
|
134
|
+
node,
|
135
|
+
message: "Callback conditionals should be bound to the right type. Use T.bind(self, #{expected_class})"
|
136
|
+
)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
4
|
+
require "rubocop"
|
5
5
|
|
6
6
|
# Correct `send` expressions in include statements by constant literals.
|
7
7
|
#
|
@@ -27,7 +27,7 @@ module RuboCop
|
|
27
27
|
module Cop
|
28
28
|
module Sorbet
|
29
29
|
class ForbidIncludeConstLiteral < RuboCop::Cop::Cop
|
30
|
-
MSG =
|
30
|
+
MSG = "Includes must only contain constant literals"
|
31
31
|
|
32
32
|
attr_accessor :used_names
|
33
33
|
|
@@ -52,6 +52,15 @@ module RuboCop
|
|
52
52
|
return unless [:module, :class, :sclass].include?(parent.type)
|
53
53
|
add_offense(node)
|
54
54
|
end
|
55
|
+
|
56
|
+
def autocorrect(node)
|
57
|
+
lambda do |corrector|
|
58
|
+
corrector.replace(
|
59
|
+
node.source_range,
|
60
|
+
"T.unsafe(self).#{node.source}"
|
61
|
+
)
|
62
|
+
end
|
63
|
+
end
|
55
64
|
end
|
56
65
|
end
|
57
66
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
4
|
+
require "rubocop"
|
5
5
|
|
6
6
|
# Correct superclass `send` expressions by constant literals.
|
7
7
|
#
|
@@ -25,7 +25,7 @@ module RuboCop
|
|
25
25
|
module Cop
|
26
26
|
module Sorbet
|
27
27
|
class ForbidSuperclassConstLiteral < RuboCop::Cop::Cop
|
28
|
-
MSG =
|
28
|
+
MSG = "Superclasses must only contain constant literals"
|
29
29
|
|
30
30
|
def_node_matcher :not_lit_const_superclass?, <<-PATTERN
|
31
31
|
(class
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rubocop"
|
4
|
+
|
5
|
+
module RuboCop
|
6
|
+
module Cop
|
7
|
+
module Sorbet
|
8
|
+
# This cop disallows using `T.unsafe` anywhere.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
#
|
12
|
+
# # bad
|
13
|
+
# T.unsafe(foo)
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# foo
|
17
|
+
class ForbidTUnsafe < RuboCop::Cop::Cop
|
18
|
+
def_node_matcher(:t_unsafe?, "(send (const nil? :T) :unsafe _)")
|
19
|
+
|
20
|
+
def on_send(node)
|
21
|
+
add_offense(node, message: "Do not use `T.unsafe`.") if t_unsafe?(node)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
4
|
+
require "rubocop"
|
5
5
|
|
6
6
|
module RuboCop
|
7
7
|
module Cop
|
@@ -23,7 +23,7 @@ module RuboCop
|
|
23
23
|
# prop :bar, T.nilable(String)
|
24
24
|
# end
|
25
25
|
class ForbidUntypedStructProps < RuboCop::Cop::Cop
|
26
|
-
MSG =
|
26
|
+
MSG = "Struct props cannot be T.untyped"
|
27
27
|
|
28
28
|
def_node_matcher :t_struct, <<~PATTERN
|
29
29
|
(const (const nil? :T) :Struct)
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "rubocop"
|
5
|
+
|
6
|
+
module RuboCop
|
7
|
+
module Cop
|
8
|
+
module Sorbet
|
9
|
+
# This cop ensures one ancestor per requires_ancestor line
|
10
|
+
# rather than chaining them as a comma-separated list.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
#
|
14
|
+
# # bad
|
15
|
+
# module SomeModule
|
16
|
+
# requires_ancestor Kernel, Minitest::Assertions
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# module SomeModule
|
21
|
+
# requires_ancestor Kernel
|
22
|
+
# requires_ancestor Minitest::Assertions
|
23
|
+
# end
|
24
|
+
class OneAncestorPerLine < RuboCop::Cop::Cop
|
25
|
+
MSG = "Cannot require more than one ancestor per line"
|
26
|
+
|
27
|
+
def_node_search :requires_ancestors, <<~PATTERN
|
28
|
+
(send nil? :requires_ancestor ...)
|
29
|
+
PATTERN
|
30
|
+
|
31
|
+
def_node_matcher :more_than_one_ancestor, <<~PATTERN
|
32
|
+
(send nil? :requires_ancestor const const+)
|
33
|
+
PATTERN
|
34
|
+
|
35
|
+
def_node_search :abstract?, <<~PATTERN
|
36
|
+
(send nil? :abstract!)
|
37
|
+
PATTERN
|
38
|
+
|
39
|
+
def on_module(node)
|
40
|
+
return unless node.body
|
41
|
+
return unless requires_ancestors(node)
|
42
|
+
process_node(node)
|
43
|
+
end
|
44
|
+
|
45
|
+
def on_class(node)
|
46
|
+
return unless abstract?(node)
|
47
|
+
return unless requires_ancestors(node)
|
48
|
+
process_node(node)
|
49
|
+
end
|
50
|
+
|
51
|
+
def autocorrect(node)
|
52
|
+
-> (corrector) do
|
53
|
+
ra_call = node.parent
|
54
|
+
split_ra_calls = ra_call.source.gsub(/,\s+/, new_ra_line(ra_call.loc.column))
|
55
|
+
corrector.replace(ra_call, split_ra_calls)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def process_node(node)
|
62
|
+
requires_ancestors(node).each do |ra|
|
63
|
+
add_offense(ra.child_nodes[1]) if more_than_one_ancestor(ra)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def new_ra_line(indent_count)
|
68
|
+
indents = " " * indent_count
|
69
|
+
indented_ra_call = "#{indents}requires_ancestor "
|
70
|
+
"\n#{indented_ra_call}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Sorbet
|
6
|
+
# This cop ensures RBI shims do not include a call to extend T::Sig
|
7
|
+
# or to extend T::Helpers
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# # bad
|
12
|
+
# module SomeModule
|
13
|
+
# extend T::Sig
|
14
|
+
# extend T::Helpers
|
15
|
+
#
|
16
|
+
# sig { returns(String) }
|
17
|
+
# def foo; end
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# module SomeModule
|
22
|
+
# sig { returns(String) }
|
23
|
+
# def foo; end
|
24
|
+
# end
|
25
|
+
class ForbidExtendTSigHelpersInShims < RuboCop::Cop::Cop
|
26
|
+
include RangeHelp
|
27
|
+
|
28
|
+
MSG = "Extending T::Sig or T::Helpers in a shim is unnecessary"
|
29
|
+
RESTRICT_ON_SEND = [:extend]
|
30
|
+
|
31
|
+
def_node_matcher :extend_t_sig?, <<~PATTERN
|
32
|
+
(send nil? :extend (const (const nil? :T) :Sig))
|
33
|
+
PATTERN
|
34
|
+
|
35
|
+
def_node_matcher :extend_t_helpers?, <<~PATTERN
|
36
|
+
(send nil? :extend (const (const nil? :T) :Helpers))
|
37
|
+
PATTERN
|
38
|
+
|
39
|
+
def autocorrect(node)
|
40
|
+
-> (corrector) do
|
41
|
+
corrector.remove(
|
42
|
+
range_by_whole_lines(node.source_range, include_final_newline: true)
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_send(node)
|
48
|
+
add_offense(node) if extend_t_helpers?(node) || extend_t_sig?(node)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|