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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.github/release.yml +23 -0
  3. data/.github/workflows/ci.yml +26 -0
  4. data/.gitignore +1 -0
  5. data/Gemfile +1 -0
  6. data/Gemfile.lock +25 -41
  7. data/README.md +22 -0
  8. data/Rakefile +12 -16
  9. data/bin/console +3 -3
  10. data/bin/rspec +6 -6
  11. data/bin/rubocop +29 -0
  12. data/config/default.yml +94 -10
  13. data/config/rbi.yml +262 -0
  14. data/dev.yml +1 -1
  15. data/lib/rubocop/cop/sorbet/binding_constants_without_type_alias.rb +4 -4
  16. data/lib/rubocop/cop/sorbet/callback_conditionals_binding.rb +142 -0
  17. data/lib/rubocop/cop/sorbet/constants_from_strings.rb +1 -1
  18. data/lib/rubocop/cop/sorbet/forbid_include_const_literal.rb +11 -2
  19. data/lib/rubocop/cop/sorbet/forbid_superclass_const_literal.rb +2 -2
  20. data/lib/rubocop/cop/sorbet/forbid_t_unsafe.rb +26 -0
  21. data/lib/rubocop/cop/sorbet/forbid_untyped_struct_props.rb +2 -2
  22. data/lib/rubocop/cop/sorbet/one_ancestor_per_line.rb +75 -0
  23. data/lib/rubocop/cop/sorbet/rbi/forbid_extend_t_sig_helpers_in_shims.rb +53 -0
  24. data/lib/rubocop/cop/sorbet/rbi/forbid_rbi_outside_of_allowed_paths.rb +47 -0
  25. data/lib/rubocop/cop/sorbet/rbi/single_line_rbi_class_module_definitions.rb +46 -0
  26. data/lib/rubocop/cop/sorbet/sigils/enforce_sigil_order.rb +6 -6
  27. data/lib/rubocop/cop/sorbet/sigils/enforce_single_sigil.rb +63 -0
  28. data/lib/rubocop/cop/sorbet/sigils/false_sigil.rb +3 -3
  29. data/lib/rubocop/cop/sorbet/sigils/has_sigil.rb +2 -2
  30. data/lib/rubocop/cop/sorbet/sigils/ignore_sigil.rb +3 -3
  31. data/lib/rubocop/cop/sorbet/sigils/strict_sigil.rb +3 -3
  32. data/lib/rubocop/cop/sorbet/sigils/strong_sigil.rb +3 -3
  33. data/lib/rubocop/cop/sorbet/sigils/true_sigil.rb +3 -3
  34. data/lib/rubocop/cop/sorbet/sigils/valid_sigil.rb +10 -8
  35. data/lib/rubocop/cop/sorbet/signatures/allow_incompatible_override.rb +3 -3
  36. data/lib/rubocop/cop/sorbet/signatures/checked_true_in_signature.rb +6 -6
  37. data/lib/rubocop/cop/sorbet/signatures/enforce_signatures.rb +30 -21
  38. data/lib/rubocop/cop/sorbet/signatures/keyword_argument_ordering.rb +3 -3
  39. data/lib/rubocop/cop/sorbet/signatures/signature_build_order.rb +24 -15
  40. data/lib/rubocop/cop/sorbet/signatures/signature_cop.rb +17 -2
  41. data/lib/rubocop/cop/sorbet_cops.rb +26 -19
  42. data/lib/rubocop/sorbet/version.rb +1 -1
  43. data/lib/rubocop/sorbet.rb +1 -1
  44. data/lib/rubocop-sorbet.rb +5 -5
  45. data/manual/cops.md +7 -1
  46. data/manual/cops_sorbet.md +247 -14
  47. data/rubocop-sorbet.gemspec +2 -2
  48. data/service.yml +0 -5
  49. data/tasks/cops_documentation.rake +60 -62
  50. metadata +20 -12
  51. data/.shopify-build/VERSION +0 -1
  52. data/.shopify-build/rubocop-sorbet.yml +0 -16
  53. 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
@@ -8,4 +8,4 @@ up:
8
8
 
9
9
  commands:
10
10
  test: "bin/rspec"
11
- style: "bundle exec rubocop"
11
+ style: "bin/rubocop"
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rubocop'
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
- '`T.type_alias` now expects a block.' \
97
+ "`T.type_alias` now expects a block." \
98
98
  'Run Sorbet with the options "--autocorrect --error-white-list=5043" ' \
99
- 'to automatically upgrade to the new syntax.'
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
- 'To do this, you must alias the type using `T.type_alias`.'
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,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rubocop'
3
+ require "rubocop"
4
4
 
5
5
  module RuboCop
6
6
  module Cop
@@ -1,7 +1,7 @@
1
1
  # encoding: utf-8
2
2
  # frozen_string_literal: true
3
3
 
4
- require 'rubocop'
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 = 'Includes must only contain constant literals'
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 'rubocop'
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 = 'Superclasses must only contain constant literals'
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 'rubocop'
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 = 'Struct props cannot be T.untyped'
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