rubocop 1.55.0 → 1.56.0

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +10 -0
  4. data/lib/rubocop/config_finder.rb +2 -2
  5. data/lib/rubocop/cop/base.rb +1 -1
  6. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -0
  7. data/lib/rubocop/cop/bundler/duplicated_group.rb +81 -0
  8. data/lib/rubocop/cop/bundler/ordered_gems.rb +9 -1
  9. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +9 -1
  10. data/lib/rubocop/cop/internal_affairs/example_description.rb +2 -1
  11. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  12. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  13. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +2 -2
  14. data/lib/rubocop/cop/layout/space_inside_parens.rb +1 -1
  15. data/lib/rubocop/cop/lint/struct_new_override.rb +12 -12
  16. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  17. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  18. data/lib/rubocop/cop/mixin/string_help.rb +4 -2
  19. data/lib/rubocop/cop/style/alias.rb +9 -8
  20. data/lib/rubocop/cop/style/arguments_forwarding.rb +82 -49
  21. data/lib/rubocop/cop/style/block_delimiters.rb +2 -1
  22. data/lib/rubocop/cop/style/class_equality_comparison.rb +2 -0
  23. data/lib/rubocop/cop/style/lambda_call.rb +5 -0
  24. data/lib/rubocop/cop/style/mixin_grouping.rb +1 -1
  25. data/lib/rubocop/cop/style/redundant_return.rb +7 -2
  26. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +2 -0
  27. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +30 -5
  28. data/lib/rubocop/cop/style/symbol_array.rb +3 -3
  29. data/lib/rubocop/cop/variable_force/assignment.rb +1 -3
  30. data/lib/rubocop/lsp/routes.rb +23 -17
  31. data/lib/rubocop/lsp/runtime.rb +8 -2
  32. data/lib/rubocop/lsp/server.rb +2 -2
  33. data/lib/rubocop/runner.rb +5 -3
  34. data/lib/rubocop/version.rb +1 -1
  35. data/lib/rubocop.rb +1 -0
  36. metadata +18 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7bfd5d73e8097398116fc389fa55f96eca51b61c95a5e9e4654d7eeebaeef985
4
- data.tar.gz: a2cf8206a9896953e85516ebdc00f673a5852eb9613e877fee6b43058f7ef080
3
+ metadata.gz: 76532b5c0c7ccf0baff454ad37e17c405e4f827a0c8323ce1a5aab0ee9e78c19
4
+ data.tar.gz: fe2f7d727d5addd930a762ebe7fb34a38845164fed801744a6802ab91e6ba779
5
5
  SHA512:
6
- metadata.gz: 454b6be8363e0a91226348e2543587d5e6c3503bdaa9b2c0287f1a1e347f9a315c8f8337c5762e59f83f7406a797645ebef7e16a896694da29cb751a658e5579
7
- data.tar.gz: 388fe4a1934bf7659148ce32a2837f5ff0822babc1e17abd1a2c4f8138e6a839a7c4f304202e7e712763dbc23fadee4c92412554cbe699a8f81810e602d62f49
6
+ metadata.gz: ff206ac0d02084dd7014818da655f5f8d718ac1bb4bfc2f781d3c007839096a142236ded54d30084fa7b3e1689205ff7a8bc7ad7eee3ffea2d809e4610d6a1ae
7
+ data.tar.gz: aaa03f29b7669ffcf6fdcb16b7da4c38a6bc62187eb91d6347e3ada22f396370b6564177eb3a040fd5ddd3eeea9cf8b3ff51943e79c3d0ad410508a75bb3177b
data/README.md CHANGED
@@ -53,7 +53,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
53
53
  in your `Gemfile`:
54
54
 
55
55
  ```rb
56
- gem 'rubocop', '~> 1.55', require: false
56
+ gem 'rubocop', '~> 1.56', require: false
57
57
  ```
58
58
 
59
59
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
data/config/default.yml CHANGED
@@ -173,6 +173,16 @@ Bundler/DuplicatedGem:
173
173
  - '**/Gemfile'
174
174
  - '**/gems.rb'
175
175
 
176
+ Bundler/DuplicatedGroup:
177
+ Description: 'Checks for duplicate group entries in Gemfile.'
178
+ Enabled: true
179
+ Severity: warning
180
+ VersionAdded: '1.56'
181
+ Include:
182
+ - '**/*.gemfile'
183
+ - '**/Gemfile'
184
+ - '**/gems.rb'
185
+
176
186
  Bundler/GemComment:
177
187
  Description: 'Add a comment describing each gem.'
178
188
  Enabled: false
@@ -46,14 +46,14 @@ module RuboCop
46
46
 
47
47
  file = File.join(Dir.home, DOTFILE)
48
48
 
49
- return file if File.exist?(file)
49
+ file if File.exist?(file)
50
50
  end
51
51
 
52
52
  def find_user_xdg_config
53
53
  xdg_config_home = expand_path(ENV.fetch('XDG_CONFIG_HOME', '~/.config'))
54
54
  xdg_config = File.join(xdg_config_home, 'rubocop', XDG_CONFIG)
55
55
 
56
- return xdg_config if File.exist?(xdg_config)
56
+ xdg_config if File.exist?(xdg_config)
57
57
  end
