rubocop 1.71.2 → 1.73.1

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.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/config/default.yml +54 -11
  4. data/config/internal_affairs.yml +16 -0
  5. data/lib/rubocop/cli/command/suggest_extensions.rb +7 -1
  6. data/lib/rubocop/comment_config.rb +1 -1
  7. data/lib/rubocop/config.rb +4 -0
  8. data/lib/rubocop/config_loader.rb +44 -8
  9. data/lib/rubocop/config_loader_resolver.rb +23 -9
  10. data/lib/rubocop/config_validator.rb +1 -1
  11. data/lib/rubocop/cop/internal_affairs/example_description.rb +4 -2
  12. data/lib/rubocop/cop/internal_affairs/location_exists.rb +116 -0
  13. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -1
  14. data/lib/rubocop/cop/internal_affairs/plugin.rb +33 -0
  15. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +7 -1
  16. data/lib/rubocop/cop/internal_affairs.rb +1 -16
  17. data/lib/rubocop/cop/layout/block_alignment.rb +2 -0
  18. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +4 -4
  19. data/lib/rubocop/cop/layout/else_alignment.rb +1 -1
  20. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  21. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +26 -1
  22. data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +22 -2
  23. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
  24. data/lib/rubocop/cop/layout/line_length.rb +3 -3
  25. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -2
  26. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  27. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -0
  28. data/lib/rubocop/cop/lint/duplicate_methods.rb +0 -14
  29. data/lib/rubocop/cop/lint/empty_conditional_body.rb +10 -5
  30. data/lib/rubocop/cop/lint/float_comparison.rb +1 -6
  31. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -1
  32. data/lib/rubocop/cop/lint/literal_as_condition.rb +99 -9
  33. data/lib/rubocop/cop/lint/mixed_case_range.rb +2 -2
  34. data/lib/rubocop/cop/lint/redundant_require_statement.rb +0 -21
  35. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +252 -0
  36. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +111 -0
  37. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +80 -0
  38. data/lib/rubocop/cop/lint/void.rb +6 -0
  39. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +7 -7
  40. data/lib/rubocop/cop/mixin/alignment.rb +2 -2
  41. data/lib/rubocop/cop/mixin/allowed_pattern.rb +4 -4
  42. data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
  43. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +18 -18
  44. data/lib/rubocop/cop/mixin/hash_subset.rb +19 -4
  45. data/lib/rubocop/cop/mixin/hash_transform_method.rb +74 -74
  46. data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
  47. data/lib/rubocop/cop/mixin/range_help.rb +3 -3
  48. data/lib/rubocop/cop/mixin/string_help.rb +1 -1
  49. data/lib/rubocop/cop/mixin/trailing_comma.rb +12 -0
  50. data/lib/rubocop/cop/naming/block_forwarding.rb +3 -3
  51. data/lib/rubocop/cop/naming/predicate_name.rb +44 -0
  52. data/lib/rubocop/cop/naming/variable_name.rb +64 -6
  53. data/lib/rubocop/cop/style/accessor_grouping.rb +19 -5
  54. data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -3
  55. data/lib/rubocop/cop/style/commented_keyword.rb +1 -1
  56. data/lib/rubocop/cop/style/endless_method.rb +163 -18
  57. data/lib/rubocop/cop/style/line_end_concatenation.rb +10 -4
  58. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +1 -1
  59. data/lib/rubocop/cop/style/redundant_condition.rb +46 -0
  60. data/lib/rubocop/cop/style/redundant_format.rb +250 -0
  61. data/lib/rubocop/cop/style/redundant_parentheses.rb +18 -4
  62. data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -1
  63. data/lib/rubocop/cop/style/single_line_methods.rb +3 -3
  64. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  65. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +47 -6
  66. data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +48 -6
  67. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  68. data/lib/rubocop/cop/util.rb +1 -1
  69. data/lib/rubocop/cop/utils/format_string.rb +7 -5
  70. data/lib/rubocop/cops_documentation_generator.rb +12 -1
  71. data/lib/rubocop/directive_comment.rb +35 -2
  72. data/lib/rubocop/lsp/runtime.rb +2 -0
  73. data/lib/rubocop/lsp/server.rb +0 -2
  74. data/lib/rubocop/options.rb +26 -11
  75. data/lib/rubocop/path_util.rb +4 -0
  76. data/lib/rubocop/plugin/configuration_integrator.rb +143 -0
  77. data/lib/rubocop/plugin/load_error.rb +26 -0
  78. data/lib/rubocop/plugin/loader.rb +100 -0
  79. data/lib/rubocop/plugin/not_supported_error.rb +29 -0
  80. data/lib/rubocop/plugin.rb +46 -0
  81. data/lib/rubocop/rake_task.rb +4 -1
  82. data/lib/rubocop/rspec/cop_helper.rb +9 -0
  83. data/lib/rubocop/rspec/shared_contexts.rb +15 -0
  84. data/lib/rubocop/rspec/support.rb +1 -0
  85. data/lib/rubocop/server/cache.rb +35 -2
  86. data/lib/rubocop/server/cli.rb +2 -2
  87. data/lib/rubocop/version.rb +17 -2
  88. data/lib/rubocop.rb +5 -1
  89. data/lib/ruby_lsp/rubocop/addon.rb +7 -10
  90. data/lib/ruby_lsp/rubocop/{wraps_built_in_lsp_runtime.rb → runtime_adapter.rb} +5 -8
  91. metadata +35 -10
  92. data/lib/rubocop/cop/utils/regexp_ranges.rb +0 -113
