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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 39f455b224ebfe0ed1167d4f65df45ba786f5605e7571c81775235563c2d964a
4
- data.tar.gz: f0b2762513b7ee13abf3e2ad63222e796df11692f837530af59285802a54389c
3
+ metadata.gz: a9ebae9ccd4d5ac64cdcede02bbad10ffe8bd68b31a99d2e3c2914de447dae24
4
+ data.tar.gz: bbf3c32b940424369bde19fabcd6ec5cab543c265ce810d288a947dbae1d7ea7
5
5
  SHA512:
6
- metadata.gz: 1407c352354c7dcc548f0e6f2a031ba214627e09c766fd67665f0c47973232bbe406cff70a1a6c6178494bbacee41fcdc0ace83a2c987c7220cdd9e494384bbe
7
- data.tar.gz: 6df2c39e491354b98265115b646ed149bf84b127d28057a0eac05714ed45312377d2fc454945c3e602817b44b46d805c9572affa68d4f04429ef3635faf9663c
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.2)
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.75.8)
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.44.0, < 2.0)
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.44.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.0)
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
- puts "git commit -m 'Release #{version}'"
64
- puts "git push origin main"
65
- puts "git tag -a v#{version} -m 'Release #{version}'"
66
- puts "git push origin v#{version}"
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.2
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.arguments.map(&:source).join(", ")
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
- replacement = "define_method(:#{method_name}) do |#{args}|#{body}\n#{indent}end"
114
+ signature_replacement = "define_method(:#{method_name}) do#{args}"
73
115
  elsif node.defs_type?
74
116
  receiver = node.receiver.source
75
- replacement = "#{receiver}.define_singleton_method(:#{method_name}) do |#{args}|#{body}\n#{indent}end"
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
- corrector.replace(node, replacement)
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"
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module Sorbet
5
- VERSION = "0.10.2"
5
+ VERSION = "0.10.3"
6
6
  end
7
7
  end
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)
@@ -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.2
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