58
58
 
59
59
  def expand_path(path)
@@ -53,7 +53,7 @@ module RuboCop
53
53
  # List of cops that should not try to autocorrect at the same
54
54
  # time as this cop
55
55
  #
56
- # @return [Array<RuboCop::Cop::Cop>]
56
+ # @return [Array<RuboCop::Cop::Base>]
57
57
  #
58
58
  # @api public
59
59
  def self.autocorrect_incompatible_with
@@ -4,6 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Bundler
6
6
  # A Gem's requirements should be listed only once in a Gemfile.
7
+ #
7
8
  # @example
8
9
  # # bad
9
10
  # gem 'rubocop'
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Bundler
6
+ # A Gem group, or a set of groups, should be listed only once in a Gemfile.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # group :development do
11
+ # gem 'rubocop'
12
+ # end
13
+ #
14
+ # group :development do
15
+ # gem 'rubocop-rails'
16
+ # end
17
+ #
18
+ # # bad (same set of groups declared twice)
19
+ # group :development, :test do
20
+ # gem 'rubocop'
21
+ # end
22
+ #
23
+ # group :test, :development do
24
+ # gem 'rspec'
25
+ # end
26
+ #
27
+ # # good
28
+ # group :development do
29
+ # gem 'rubocop'
30
+ # end
31
+ #
32
+ # group :development, :test do
33
+ # gem 'rspec'
34
+ # end
35
+ #
36
+ # # good
37
+ # gem 'rubocop', groups: [:development, :test]
38
+ # gem 'rspec', groups: [:development, :test]
39
+ #
40
+ class DuplicatedGroup < Base
41
+ include RangeHelp
42
+
43
+ MSG = 'Gem group `%<group_name>s` already defined on line ' \
44
+ '%<line_of_first_occurrence>d of the Gemfile.'
45
+
46
+ def on_new_investigation
47
+ return if processed_source.blank?
48
+
49
+ duplicated_group_nodes.each do |nodes|
50
+ nodes[1..].each do |node|
51
+ register_offense(node, node.arguments.map(&:value).join(', '), nodes.first.first_line)
52
+ end
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ # @!method group_declarations(node)
59
+ def_node_search :group_declarations, '(send nil? :group ...)'
60
+
61
+ def duplicated_group_nodes
62
+ group_declarations(processed_source.ast)
63
+ .group_by { |node| node.arguments.map(&:value).map(&:to_s).sort }
64
+ .values
65
+ .select { |nodes| nodes.size > 1 }
66
+ end
67
+
68
+ def register_offense(node, group_name, line_of_first_occurrence)
69
+ line_range = node.loc.column...node.loc.last_column
70
+ offense_location = source_range(processed_source.buffer, node.first_line, line_range)
71
+ message = format(
72
+ MSG,
73
+ group_name: group_name,
74
+ line_of_first_occurrence: line_of_first_occurrence
75
+ )
76
+ add_offense(offense_location, message: message)
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -19,7 +19,15 @@ module RuboCop
19
19
  #
20
20
  # gem 'rspec'
21
21
  #
22
- # # good only if TreatCommentsAsGroupSeparators is true
22
+ # @example TreatCommentsAsGroupSeparators: true (default)
23
+ # # good
24
+ # # For code quality
25
+ # gem 'rubocop'
26
+ # # For tests
27
+ # gem 'rspec'
28
+ #
29
+ # @example TreatCommentsAsGroupSeparators: false
30
+ # # bad
23
31
  # # For code quality
24
32
  # gem 'rubocop'
25
33
  # # For tests
@@ -45,7 +45,15 @@ module RuboCop
45
45
  #
46
46
  # spec.add_runtime_dependency 'rspec'
47
47
  #
48
- # # good only if TreatCommentsAsGroupSeparators is true
48
+ # @example TreatCommentsAsGroupSeparators: true (default)
49
+ # # good
50
+ # # For code quality
51
+ # spec.add_dependency 'rubocop'
52
+ # # For tests
53
+ # spec.add_dependency 'rspec'
54
+ #
55
+ # @example TreatCommentsAsGroupSeparators: false
56
+ # # bad
49
57
  # # For code quality
50
58
  # spec.add_dependency 'rubocop'
51
59
  # # For tests
@@ -46,7 +46,8 @@ module RuboCop
46
46
 