@@ -9,6 +9,80 @@ module RuboCop
9
9
 
10
10
  RESTRICT_ON_SEND = %i[[] to_h].freeze
11
11
 
12
+ # Internal helper class to hold match data
13
+ Captures = Struct.new(:transformed_argname, :transforming_body_expr, :unchanged_body_expr) do
14
+ def noop_transformation?
15
+ transforming_body_expr.lvar_type? &&
16
+ transforming_body_expr.children == [transformed_argname]
17
+ end
18
+
19
+ def transformation_uses_both_args?
20
+ transforming_body_expr.descendants.include?(unchanged_body_expr)
21
+ end
22
+
23
+ def use_transformed_argname?
24
+ transforming_body_expr.each_descendant(:lvar).any? do |node|
25
+ node.source == transformed_argname.to_s
26
+ end
27
+ end
28
+ end
29
+
30
+ # Internal helper class to hold autocorrect data
31
+ Autocorrection = Struct.new(:match, :block_node, :leading, :trailing) do
32
+ def self.from_each_with_object(node, match)
33
+ new(match, node, 0, 0)
34
+ end
35
+
36
+ def self.from_hash_brackets_map(node, match)
37
+ new(match, node.children.last, 'Hash['.length, ']'.length)
38
+ end
39
+
40
+ def self.from_map_to_h(node, match)
41
+ if node.parent&.block_type? && node.parent.send_node == node
42
+ strip_trailing_chars = 0
43
+ else
44
+ map_range = node.children.first.source_range
45
+ node_range = node.source_range
46
+ strip_trailing_chars = node_range.end_pos - map_range.end_pos
47
+ end
48
+
49
+ new(match, node.children.first, 0, strip_trailing_chars)
50
+ end
51
+
52
+ def self.from_to_h(node, match)
53
+ new(match, node, 0, 0)
54
+ end
55
+
56
+ def strip_prefix_and_suffix(node, corrector)
57
+ expression = node.source_range
58
+ corrector.remove_leading(expression, leading)
59
+ corrector.remove_trailing(expression, trailing)
60
+ end
61
+
62
+ def set_new_method_name(new_method_name, corrector)
63
+ range = block_node.send_node.loc.selector
64
+ if (send_end = block_node.send_node.loc.end)
65
+ # If there are arguments (only true in the `each_with_object`
66
+ # case)
67
+ range = range.begin.join(send_end)
68
+ end
69
+ corrector.replace(range, new_method_name)
70
+ end
71
+
72
+ def set_new_arg_name(transformed_argname, corrector)
73
+ corrector.replace(block_node.arguments, "|#{transformed_argname}|")
74
+ end
75
+
76
+ def set_new_body_expression(transforming_body_expr, corrector)
77
+ body_source = transforming_body_expr.source
78
+ if transforming_body_expr.hash_type? && !transforming_body_expr.braces?
79
+ body_source = "{ #{body_source} }"
80
+ end
81
+
82
+ corrector.replace(block_node.body, body_source)
83
+ end
84
+ end
85
+
12
86
  # @!method array_receiver?(node)
13
87
  def_node_matcher :array_receiver?, <<~PATTERN
14
88
  {(array ...) (send _ :each_with_index) (send _ :with_index _ ?) (send _ :zip ...)}
@@ -113,80 +187,6 @@ module RuboCop
113
187
  correction.set_new_arg_name(captures.transformed_argname, corrector)