47
47
  EXPECT_OFFENSE_INCORRECT_DESCRIPTIONS = [
48
48
  /^(does not|doesn't) (register|find|flag|report)/,
49
- /^(does not|doesn't) add (a|an|any )?offense/
49
+ /^(does not|doesn't) add (a|an|any )?offense/,
50
+ /^accepts\b/
50
51
  ].freeze
51
52
 
52
53
  EXPECT_NO_CORRECTIONS_INCORRECT_DESCRIPTIONS = [/^(auto[- ]?)?correct/].freeze
@@ -137,7 +137,7 @@ module RuboCop
137
137
  return node if node
138
138
  end
139
139
 
140
- return last_heredoc_argument(n.receiver) if n.respond_to?(:receiver)
140
+ last_heredoc_argument(n.receiver) if n.respond_to?(:receiver)
141
141
  end
142
142
 
143
143
  def last_heredoc_argument_node(node)
@@ -354,7 +354,7 @@ module RuboCop
354
354
  # Don't check indentation if the line doesn't start with the body.
355
355
  # For example, lines like "else do_something".
356
356
  first_char_pos_on_line = body_node.source_range.source_line =~ /\S/
357
- return true unless body_node.loc.column == first_char_pos_on_line
357
+ true unless body_node.loc.column == first_char_pos_on_line
358
358
  end
359
359
 
360
360
  def offending_range(body_node, indentation)
@@ -19,7 +19,7 @@ module RuboCop
19
19
  # foo &. bar
20
20
  # foo &. bar&. buzz
21
21
  # RuboCop:: Cop
22
- # RuboCop:: Cop:: Cop
22
+ # RuboCop:: Cop:: Base
23
23
  # :: RuboCop::Cop
24
24
  #
25
25
  # # good
@@ -31,7 +31,7 @@ module RuboCop
31
31
  # foo&.bar
32
32
  # foo&.bar&.buzz
33
33
  # RuboCop::Cop
34
- # RuboCop::Cop::Cop
34
+ # RuboCop::Cop::Base
35
35
  # ::RuboCop::Cop
36
36
  #
37
37
  class SpaceAroundMethodCallOperator < Base
@@ -168,7 +168,7 @@ module RuboCop
168
168
  # follows, and that the rules for space inside don't apply.
169
169
  return true if token2.comment?
170
170
 
171
- return true unless same_line?(token1, token2) && !token1.space_after?
171
+ true unless same_line?(token1, token2) && !token1.space_after?
172
172
  end
173
173
  end
174
174
  end
@@ -32,25 +32,25 @@ module RuboCop
32
32
  # @!method struct_new(node)
33
33
  def_node_matcher :struct_new, <<~PATTERN
34
34
  (send
35
- (const ${nil? cbase} :Struct) :new ...)
35
+ (const {nil? cbase} :Struct) :new ...)
36
36
  PATTERN
37
37
 
38
38
  def on_send(node)
39
- return unless struct_new(node) do
40
- node.arguments.each_with_index do |arg, index|
41
- # Ignore if the first argument is a class name
42
- next if index.zero? && arg.str_type?
39
+ return unless struct_new(node)
43
40
 
44
- # Ignore if the argument is not a member name
45
- next unless STRUCT_MEMBER_NAME_TYPES.include?(arg.type)
41
+ node.arguments.each_with_index do |arg, index|
42
+ # Ignore if the first argument is a class name
43
+ next if index.zero? && arg.str_type?
46
44
 
47
- member_name = arg.value
45
+ # Ignore if the argument is not a member name
46
+ next unless STRUCT_MEMBER_NAME_TYPES.include?(arg.type)
48
47
 
49
- next unless STRUCT_METHOD_NAMES.include?(member_name.to_sym)
48
+ member_name = arg.value
50
49
 
51
- message = format(MSG, member_name: member_name.inspect, method_name: member_name.to_s)
52
- add_offense(arg, message: message)
53
- end
50
+ next unless STRUCT_METHOD_NAMES.include?(member_name.to_sym)
51
+
52
+ message = format(MSG, member_name: member_name.inspect, method_name: member_name.to_s)
53
+ add_offense(arg, message: message)
54
54
  end
55
55
  end
56
56
  end
@@ -119,7 +119,7 @@ module RuboCop
119
119
  ancestor = node.each_ancestor(:kwbegin, :def, :defs, :block, :numblock).first
120
120
  return false unless ancestor
121
121
 
122
- end_line = ancestor.loc.end.line
122
+ end_line = ancestor.loc.end&.line || ancestor.loc.last_line
123
123
  processed_source[node.first_line...end_line].any? { |line| comment_line?(line) }
124
124
  end
125
125
 
@@ -19,7 +19,7 @@ module RuboCop
19
19
 
20
20
  # @!method non_public_modifier?(node)
21
21
  def_node_matcher :non_public_modifier?, <<~PATTERN
22
- (send nil? {:private :protected} ({def defs} ...))
22
+ (send nil? {:private :protected :private_class_method} ({def defs} ...))
23
23
  PATTERN
24
24
  end
25
25
  end
@@ -30,8 +30,10 @@ module RuboCop
30
30
  private
31
31
 
32
32
  def inside_interpolation?(node)
33
- # A :begin node inside a :dstr or :dsym node is an interpolation.
34
- node.ancestors.drop_while { |a| !a.begin_type? }.any? { |a| a.dstr_type? || a.dsym_type? }
33
+ # A :begin node inside a :dstr, :dsym, or :regexp node is an interpolation.
34
+ node.ancestors
35
+ .drop_while { |a| !a.begin_type? }
36
+ .any? { |a| a.dstr_type? || a.dsym_type? || a.regexp_type? }
35
37
  end
36
38
  end
37
39
  end
@@ -122,7 +122,7 @@ module RuboCop
122
122
  end
123
123
 
124
124
  def bareword?(sym_node)
125
- !sym_node.source.start_with?(':')
125
+ !sym_node.source.start_with?(':') || sym_node.dsym_type?
126
126
  end
127
127
 
128
128
  def correct_alias_method_to_alias(corrector, send_node)
@@ -134,9 +134,7 @@ module RuboCop
134
134
 
135
135
  def correct_alias_to_alias_method(corrector, node)
136
136
  replacement =
137
- 'alias_method ' \
138
- ":#{identifier(node.new_identifier)}, " \
139
- ":#{identifier(node.old_identifier)}"
137
+ "alias_method #{identifier(node.new_identifier)}, #{identifier(node.old_identifier)}"
140
138
 
141
139
  corrector.replace(node, replacement)
142
140
  end
@@ -146,10 +144,13 @@ module RuboCop
146
144
  corrector.replace(node.old_identifier, node.old_identifier.source[1..])
147
145
  end
148
146
 
149
- # @!method identifier(node)
150
- def_node_matcher :identifier, <<~PATTERN
151
- (sym $_)
152
- PATTERN
147
+ def identifier(node)
148
+ if node.sym_type?
149
+ ":#{node.children.first}"
150
+ else
151
+ node.source
152
+ end
153
+ end
153
154
  end
154
155
  end
155
156
  end
@@ -100,7 +100,7 @@ module RuboCop
100
100
  return if send_classifications.empty?
101
101
 
102
102
  if only_forwards_all?(send_classifications)
103
- add_forward_all_offenses(node, send_classifications)
103
+ add_forward_all_offenses(node, send_classifications, forwardable_args)
104
104
  elsif target_ruby_version >= 3.2
105
105
  add_post_ruby_32_offenses(node, send_classifications, forwardable_args)
106
106
  end
@@ -118,12 +118,13 @@ module RuboCop
118
118
  send_classifications.each_value.all? { |c, _, _| c == :all }
119
119
  end
120
120
 
121
- def add_forward_all_offenses(node, send_classifications)
122
- send_classifications.each_key do |send_node|
123
- register_forward_all_offense_on_forwarding_method(send_node)
121
+ def add_forward_all_offenses(node, send_classifications, forwardable_args)
122
+ send_classifications.each do |send_node, (_c, forward_rest, _forward_kwrest)|
123
+ register_forward_all_offense(send_node, send_node, forward_rest)
124
124
  end
125
125
 
126
- register_forward_all_offense_on_method_def(node)
126
+ rest_arg, _kwrest_arg, _block_arg = *forwardable_args
127
+ register_forward_all_offense(node, node.arguments, rest_arg)
127
128
  end
128
129
 
129
130
  def add_post_ruby_32_offenses(def_node, send_classifications, forwardable_args)
@@ -186,9 +187,7 @@ module RuboCop
186
187
 
187
188
  def register_forward_args_offense(def_arguments_or_send, rest_arg_or_splat)
188
189
  add_offense(rest_arg_or_splat, message: ARGS_MSG) do |corrector|
189
- unless parentheses?(def_arguments_or_send)
190
- add_parentheses(def_arguments_or_send, corrector)
191
- end
190
+ add_parens_if_missing(def_arguments_or_send, corrector)
192
191
 
193
192
  corrector.replace(rest_arg_or_splat, '*')
194
193
  end
@@ -196,36 +195,28 @@ module RuboCop
196
195
 
197
196
  def register_forward_kwargs_offense(add_parens, def_arguments_or_send, kwrest_arg_or_splat)
198
197
  add_offense(kwrest_arg_or_splat, message: KWARGS_MSG) do |corrector|
199
- if add_parens && !parentheses?(def_arguments_or_send)
200
- add_parentheses(def_arguments_or_send, corrector)
201
- end
198
+ add_parens_if_missing(def_arguments_or_send, corrector) if add_parens
202
199
 
203
200
  corrector.replace(kwrest_arg_or_splat, '**')
204
201
  end
205
202
  end
206
203
 
207
- def register_forward_all_offense_on_forwarding_method(forwarding_method)
208
- add_offense(arguments_range(forwarding_method), message: FORWARDING_MSG) do |corrector|
209
- begin_pos = forwarding_method.loc.selector&.end_pos || forwarding_method.loc.dot.end_pos
210
- range = range_between(begin_pos, forwarding_method.source_range.end_pos)
204
+ def register_forward_all_offense(def_or_send, send_or_arguments, rest_or_splat)
205
+ arg_range = arguments_range(def_or_send, rest_or_splat)
211
206
 
212
- corrector.replace(range, '(...)')
213
- end
214
- end
207
+ add_offense(arg_range, message: FORWARDING_MSG) do |corrector|
208
+ add_parens_if_missing(send_or_arguments, corrector)
215
209
 
216
- def register_forward_all_offense_on_method_def(method_definition)
217
- add_offense(arguments_range(method_definition), message: FORWARDING_MSG) do |corrector|
218
- arguments_range = range_with_surrounding_space(
219
- method_definition.arguments.source_range, side: :left
220
- )
221
- corrector.replace(arguments_range, '(...)')
210
+ corrector.replace(arg_range, '...')
222
211
  end
223
212
  end
224
213
 
225
- def arguments_range(node)
214
+ def arguments_range(node, first_node)
226
215
  arguments = node.arguments
227
216
 
228
- range_between(arguments.first.source_range.begin_pos, arguments.last.source_range.end_pos)
217
+ start_node = first_node || arguments.first
218
+
219
+ range_between(start_node.source_range.begin_pos, arguments.last.source_range.end_pos)
229
220
  end
230
221
 
231
222
  def allow_only_rest_arguments?
@@ -236,18 +227,24 @@ module RuboCop
236
227
  cop_config.fetch('UseAnonymousForwarding', false)
237
228
  end
238
229
 
230
+ def add_parens_if_missing(node, corrector)
231
+ return if parentheses?(node)
232
+
233
+ add_parentheses(node, corrector)
234
+ end
235
+
239
236
  # Classifies send nodes for possible rest/kwrest/all (including block) forwarding.
240
237
  class SendNodeClassifier
241
238
  extend NodePattern::Macros
242
239
 
243
- # @!method find_forwarded_rest_arg(node, rest_name)
244
- def_node_search :find_forwarded_rest_arg, '(splat (lvar %1))'
240
+ # @!method forwarded_rest_arg?(node, rest_name)
241
+ def_node_matcher :forwarded_rest_arg?, '(splat (lvar %1))'
245
242
 
246
- # @!method find_forwarded_kwrest_arg(node, kwrest_name)
247
- def_node_search :find_forwarded_kwrest_arg, '(kwsplat (lvar %1))'
243
+ # @!method extract_forwarded_kwrest_arg(node, kwrest_name)
244
+ def_node_matcher :extract_forwarded_kwrest_arg, '(hash <$(kwsplat (lvar %1)) ...>)'
248
245
 
249
- # @!method find_forwarded_block_arg(node, block_name)
250
- def_node_search :find_forwarded_block_arg, '(block_pass {(lvar %1) nil?})'
246
+ # @!method forwarded_block_arg?(node, block_name)
247
+ def_node_matcher :forwarded_block_arg?, '(block_pass {(lvar %1) nil?})'
251
248
 
252
249
  def initialize(def_node, send_node, referenced_lvars, forwardable_args, **config)
253
250
  @def_node = def_node
@@ -262,25 +259,25 @@ module RuboCop
262
259
  def forwarded_rest_arg
263
260
  return nil if referenced_rest_arg?
264
261
 
265
- find_forwarded_rest_arg(@send_node, @rest_arg_name).first
262
+ arguments.find { |arg| forwarded_rest_arg?(arg, @rest_arg_name) }
266
263
  end
267
264
 
268
265
  def forwarded_kwrest_arg
269
266
  return nil if referenced_kwrest_arg?
270
267
 
271
- find_forwarded_kwrest_arg(@send_node, @kwrest_arg_name).first
268
+ arguments.filter_map { |arg| extract_forwarded_kwrest_arg(arg, @kwrest_arg_name) }.first
272
269
  end
273
270
 
274
271
  def forwarded_block_arg
275
272
  return nil if referenced_block_arg?
276
273
 
277
- find_forwarded_block_arg(@send_node, @block_arg_name).first
274
+ arguments.find { |arg| forwarded_block_arg?(arg, @block_arg_name) }
278
275
  end
279
276
 
280
277
  def classification
281
278
  return nil unless forwarded_rest_arg || forwarded_kwrest_arg
282
279
 
283
- if referenced_none? && (forwarded_exactly_all? || pre_ruby_32_allow_forward_all?)
280
+ if can_forward_all?
284
281
  :all
285
282
  elsif target_ruby_version >= 3.2
286
283
  :rest_or_kwrest
@@ -289,6 +286,31 @@ module RuboCop
289
286
 
290
287
  private
291
288
 
289
+ def can_forward_all?
290
+ return false if any_arg_referenced?
291
+ return false if ruby_32_missing_rest_or_kwest?
292
+ return false unless offensive_block_forwarding?
293
+ return false if forward_additional_kwargs?
294
+
295
+ no_additional_args? || (target_ruby_version >= 3.0 && no_post_splat_args?)
296
+ end
297
+
298
+ def ruby_32_missing_rest_or_kwest?
299
+ target_ruby_version >= 3.2 && !forwarded_rest_and_kwrest_args
300
+ end
301
+
302
+ def offensive_block_forwarding?
303
+ @block_arg ? forwarded_block_arg : allow_offense_for_no_block?
304
+ end
305
+
306
+ def forwarded_rest_and_kwrest_args
307
+ forwarded_rest_arg && forwarded_kwrest_arg
308
+ end
309
+
310
+ def arguments
311
+ @send_node.arguments
312
+ end
313
+
292
314
  def referenced_rest_arg?
293
315
  @referenced_lvars.include?(@rest_arg_name)
294
316
  end
@@ -301,25 +323,36 @@ module RuboCop
301
323
  @referenced_lvars.include?(@block_arg_name)
302
324
  end
303
325
 
304
- def referenced_none?
305
- !(referenced_rest_arg? || referenced_kwrest_arg? || referenced_block_arg?)
306
- end
307
-
308
- def forwarded_exactly_all?
309
- @send_node.arguments.size == 3 &&
310
- forwarded_rest_arg &&
311
- forwarded_kwrest_arg &&
312
- forwarded_block_arg
326
+ def any_arg_referenced?
327
+ referenced_rest_arg? || referenced_kwrest_arg? || referenced_block_arg?
313
328
  end
314
329
 
315
330
  def target_ruby_version
316
331
  @config.fetch(:target_ruby_version)
317
332
  end
318
333
 
319
- def pre_ruby_32_allow_forward_all?
320
- target_ruby_version < 3.2 &&
321
- @def_node.arguments.none?(&:default?) &&
322
- (@block_arg ? forwarded_block_arg : !@config.fetch(:allow_only_rest_arguments))
334
+ def no_post_splat_args?
335
+ splat_index = arguments.index(forwarded_rest_arg)
336
+ arg_after_splat = arguments[splat_index + 1]
337
+
338
+ [nil, :hash, :block_pass].include?(arg_after_splat&.type)
339
+ end
340
+
341
+ def forward_additional_kwargs?
342
+ return false unless forwarded_kwrest_arg
343
+
344
+ !forwarded_kwrest_arg.parent.children.one?
345
+ end
346
+
347
+ def allow_offense_for_no_block?
348
+ !@config.fetch(:allow_only_rest_arguments)
349
+ end
350
+
351
+ def no_additional_args?
352
+ forwardable_count = [@rest_arg, @kwrest_arg, @block_arg].compact.size
353
+
354
+ @def_node.arguments.size == forwardable_count &&
355
+ @send_node.arguments.size == forwardable_count
323
356
  end
324
357
  end
325
358
  end
@@ -370,7 +370,8 @@ module RuboCop
370
370
  def special_method_proper_block_style?(node)
371
371
  method_name = node.method_name
372
372
  return true if allowed_method?(method_name) || matches_allowed_pattern?(method_name)
373
- return node.braces? if braces_required_method?(method_name)
373
+
374
+ node.braces? if braces_required_method?(method_name)
374
375
  end
375
376
 
376
377
  def braces_required_method?(method_name)
@@ -69,6 +69,8 @@ module RuboCop
69
69
  matches_allowed_pattern?(def_node.method_name))
70
70
 
71
71
  class_comparison_candidate?(node) do |receiver_node, class_node|
72
+ return if class_node.dstr_type?
73
+
72
74
  range = offense_range(receiver_node, node)
73
75
  class_argument = (class_name = class_name(class_node, node)) ? "(#{class_name})" : ''
74
76
 
@@ -20,6 +20,7 @@ module RuboCop
20
20
  # lambda.(x, y)
21
21
  class LambdaCall < Base
22
22
  include ConfigurableEnforcedStyle
23
+ include IgnoredNode
23
24
  extend AutoCorrector
24
25
 
25
26
  MSG = 'Prefer the use of `%<prefer>s` over `%<current>s`.'
@@ -33,8 +34,12 @@ module RuboCop
33
34
  current = node.source
34
35
 
35
36
  add_offense(node, message: format(MSG, prefer: prefer, current: current)) do |corrector|
37
+ next if part_of_ignored_node?(node)
38
+
36
39
  opposite_style_detected
37
40
  corrector.replace(node, prefer)
41
+
42
+ ignore_node(node)
38
43
  end
39
44
  else
40
45
  correct_style_detected
@@ -40,7 +40,7 @@ module RuboCop
40
40
  def on_class(node)
41
41
  begin_node = node.child_nodes.find(&:begin_type?) || node
42
42
  begin_node.each_child_node(:send).select(&:macro?).each do |macro|
43
- next unless MIXIN_METHODS.include?(macro.method_name)
43
+ next if !MIXIN_METHODS.include?(macro.method_name) || macro.arguments.empty?
44
44
 
45
45
  check(macro)
46
46
  end
@@ -22,13 +22,18 @@ module RuboCop
22
22
  # return something
23
23
  # end
24
24
  #
25
- # # good
25
+ # # bad
26
26
  # def test
27
27
  # return something if something_else
28
28
  # end
29
29
  #
30
30
  # # good
31
31
  # def test
32
+ # something if something_else
33
+ # end
34
+ #
35
+ # # good
36
+ # def test
32
37
  # if x
33
38
  # elsif y
34
39
  # else
@@ -136,7 +141,7 @@ module RuboCop
136
141
  end
137
142
 
138
143
  def check_if_node(node)
139
- return if node.modifier_form? || node.ternary?
144
+ return if node.ternary?
140
145
 
141
146
  check_branch(node.if_branch)
142
147
  check_branch(node.else_branch)
@@ -77,6 +77,8 @@ module RuboCop
77
77
  private
78
78
 
79
79
  def nil_node_at_the_end_of_method_body(body)
80
+ return body if body.nil_type?
81
+ return unless body.begin_type?
80
82
  return unless (last_child = body.children.last)
81
83
 
82
84
  last_child if last_child.is_a?(AST::Node) && last_child.nil_type?
@@ -3,22 +3,42 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks that quotes inside the string interpolation
6
+ # Checks that quotes inside string, symbol, and regexp interpolations
7
7
  # match the configured preference.
8
8
  #
9
9
  # @example EnforcedStyle: single_quotes (default)
10
10
  # # bad
11
- # result = "Tests #{success ? "PASS" : "FAIL"}"
11
+ # string = "Tests #{success ? "PASS" : "FAIL"}"
12
+ # symbol = :"Tests #{success ? "PASS" : "FAIL"}"
13
+ # heredoc = <<~TEXT
14
+ # Tests #{success ? "PASS" : "FAIL"}
15
+ # TEXT
16
+ # regexp = /Tests #{success ? "PASS" : "FAIL"}/
12
17
  #
13
18
  # # good
14
- # result = "Tests #{success ? 'PASS' : 'FAIL'}"
19
+ # string = "Tests #{success ? 'PASS' : 'FAIL'}"
20
+ # symbol = :"Tests #{success ? 'PASS' : 'FAIL'}"
21
+ # heredoc = <<~TEXT
22
+ # Tests #{success ? 'PASS' : 'FAIL'}
23
+ # TEXT
24
+ # regexp = /Tests #{success ? 'PASS' : 'FAIL'}/
15
25
  #
16
26
  # @example EnforcedStyle: double_quotes
17
27
  # # bad
18
- # result = "Tests #{success ? 'PASS' : 'FAIL'}"
28
+ # string = "Tests #{success ? 'PASS' : 'FAIL'}"
29
+ # symbol = :"Tests #{success ? 'PASS' : 'FAIL'}"
30
+ # heredoc = <<~TEXT
31
+ # Tests #{success ? 'PASS' : 'FAIL'}
32
+ # TEXT
33
+ # regexp = /Tests #{success ? 'PASS' : 'FAIL'}/
19
34
  #
20
35
  # # good
21
- # result = "Tests #{success ? "PASS" : "FAIL"}"
36
+ # string = "Tests #{success ? "PASS" : "FAIL"}"
37
+ # symbol = :"Tests #{success ? "PASS" : "FAIL"}"
38
+ # heredoc = <<~TEXT
39
+ # Tests #{success ? "PASS" : "FAIL"}
40
+ # TEXT
41
+ # regexp = /Tests #{success ? "PASS" : "FAIL"}/
22
42
  class StringLiteralsInInterpolation < Base
23
43
  include ConfigurableEnforcedStyle
24
44
  include StringLiteralsHelp
@@ -29,6 +49,11 @@ module RuboCop
29
49
  StringLiteralCorrector.correct(corrector, node, style)
30
50
  end
31
51
 
52
+ # Cop classes that include the StringHelp module usually ignore regexp
53
+ # nodes. Not so for this cop, which is why we override the on_regexp
54
+ # definition with an empty one.
55
+ def on_regexp(node); end
56
+
32
57
  private
33
58
 
34
59
  def message(_node)
@@ -69,9 +69,9 @@ module RuboCop
69
69
 
70
70
  def complex_content?(node)
71
71
  node.children.any? do |sym|
72
- content, = *sym
73
- content = content.to_s
74
- content_without_delimiter_pairs = content.gsub(/(\[\])|(\(\))/, '')
72
+ content = *sym
73
+ content = content.map { |c| c.is_a?(AST::Node) ? c.source : c }.join
74
+ content_without_delimiter_pairs = content.gsub(/(\[[^\s\[\]]*\])|(\([^\s\(\)]*\))/, '')
75
75
 
76
76
  content.include?(' ') || DELIMITERS.any? do |delimiter|
77
77
  content_without_delimiter_pairs.include?(delimiter)
@@ -119,9 +119,7 @@ module RuboCop
119
119
  end
120
120
 
121
121
  def for_assignment_node
122
- return nil unless node.parent&.for_type?
123
-
124
- node.parent
122
+ node.ancestors.find(&:for_type?)
125
123
  end
126
124
 
127
125
  def find_multiple_assignment_node(grandparent_node)
@@ -112,23 +112,29 @@ module RuboCop
112
112
  end
113
113
 
114
114
  handle 'workspace/executeCommand' do |request|
115
- if request[:params][:command] == 'rubocop.formatAutocorrects'
116
- uri = request[:params][:arguments][0][:uri]
117
- @server.write(
118
- id: request[:id],
119
- method: 'workspace/applyEdit',
120
- params: {
121
- label: 'Format with RuboCop autocorrects',
122
- edit: {
123
- changes: {
124
- uri => format_file(uri)
125
- }
126
- }
127
- }
128
- )
115
+ case (command = request[:params][:command])
116
+ when 'rubocop.formatAutocorrects'
117
+ label = 'Format with RuboCop autocorrects'
118
+ when 'rubocop.formatAutocorrectsAll'
119
+ label = 'Format all with RuboCop autocorrects'
129
120
  else
130
- handle_unsupported_method(request, request[:params][:command])
121
+ handle_unsupported_method(request, command)
122
+ return
131
123
  end
124
+
125
+ uri = request[:params][:arguments][0][:uri]
126
+ @server.write(
127
+ id: request[:id],
128
+ method: 'workspace/applyEdit',
129
+ params: {
130
+ label: label,
131
+ edit: {
132
+ changes: {
133
+ uri => format_file(uri, command: command)
134
+ }
135
+ }
136
+ }
137
+ )
132
138
  end
133
139
 
134
140
  handle 'textDocument/willSave' do |_request|
@@ -176,14 +182,14 @@ module RuboCop
176
182
  }
177
183
  end
178
184
 
179
- def format_file(file_uri)
185
+ def format_file(file_uri, command: nil)
180
186
  unless (text = @text_cache[file_uri])
181
187
  Logger.log("Format request arrived before text synchronized; skipping: `#{file_uri}'")
182
188
 
183
189
  return []
184
190
  end
185
191
 
186
- new_text = @server.format(remove_file_protocol_from(file_uri), text)
192
+ new_text = @server.format(remove_file_protocol_from(file_uri), text, command: command)
187
193
 
188
194
  return [] if new_text == text
189
195
 
@@ -35,9 +35,15 @@ module RuboCop
35
35
  # https://github.com/rubocop/rubocop/blob/v1.52.0/lib/rubocop/cli/command/execute_runner.rb#L95
36
36
  # Setting `parallel: true` would break this here:
37
37
  # https://github.com/rubocop/rubocop/blob/v1.52.0/lib/rubocop/runner.rb#L72
38
- def format(path, text)
38
+ def format(path, text, command:)
39
+ safe_autocorrect = if command
40
+ command == 'rubocop.formatAutocorrects'
41
+ else
42
+ @safe_autocorrect
43
+ end
44
+
39
45
  formatting_options = {
40
- stdin: text, force_exclusion: true, autocorrect: true, safe_autocorrect: @safe_autocorrect
46
+ stdin: text, force_exclusion: true, autocorrect: true, safe_autocorrect: safe_autocorrect
41
47
  }