114
188
  correction.set_new_body_expression(captures.transforming_body_expr, corrector)
115
189
  end
116
-
117
- # Internal helper class to hold match data
118
- Captures = Struct.new(:transformed_argname, :transforming_body_expr, :unchanged_body_expr) do
119
- def noop_transformation?
120
- transforming_body_expr.lvar_type? &&
121
- transforming_body_expr.children == [transformed_argname]
122
- end
123
-
124
- def transformation_uses_both_args?
125
- transforming_body_expr.descendants.include?(unchanged_body_expr)
126
- end
127
-
128
- def use_transformed_argname?
129
- transforming_body_expr.each_descendant(:lvar).any? do |node|
130
- node.source == transformed_argname.to_s
131
- end
132
- end
133
- end
134
-
135
- # Internal helper class to hold autocorrect data
136
- Autocorrection = Struct.new(:match, :block_node, :leading, :trailing) do
137
- def self.from_each_with_object(node, match)
138
- new(match, node, 0, 0)
139
- end
140
-
141
- def self.from_hash_brackets_map(node, match)
142
- new(match, node.children.last, 'Hash['.length, ']'.length)
143
- end
144
-
145
- def self.from_map_to_h(node, match)
146
- if node.parent&.block_type? && node.parent.send_node == node
147
- strip_trailing_chars = 0
148
- else
149
- map_range = node.children.first.source_range
150
- node_range = node.source_range
151
- strip_trailing_chars = node_range.end_pos - map_range.end_pos
152
- end
153
-
154
- new(match, node.children.first, 0, strip_trailing_chars)
155
- end
156
-
157
- def self.from_to_h(node, match)
158
- new(match, node, 0, 0)
159
- end
160
-
161
- def strip_prefix_and_suffix(node, corrector)
162
- expression = node.source_range
163
- corrector.remove_leading(expression, leading)
164
- corrector.remove_trailing(expression, trailing)
165
- end
166
-
167
- def set_new_method_name(new_method_name, corrector)
168
- range = block_node.send_node.loc.selector
169
- if (send_end = block_node.send_node.loc.end)
170
- # If there are arguments (only true in the `each_with_object`
171
- # case)
172
- range = range.begin.join(send_end)
173
- end
174
- corrector.replace(range, new_method_name)
175
- end
176
-
177
- def set_new_arg_name(transformed_argname, corrector)
178
- corrector.replace(block_node.arguments, "|#{transformed_argname}|")
179
- end
180
-
181
- def set_new_body_expression(transforming_body_expr, corrector)
182
- body_source = transforming_body_expr.source
183
- if transforming_body_expr.hash_type? && !transforming_body_expr.braces?
184
- body_source = "{ #{body_source} }"
185
- end
186
-
187
- corrector.replace(block_node.body, body_source)
188
- end
189
- end
190
190
  end
191
191
  end
192
192
  end
@@ -21,7 +21,7 @@ module RuboCop
21
21
  end
22
22
 
23
23
  def begin_source(node)
24
- node.loc.begin.source if node.loc.respond_to?(:begin) && node.loc.begin
24
+ node.loc.begin.source if node.loc?(:begin)
25
25
  end
26
26
 
27
27
  def type(node)
@@ -4,9 +4,10 @@ module RuboCop
4
4
  module Cop
5
5
  # Methods that calculate and return Parser::Source::Ranges
6
6
  module RangeHelp
7
- private
8
-
9
7
  BYTE_ORDER_MARK = 0xfeff # The Unicode codepoint
8
+ NOT_GIVEN = Module.new
9
+
10
+ private
10
11
 
11
12
  def source_range(source_buffer, line_number, column, length = 1)
12
13
  if column.is_a?(Range)
@@ -51,7 +52,6 @@ module RuboCop
51
52
  Parser::Source::Range.new(buffer, begin_pos, end_pos)
52
53
  end
53
54
 
54
- NOT_GIVEN = Module.new
55
55
  def range_with_surrounding_space(range_positional = NOT_GIVEN, # rubocop:disable Metrics/ParameterLists
56
56
  range: NOT_GIVEN, side: :both, newlines: true,
57
57
  whitespace: false, continuations: false,
@@ -10,7 +10,7 @@ module RuboCop
10
10
  def on_str(node)
11
11
  # Constants like __FILE__ are handled as strings,
12
12
  # but don't respond to begin.
13
- return unless node.loc.respond_to?(:begin) && node.loc.begin
13
+ return unless node.loc?(:begin)
14
14
  return if part_of_ignored_node?(node)
15
15
 
16
16
  if offense?(node)
@@ -4,6 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  # Common methods shared by Style/TrailingCommaInArguments,
6
6
  # Style/TrailingCommaInArrayLiteral and Style/TrailingCommaInHashLiteral
7
+ # rubocop:disable Metrics/ModuleLength
7
8
  module TrailingComma
8
9
  include ConfigurableEnforcedStyle
9
10
  include RangeHelp
@@ -57,6 +58,8 @@ module RuboCop
57
58
  ', unless each item is on its own line'
58
59
  when :consistent_comma
59
60
  ', unless items are split onto multiple lines'
61
+ when :diff_comma
62
+ ', unless that item immediately precedes a newline'
60
63
  else
61
64
  ''
62
65
  end
@@ -68,6 +71,8 @@ module RuboCop
68
71
  multiline?(node) && no_elements_on_same_line?(node)
69
72
  when :consistent_comma
70
73
  multiline?(node) && !method_name_and_arguments_on_same_line?(node)
74
+ when :diff_comma
75
+ multiline?(node) && last_item_precedes_newline?(node)
71
76
  else
72
77
  false
73
78
  end
@@ -130,6 +135,12 @@ module RuboCop
130
135
  range1.last_line == range2.line
131
136
  end
132
137
 
138
+ def last_item_precedes_newline?(node)
139
+ after_last_item =
140
+ range_between(node.children.last.source_range.end_pos, node.loc.end.begin_pos)
141
+ after_last_item.source =~ /\A,?\s*\n/
142
+ end
143
+
133
144
  def avoid_comma(kind, comma_begin_pos, extra_info)
134
145
  range = range_between(comma_begin_pos, comma_begin_pos + 1)
135
146
  article = kind.include?('array') ? 'an' : 'a'
@@ -205,5 +216,6 @@ module RuboCop
205
216
  false
206
217
  end
207
218
  end
219
+ # rubocop:enable Metrics/ModuleLength
208
220
  end
209
221
  end
@@ -14,10 +14,10 @@ module RuboCop
14
14
  # autocorrected.
15
15
  #
16
16
  # [NOTE]
17
- # --
17
+ # ====
18
18
  # Because of a bug in Ruby 3.3.0, when a block is referenced inside of another block,
19
19
  # no offense will be registered until Ruby 3.4:
20
-
20
+ #
21
21
  # [source,ruby]
22
22
  # ----
23
23
  # def foo(&block)
@@ -25,7 +25,7 @@ module RuboCop
25
25
  # block_method { bar(&block) }
26
26
  # end
27
27
  # ----
28
- # --
28
+ # ====
29
29
  #
30
30
  # @example EnforcedStyle: anonymous (default)
31
31
  #
@@ -17,6 +17,10 @@ module RuboCop
17
17
  # they end with a `?`. These methods should be changed to remove the
18
18
  # prefix.
19
19
  #
20
+ # When `UseSorbetSigs` set to true (optional), the cop will only report
21
+ # offenses if the method has a Sorbet `sig` with a return type of
22
+ # `T::Boolean`. Dynamic methods are not supported with this configuration.
23
+ #
20
24
  # @example NamePrefix: ['is_', 'has_', 'have_'] (default)
21
25
  # # bad
22
26
  # def is_even(value)
@@ -58,6 +62,30 @@ module RuboCop
58
62
  # def is_even?(value)
59
63
  # end
60
64
  #
65
+ # @example UseSorbetSigs: false (default)
66
+ # # bad
67
+ # sig { returns(String) }
68
+ # def is_this_thing_on
69
+ # "yes"
70
+ # end
71
+ #
72
+ # # good - Sorbet signature is not evaluated
73
+ # sig { returns(String) }
74
+ # def is_this_thing_on?
75
+ # "yes"
76
+ # end
77
+ #
78
+ # @example UseSorbetSigs: true
79
+ # # bad
80
+ # sig { returns(T::Boolean) }
81
+ # def odd(value)
82
+ # end
83
+ #
84
+ # # good
85
+ # sig { returns(T::Boolean) }
86
+ # def odd?(value)
87
+ # end
88
+ #
61
89
  # @example MethodDefinitionMacros: ['define_method', 'define_singleton_method'] (default)
62
90
  # # bad
63
91
  # define_method(:is_even) { |value| }
@@ -100,6 +128,7 @@ module RuboCop
100
128
  method_name = node.method_name.to_s
101
129
 
102
130
  next if allowed_method_name?(method_name, prefix)
131
+ next if use_sorbet_sigs? && !sorbet_sig?(node, return_type: 'T::Boolean')
103
132
 
104
133
  add_offense(
105
134
  node.loc.name,
@@ -121,6 +150,17 @@ module RuboCop
121
150
 
122
151
  private
123
152
 
153
+ # @!method sorbet_return_type(node)
154
+ def_node_matcher :sorbet_return_type, <<~PATTERN
155
+ (block (send nil? :sig) args (send _ :returns $_type))
156
+ PATTERN
157
+
158
+ def sorbet_sig?(node, return_type: nil)
159
+ return false unless (type = sorbet_return_type(node.left_sibling))
160
+
161
+ type.source == return_type
162
+ end
163
+
124
164
  def allowed_method_name?(method_name, prefix)
125
165
  !(method_name.start_with?(prefix) && # cheap check to avoid allocating Regexp
126
166
  method_name.match?(/^#{prefix}[^0-9]/)) ||
@@ -151,6 +191,10 @@ module RuboCop
151
191
  cop_config['NamePrefix']
152
192
  end
153
193
 
194
+ def use_sorbet_sigs?
195
+ cop_config['UseSorbetSigs']
196
+ end
197
+
154
198
  def method_definition_macros(macro_name)
155
199
  cop_config['MethodDefinitionMacros'].include?(macro_name.to_s)
156
200
  end
@@ -3,8 +3,15 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Naming
6
- # Makes sure that all variables use the configured style,
7
- # snake_case or camelCase, for their names.
6
+ # Checks that the configured style (snake_case or camelCase) is used for all variable names.
7
+ # This includes local variables, instance variables, class variables, method arguments
8
+ # (positional, keyword, rest or block), and block arguments.
9
+ #
10
+ # The cop can also be configured to forbid using specific names for variables, using
11
+ # `ForbiddenIdentifiers` or `ForbiddenPatterns`. In addition to the above, this applies
12
+ # to global variables as well.
13
+ #
14
+ # Method definitions and method calls are not affected by this cop.
8
15
  #
9
16
  # @example EnforcedStyle: snake_case (default)
10
17
  # # bad
@@ -26,7 +33,21 @@ module RuboCop
26
33
  #
27
34
  # @example AllowedPatterns: ['_v\d+\z']
28
35
  # # good (with EnforcedStyle: camelCase)
29
- # :release_v1
36
+ # release_v1 = true
37
+ #
38
+ # @example ForbiddenIdentifiers: ['fooBar']
39
+ # # bad (in all cases)
40
+ # fooBar = 1
41
+ # @fooBar = 1
42
+ # @@fooBar = 1
43
+ # $fooBar = 1
44
+ #
45
+ # @example ForbiddenPatterns: ['_v\d+\z']
46
+ # # bad (in all cases)
47
+ # release_v1 = true
48
+ # @release_v1 = true
49
+ # @@release_v1 = true
50
+ # $release_v1 = true
30
51
  #
31
52
  class VariableName < Base
32
53
  include AllowedIdentifiers
@@ -34,16 +55,21 @@ module RuboCop
34
55
  include AllowedPattern
35
56
 
36
57
  MSG = 'Use %<style>s for variable names.'
58
+ MSG_FORBIDDEN = '`%<identifier>s` is forbidden, use another name instead.'
37
59
 
38
60
  def valid_name?(node, name, given_style = style)
39
61
  super || matches_allowed_pattern?(name)
40
62
  end
41
63
 
42
64
  def on_lvasgn(node)
43
- return unless node.name
44
- return if allowed_identifier?(node.name)
65
+ return unless (name = node.name)
66
+ return if allowed_identifier?(name)
45
67
 
46
- check_name(node, node.name, node.loc.name)
68
+ if forbidden_name?(name)
69
+ register_forbidden_name(node)
70
+ else
71
+ check_name(node, name, node.loc.name)
72
+ end
47
73
  end
48
74
  alias on_ivasgn on_lvasgn
49
75
  alias on_cvasgn on_lvasgn
@@ -56,11 +82,43 @@ module RuboCop
56
82
  alias on_blockarg on_lvasgn
57
83
  alias on_lvar on_lvasgn
58
84
 
85
+ # Only forbidden names are checked for global variable assignment
86
+ def on_gvasgn(node)
87
+ return unless (name = node.name)
88
+ return unless forbidden_name?(name)
89
+
90
+ register_forbidden_name(node)
91
+ end
92
+
59
93
  private
60
94
 
61
95
  def message(style)
62
96
  format(MSG, style: style)
63
97
  end
98
+
99
+ def forbidden_identifiers
100
+ cop_config.fetch('ForbiddenIdentifiers', [])
101
+ end
102
+
103
+ def forbidden_patterns
104
+ cop_config.fetch('ForbiddenPatterns', [])
105
+ end
106
+
107
+ def matches_forbidden_pattern?(name)
108
+ forbidden_patterns.any? { |pattern| Regexp.new(pattern).match?(name) }
109
+ end
110
+
111
+ def forbidden_name?(name)
112
+ name = name.to_s.delete(SIGILS)
113
+
114
+ (forbidden_identifiers.any? && forbidden_identifiers.include?(name)) ||
115
+ (forbidden_patterns.any? && matches_forbidden_pattern?(name))
116
+ end
117
+
118
+ def register_forbidden_name(node)
119
+ message = format(MSG_FORBIDDEN, identifier: node.name)
120
+ add_offense(node.loc.name, message: message)
121
+ end
64
122
  end
65
123
  end
66
124
  end
@@ -139,12 +139,16 @@ module RuboCop
139
139
  style == :separated
140
140
  end
141
141
 
142
+ def groupable_sibling_accessor?(node, sibling)
143
+ sibling.attribute_accessor? &&
144
+ sibling.method?(node.method_name) &&
145
+ node_visibility(sibling) == node_visibility(node) &&
146
+ groupable_accessor?(sibling) && !previous_line_comment?(sibling)
147
+ end
148
+
142
149
  def groupable_sibling_accessors(send_node)
143
150
  send_node.parent.each_child_node(:send).select do |sibling|
144
- sibling.attribute_accessor? &&
145
- sibling.method?(send_node.method_name) &&
146
- node_visibility(sibling) == node_visibility(send_node) &&
147
- groupable_accessor?(sibling) && !previous_line_comment?(sibling)
151
+ groupable_sibling_accessor?(send_node, sibling)
148
152
  end
149
153
  end
150
154
 
@@ -155,13 +159,23 @@ module RuboCop
155
159
 
156
160
  def preferred_accessors(node)
157
161
  if grouped_style?
162
+ return if skip_for_grouping?(node)
163
+
158
164
  accessors = groupable_sibling_accessors(node)
159
- group_accessors(node, accessors) if node.loc == accessors.first.loc
165
+ if node.loc == accessors.first.loc || skip_for_grouping?(accessors.first)
166
+ group_accessors(node, accessors)
167
+ end
160
168
  else
161
169
  separate_accessors(node)
162
170
  end
163
171
  end
164
172
 
173
+ # Group after constants
174
+ def skip_for_grouping?(node)
175
+ node.right_siblings.any?(&:casgn_type?) &&
176
+ node.right_siblings.any? { |n| n.send_type? && groupable_sibling_accessor?(node, n) }
177
+ end
178
+
165
179
  def group_accessors(node, accessors)
166
180
  accessor_names = accessors.flat_map { |accessor| accessor.arguments.map(&:source) }.uniq
167
181
 
@@ -32,10 +32,10 @@ module RuboCop
32
32
  # This cop handles not only method forwarding but also forwarding to `super`.
33
33
  #
34
34
  # [NOTE]
35
- # --
35
+ # ====
36
36
  # Because of a bug in Ruby 3.3.0, when a block is referenced inside of another block,
37
37
  # no offense will be registered until Ruby 3.4:
38
-
38
+ #
39
39
  # [source,ruby]
40
40
  # ----
41
41
  # def foo(&block)
@@ -43,7 +43,7 @@ module RuboCop
43
43
  # block_method { bar(&block) }
44
44
  # end
45
45
  # ----
46
- # --
46
+ # ====
47
47
  #
48
48
  # @example
49
49
  # # bad
@@ -57,7 +57,7 @@ module RuboCop
57
57
 
58
58
  REGEXP = /(?<keyword>\S+).*#/.freeze
59
59
 
60
- SUBCLASS_DEFINITION = /\A\s*class\s+\w+\s*<\s*\w+/.freeze
60
+ SUBCLASS_DEFINITION = /\A\s*class\s+(\w|::)+\s*<\s*(\w|::)+/.freeze
61
61
  METHOD_DEFINITION = /\A\s*def\s/.freeze
62
62
 
63
63
  def on_new_investigation