42
48
  formatting_options[:only] = config_only_options if @lint_mode || @layout_mode
43
49
 
@@ -45,8 +45,8 @@ module RuboCop
45
45
  @writer.write(response)
46
46
  end
47
47
 
48
- def format(path, text)
49
- @runtime.format(path, text)
48
+ def format(path, text, command:)
49
+ @runtime.format(path, text, command: command)
50
50
  end
51
51
 
52
52
  def offenses(path, text)
@@ -421,10 +421,10 @@ module RuboCop
421
421
  end
422
422
 
423
423
  def considered_failure?(offense)
424
- # For :autocorrect level, any offense - corrected or not - is a failure.
425
424
  return false if offense.disabled?
426
425
 
427
- return true if @options[:fail_level] == :autocorrect
426
+ # For :autocorrect level, any correctable offense is a failure, regardless of severity
427
+ return true if @options[:fail_level] == :autocorrect && offense.correctable?
428
428
 
429
429
  !offense.corrected? && offense.severity >= minimum_severity_to_fail
430
430
  end
@@ -461,7 +461,9 @@ module RuboCop
461
461
  @minimum_severity_to_fail ||= begin
462
462
  # Unless given explicitly as `fail_level`, `:info` severity offenses do not fail
463
463
  name = @options[:fail_level] || :refactor
464
- RuboCop::Cop::Severity.new(name)
464
+
465
+ # autocorrect is a fake level - use the default
466
+ RuboCop::Cop::Severity.new(name == :autocorrect ? :refactor : name)
465
467
  end
466
468
  end
467
469
 
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.55.0'
6
+ STRING = '1.56.0'
7
7
 
8
8
  MSG = '%<version>s (using Parser %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
data/lib/rubocop.rb CHANGED
@@ -162,6 +162,7 @@ require_relative 'rubocop/cop/correctors/string_literal_corrector'
162
162
  require_relative 'rubocop/cop/correctors/unused_arg_corrector'
163
163
 
164
164
  require_relative 'rubocop/cop/bundler/duplicated_gem'
165
+ require_relative 'rubocop/cop/bundler/duplicated_group'
165
166
  require_relative 'rubocop/cop/bundler/gem_comment'
166
167
  require_relative 'rubocop/cop/bundler/gem_filename'
167
168
  require_relative 'rubocop/cop/bundler/gem_version'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.55.0
4
+ version: 1.56.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,8 +10,22 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2023-07-25 00:00:00.000000000 Z
13
+ date: 2023-08-09 00:00:00.000000000 Z
14
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: base64
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: 0.1.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: 0.1.1
15
29
  - !ruby/object:Gem::Dependency
16
30
  name: json
17
31
  requirement: !ruby/object:Gem::Requirement
@@ -240,6 +254,7 @@ files:
240
254
  - lib/rubocop/cop/badge.rb
241
255
  - lib/rubocop/cop/base.rb
242
256
  - lib/rubocop/cop/bundler/duplicated_gem.rb
257
+ - lib/rubocop/cop/bundler/duplicated_group.rb
243
258
  - lib/rubocop/cop/bundler/gem_comment.rb
244
259
  - lib/rubocop/cop/bundler/gem_filename.rb
245
260
  - lib/rubocop/cop/bundler/gem_version.rb
@@ -1023,7 +1038,7 @@ metadata:
1023
1038
  homepage_uri: https://rubocop.org/
1024
1039
  changelog_uri: https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md
1025
1040
  source_code_uri: https://github.com/rubocop/rubocop/
1026
- documentation_uri: https://docs.rubocop.org/rubocop/1.55/
1041
+ documentation_uri: https://docs.rubocop.org/rubocop/1.56/
1027
1042
  bug_tracker_uri: https://github.com/rubocop/rubocop/issues
1028
1043
  rubygems_mfa_required: 'true'
1029
1044
  post_install_message: