rubocop 1.75.8 → 1.80.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 (131) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -16
  3. data/config/default.yml +107 -26
  4. data/config/obsoletion.yml +6 -3
  5. data/exe/rubocop +1 -8
  6. data/lib/rubocop/cli.rb +17 -1
  7. data/lib/rubocop/config_loader.rb +1 -38
  8. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  9. data/lib/rubocop/cop/correctors/alignment_corrector.rb +6 -3
  10. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
  11. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
  12. data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
  13. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +0 -22
  14. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  15. data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
  16. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  17. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
  18. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +1 -0
  19. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
  20. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
  21. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  22. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +101 -0
  23. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -1
  24. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +8 -29
  25. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +1 -1
  26. data/lib/rubocop/cop/layout/line_length.rb +26 -5
  27. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -0
  28. data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
  29. data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
  30. data/lib/rubocop/cop/layout/space_before_brackets.rb +2 -9
  31. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -2
  32. data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
  33. data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
  34. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
  35. data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
  36. data/lib/rubocop/cop/lint/float_comparison.rb +4 -4
  37. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  38. data/lib/rubocop/cop/lint/literal_as_condition.rb +34 -28
  39. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -2
  40. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
  41. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  42. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
  43. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
  44. data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
  45. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  46. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
  47. data/lib/rubocop/cop/lint/self_assignment.rb +30 -4
  48. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
  49. data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
  50. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
  51. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
  52. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  53. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
  54. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
  55. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  56. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
  57. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  58. data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
  59. data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
  60. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  61. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  62. data/lib/rubocop/cop/naming/method_name.rb +127 -13
  63. data/lib/rubocop/cop/naming/predicate_method.rb +306 -0
  64. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -4
  65. data/lib/rubocop/cop/security/eval.rb +2 -1
  66. data/lib/rubocop/cop/security/open.rb +1 -0
  67. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
  68. data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
  69. data/lib/rubocop/cop/style/arguments_forwarding.rb +11 -17
  70. data/lib/rubocop/cop/style/array_intersect.rb +98 -34
  71. data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
  72. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  73. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  74. data/lib/rubocop/cop/style/collection_querying.rb +167 -0
  75. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -2
  76. data/lib/rubocop/cop/style/dig_chain.rb +1 -1
  77. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  78. data/lib/rubocop/cop/style/exponential_notation.rb +3 -2
  79. data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
  80. data/lib/rubocop/cop/style/hash_conversion.rb +16 -8
  81. data/lib/rubocop/cop/style/if_unless_modifier.rb +13 -6
  82. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  83. data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
  84. data/lib/rubocop/cop/style/it_assignment.rb +69 -12
  85. data/lib/rubocop/cop/style/it_block_parameter.rb +36 -15
  86. data/lib/rubocop/cop/style/map_to_hash.rb +1 -3
  87. data/lib/rubocop/cop/style/map_to_set.rb +1 -3
  88. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -6
  89. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
  90. data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
  91. data/lib/rubocop/cop/style/parallel_assignment.rb +32 -20
  92. data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
  93. data/lib/rubocop/cop/style/redundant_begin.rb +34 -0
  94. data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
  95. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
  96. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  97. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  98. data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
  99. data/lib/rubocop/cop/style/redundant_parentheses.rb +42 -6
  100. data/lib/rubocop/cop/style/redundant_self.rb +8 -5
  101. data/lib/rubocop/cop/style/safe_navigation.rb +38 -12
  102. data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
  103. data/lib/rubocop/cop/style/sole_nested_conditional.rb +32 -2
  104. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
  105. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  106. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  107. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  108. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  109. data/lib/rubocop/cop/variable_force/variable.rb +1 -1
  110. data/lib/rubocop/cop/variable_force.rb +25 -8
  111. data/lib/rubocop/cops_documentation_generator.rb +1 -0
  112. data/lib/rubocop/formatter/disabled_config_formatter.rb +18 -5
  113. data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
  114. data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
  115. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  116. data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
  117. data/lib/rubocop/lsp/diagnostic.rb +4 -4
  118. data/lib/rubocop/lsp/routes.rb +35 -6
  119. data/lib/rubocop/pending_cops_reporter.rb +56 -0
  120. data/lib/rubocop/result_cache.rb +14 -12
  121. data/lib/rubocop/rspec/expect_offense.rb +9 -3
  122. data/lib/rubocop/runner.rb +6 -4
  123. data/lib/rubocop/server/cache.rb +4 -2
  124. data/lib/rubocop/server/client_command/base.rb +10 -0
  125. data/lib/rubocop/server/client_command/exec.rb +2 -1
  126. data/lib/rubocop/server/client_command/start.rb +11 -1
  127. data/lib/rubocop/target_finder.rb +9 -9
  128. data/lib/rubocop/version.rb +1 -1
  129. data/lib/rubocop.rb +11 -1
  130. data/lib/ruby_lsp/rubocop/addon.rb +2 -2
  131. metadata +17 -7
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Check for uses of `Object#freeze` on immutable objects.
6
+ # Checks for uses of `Object#freeze` on immutable objects.
7
7
  #
8
8
  # NOTE: `Regexp` and `Range` literals are frozen objects since Ruby 3.0.
9
9
  #
@@ -130,7 +130,7 @@ module RuboCop
130
130
  end
131
131
 
132
132
  def require_parentheses?(node)
133
- node.send_type? && !node.arguments.count.zero? && !node.parenthesized_call?
133
+ node.send_type? && node.arguments.any? && !node.parenthesized_call?
134
134
  end
135
135
  end
136
136
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Check for redundant line continuation.
6
+ # Checks for redundant line continuation.
7
7
  #
8
8
  # This cop marks a line continuation as redundant if removing the backslash
9
9
  # does not result in a syntax error.
@@ -49,6 +49,7 @@ module RuboCop
49
49
  empty_parentheses?(node) ||
50
50
  first_arg_begins_with_hash_literal?(node) ||
51
51
  rescue?(node) ||
52
+ in_pattern_matching_in_method_argument?(node) ||
52
53
  allowed_pin_operator?(node) ||
53
54
  allowed_expression?(node)
54
55
  end
@@ -122,6 +123,13 @@ module RuboCop
122
123
  hash_literal && first_argument?(node) && !parentheses?(hash_literal) && !parenthesized
123
124
  end
124
125
 
126
+ def in_pattern_matching_in_method_argument?(begin_node)
127
+ return false unless begin_node.parent&.call_type?
128
+ return false unless (node = begin_node.children.first)
129
+
130
+ target_ruby_version <= 2.7 ? node.match_pattern_type? : node.match_pattern_p_type?
131
+ end
132
+
125
133
  def method_chain_begins_with_hash_literal(node)
126
134
  return if node.nil?
127
135
  return node if node.hash_type?
@@ -134,14 +142,14 @@ module RuboCop
134
142
  node = begin_node.children.first
135
143
 
136
144
  if (message = find_offense_message(begin_node, node))
137
- if node.range_type? && !argument_of_parenthesized_method_call?(begin_node)
145
+ if node.range_type? && !argument_of_parenthesized_method_call?(begin_node, node)
138
146
  begin_node = begin_node.parent
139
147
  end
140
148
 
141
149
  return offense(begin_node, message)
142
150
  end
143
151
 
144
- check_send(begin_node, node) if node.call_type?
152
+ check_send(begin_node, node) if call_node?(node)
145
153
  end
146
154
 
147
155
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
@@ -156,8 +164,12 @@ module RuboCop
156
164
  if node.lambda_or_proc? && (node.braces? || node.send_node.lambda_literal?)
157
165
  return 'an expression'
158
166
  end
167
+ if disallowed_one_line_pattern_matching?(begin_node, node)
168
+ return 'a one-line pattern matching'
169
+ end
159
170
  return 'an interpolated expression' if interpolation?(begin_node)
160
- return 'a method argument' if argument_of_parenthesized_method_call?(begin_node)
171
+ return 'a method argument' if argument_of_parenthesized_method_call?(begin_node, node)
172
+ return 'a one-line rescue' if oneline_rescue_parentheses_required?(begin_node, node)
161
173
 
162
174
  return if begin_node.chained?
163
175
 
@@ -180,14 +192,23 @@ module RuboCop
180
192
  # @!method interpolation?(node)
181
193
  def_node_matcher :interpolation?, '[^begin ^^dstr]'
182
194
 
183
- def argument_of_parenthesized_method_call?(begin_node)
184
- node = begin_node.children.first
185
- return false if node.basic_conditional? || method_call_parentheses_required?(node)
195
+ def argument_of_parenthesized_method_call?(begin_node, node)
196
+ if node.basic_conditional? || node.rescue_type? || method_call_parentheses_required?(node)
197
+ return false
198
+ end
186
199
  return false unless (parent = begin_node.parent)
187
200
 
188
201
  parent.call_type? && parent.parenthesized? && parent.receiver != begin_node
189
202
  end
190
203
 
204
+ def oneline_rescue_parentheses_required?(begin_node, node)
205
+ return false unless node.rescue_type?
206
+ return false unless (parent = begin_node.parent)
207
+ return false if parent.if_type? && parent.ternary?
208
+
209
+ !parent.type?(:call, :array, :pair)
210
+ end
211
+
191
212
  def method_call_parentheses_required?(node)
192
213
  return false unless node.call_type?
193
214
 
@@ -198,7 +219,13 @@ module RuboCop
198
219
  !!config.for_enabled_cop('Style/ParenthesesAroundCondition')['AllowInMultilineConditions']
199
220
  end
200
221
 
222
+ def call_node?(node)
223
+ node.call_type? || (node.any_block_type? && node.braces? && !node.lambda_or_proc?)
224
+ end
225
+
201
226
  def check_send(begin_node, node)
227
+ node = node.send_node if node.any_block_type?
228
+
202
229
  return check_unary(begin_node, node) if node.unary_operation?
203
230
 
204
231
  return unless method_call_with_redundant_parentheses?(node)
@@ -242,6 +269,15 @@ module RuboCop
242
269
  end
243
270
  end
244
271
 
272
+ def disallowed_one_line_pattern_matching?(begin_node, node)
273
+ if (parent = begin_node.parent)
274
+ return false if parent.any_def_type? && parent.endless?
275
+ return false if parent.assignment?
276
+ end
277
+
278
+ node.any_match_pattern_type? && node.each_ancestor.none?(&:operator_keyword?)
279
+ end
280
+
245
281
  def raised_to_power_negative_numeric?(begin_node, node)
246
282
  return false unless node.numeric_type?
247
283
 
@@ -67,6 +67,9 @@ module RuboCop
67
67
 
68
68
  def on_or_asgn(node)
69
69
  allow_self(node.lhs)
70
+
71
+ lhs_name = node.lhs.lvasgn_type? ? node.lhs.name : node.lhs
72
+ add_lhs_to_local_variables_scopes(node.rhs, lhs_name)
70
73
  end
71
74
  alias on_and_asgn on_or_asgn
72
75
 
@@ -123,11 +126,11 @@ module RuboCop
123
126
  def on_if(node)
124
127
  # Allow conditional nodes to use `self` in the condition if that variable
125
128
  # name is used in an `lvasgn` or `masgn` within the `if`.
126
- node.child_nodes.each do |child_node|
127
- if child_node.lvasgn_type?
128
- add_lhs_to_local_variables_scopes(node.condition, child_node.lhs)
129
- elsif child_node.masgn_type?
130
- add_masgn_lhs_variables(node.condition, child_node.lhs)
129
+ node.each_descendant(:lvasgn, :masgn) do |descendant_node|
130
+ if descendant_node.lvasgn_type?
131
+ add_lhs_to_local_variables_scopes(node.condition, descendant_node.lhs)
132
+ else
133
+ add_masgn_lhs_variables(node.condition, descendant_node.lhs)
131
134
  end
132
135
  end
133
136
  end
@@ -86,6 +86,10 @@ module RuboCop
86
86
  # foo.baz = bar if foo
87
87
  # foo.baz + bar if foo
88
88
  # foo.bar > 2 if foo
89
+ #
90
+ # foo ? foo[index] : nil # Ignored `foo&.[](index)` due to unclear readability benefit.
91
+ # foo ? foo[idx] = v : nil # Ignored `foo&.[]=(idx, v)` due to unclear readability benefit.
92
+ # foo ? foo * 42 : nil # Ignored `foo&.*(42)` due to unclear readability benefit.
89
93
  class SafeNavigation < Base # rubocop:disable Metrics/ClassLength
90
94
  include NilMethods
91
95
  include RangeHelp
@@ -138,6 +142,7 @@ module RuboCop
138
142
  # @!method strip_begin(node)
139
143
  def_node_matcher :strip_begin, '{ (begin $!begin) $!(begin) }'
140
144
 
145
+ # rubocop:disable Metrics/AbcSize
141
146
  def on_if(node)
142
147
  return if allowed_if_condition?(node)
143
148
 
@@ -146,13 +151,16 @@ module RuboCop
146
151
 
147
152
  body = extract_if_body(node)
148
153
  method_call = receiver.parent
154
+ return if dotless_operator_call?(method_call) || method_call.double_colon?
149
155
 
150
156
  removal_ranges = [begin_range(node, body), end_range(node, body)]
151
157
 
152
158
  report_offense(node, method_chain, method_call, *removal_ranges) do |corrector|
159
+ corrector.replace(receiver, checked_variable.source) if checked_variable.csend_type?
153
160
  corrector.insert_before(method_call.loc.dot, '&') unless method_call.safe_navigation?
154
161
  end
155
162
  end
163
+ # rubocop:enable Metrics/AbcSize
156
164
 
157
165
  def on_and(node) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
158
166
  collect_and_clauses(node).each do |(lhs, lhs_operator_range), (rhs, _rhs_operator_range)|
@@ -181,6 +189,8 @@ module RuboCop
181
189
  end
182
190
  end
183
191
 
192
+ private
193
+
184
194
  def report_offense(node, rhs, rhs_receiver, *removal_ranges, offense_range: node)
185
195
  add_offense(offense_range) do |corrector|
186
196
  next if ignored_node?(node)
@@ -198,8 +208,6 @@ module RuboCop
198
208
  end
199
209
  end
200
210
 
201
- private
202
-
203
211
  def find_method_chain(node)
204
212
  return node unless node&.parent&.call_type?
205
213
 
@@ -235,7 +243,7 @@ module RuboCop
235
243
  return false if !matching_nodes?(lhs_receiver, rhs_receiver) || rhs_receiver.nil?
236
244
  return false if use_var_only_in_unless_modifier?(node, lhs_receiver)
237
245
  return false if chain_length(rhs, rhs_receiver) > max_chain_length
238
- return false if unsafe_method_used?(rhs, rhs_receiver.parent)
246
+ return false if unsafe_method_used?(node, rhs, rhs_receiver.parent)
239
247
  return false if rhs.send_type? && rhs.method?(:empty?)
240
248
 
241
249
  true
@@ -253,6 +261,14 @@ module RuboCop
253
261
  end
254
262
  end
255
263
 
264
+ def dotless_operator_call?(method_call)
265
+ method_call = method_call.parent while method_call.parent.send_type?
266
+
267
+ return false if method_call.loc.dot
268
+
269
+ method_call.method?(:[]) || method_call.method?(:[]=) || method_call.operator_method?
270
+ end
271
+
256
272
  def handle_comments(corrector, node, method_call)
257
273
  comments = comments(node)
258
274
  return if comments.empty?
@@ -322,8 +338,16 @@ module RuboCop
322
338
 
323
339
  def matching_call_nodes?(left, right)
324
340
  return false unless left && right.respond_to?(:call_type?)
341
+ return false unless left.call_type? && right.call_type?
325
342
 
326
- left.call_type? && right.call_type? && left.children == right.children
343
+ # Compare receiver and method name, but ignore the difference between
344
+ # safe navigation method call (`&.`) and dot method call (`.`).
345
+ left_receiver, left_method, *left_args = left.children
346
+ right_receiver, right_method, *right_args = right.children
347
+
348
+ left_method == right_method &&
349
+ matching_nodes?(left_receiver, right_receiver) &&
350
+ left_args == right_args
327
351
  end
328
352
 
329
353
  def chain_length(method_chain, method)
@@ -334,21 +358,24 @@ module RuboCop
334
358
  end
335
359
  end
336
360
 
337
- def unsafe_method_used?(method_chain, method)
338
- return true if unsafe_method?(method)
361
+ def unsafe_method_used?(node, method_chain, method)
362
+ return true if unsafe_method?(node, method)
339
363
 
340
364
  method.each_ancestor(:send).any? do |ancestor|
341
365
  break true unless config.cop_enabled?('Lint/SafeNavigationChain')
342
366
 
343
- break true if unsafe_method?(ancestor)
367
+ break true if unsafe_method?(node, ancestor)
344
368
  break true if nil_methods.include?(ancestor.method_name)
345
369
  break false if ancestor == method_chain
346
370
  end
347
371
  end
348
372
 
349
- def unsafe_method?(send_node)
350
- negated?(send_node) ||
351
- send_node.assignment? ||
373
+ def unsafe_method?(node, send_node)
374
+ return true if negated?(send_node)
375
+
376
+ return false if node.respond_to?(:ternary?) && node.ternary?
377
+
378
+ send_node.assignment? ||
352
379
  (!send_node.dot? && !send_node.safe_navigation?)
353
380
  end
354
381
 
@@ -377,8 +404,7 @@ module RuboCop
377
404
  method_chain)
378
405
  start_method.each_ancestor do |ancestor|
379
406
  break unless %i[send block].include?(ancestor.type)
380
- next unless ancestor.send_type?
381
- next if ancestor.safe_navigation?
407
+ next if !ancestor.send_type? || ancestor.operator_method?
382
408
 
383
409
  corrector.insert_before(ancestor.loc.dot, '&')
384
410
 
@@ -65,7 +65,7 @@ module RuboCop
65
65
  return false if target_ruby_version < 3.0
66
66
  return false if disallow_endless_method_style?
67
67
  return false unless body_node
68
- return false if body_node.parent.assignment_method? ||
68
+ return false if body_node.basic_conditional? || body_node.parent.assignment_method? ||
69
69
  NOT_SUPPORTED_ENDLESS_METHOD_BODY_TYPES.include?(body_node.type)
70
70
 
71
71
  !body_node.type?(:begin, :kwbegin)
@@ -86,10 +86,10 @@ module RuboCop
86
86
  end
87
87
 
88
88
  def correct_to_endless(corrector, node)
89
- self_receiver = node.self_receiver? ? 'self.' : ''
89
+ receiver = "#{node.receiver.source}." if node.receiver
90
90
  arguments = node.arguments.any? ? node.arguments.source : '()'
91
91
  body_source = method_body_source(node.body)
92
- replacement = "def #{self_receiver}#{node.method_name}#{arguments} = #{body_source}"
92
+ replacement = "def #{receiver}#{node.method_name}#{arguments} = #{body_source}"
93
93
 
94
94
  corrector.replace(node, replacement)
95
95
  end
@@ -130,7 +130,10 @@ module RuboCop
130
130
  end
131
131
 
132
132
  def require_parentheses?(method_body)
133
- method_body.send_type? && !method_body.arguments.empty? && !method_body.comparison_method?
133
+ return false unless method_body.send_type?
134
+ return false if method_body.arithmetic_operation?
135
+
136
+ !method_body.arguments.empty? && !method_body.comparison_method?
134
137
  end
135
138
 
136
139
  def disallow_endless_method_style?
@@ -115,8 +115,9 @@ module RuboCop
115
115
  end
116
116
 
117
117
  def correct_node(corrector, node)
118
- corrector.replace(node.loc.keyword, 'if') if node.unless?
118
+ corrector.replace(node.loc.keyword, 'if') if node.unless? && !part_of_ignored_node?(node)
119
119
  corrector.replace(node.condition, chainable_condition(node))
120
+ ignore_node(node)
120
121
  end
121
122
 
122
123
  def correct_for_guard_condition_style(corrector, node, if_branch)
@@ -174,6 +175,8 @@ module RuboCop
174
175
 
175
176
  if parenthesize_method?(condition)
176
177
  parenthesized_method_arguments(condition)
178
+ elsif condition.and_type?
179
+ parenthesized_and(condition)
177
180
  else
178
181
  "(#{condition.source})"
179
182
  end
@@ -185,12 +188,19 @@ module RuboCop
185
188
  end
186
189
 
187
190
  def add_parentheses?(node)
188
- return true if node.assignment? || (node.operator_keyword? && !node.and_type?)
191
+ return true if node.assignment? || node.or_type?
192
+ return true if assignment_in_and?(node)
189
193
  return false unless node.call_type?
190
194
 
191
195
  (node.arguments.any? && !node.parenthesized?) || node.prefix_not?
192
196
  end
193
197
 
198
+ def assignment_in_and?(node)
199
+ return false unless node.and_type?
200
+
201
+ node.each_descendant.any?(&:assignment?)
202
+ end
203
+
194
204
  def parenthesized_method_arguments(node)
195
205
  method_call = node.source_range.begin.join(node.loc.selector.end).source
196
206
  arguments = node.first_argument.source_range.begin.join(node.source_range.end).source
@@ -198,6 +208,26 @@ module RuboCop
198
208
  "#{method_call}(#{arguments})"
199
209
  end
200
210
 
211
+ def parenthesized_and(node)
212
+ # We only need to add parentheses around the last clause if it's an assignment,
213
+ # because other clauses will be unchanged by merging conditionals.
214
+ lhs = node.lhs.source
215
+ rhs = parenthesized_and_clause(node.rhs)
216
+ operator = range_with_surrounding_space(node.loc.operator, whitespace: true).source
217
+
218
+ "#{lhs}#{operator}#{rhs}"
219
+ end
220
+
221
+ def parenthesized_and_clause(node)
222
+ if node.and_type?
223
+ parenthesized_and(node)
224
+ elsif node.assignment?
225
+ "(#{node.source})"
226
+ else
227
+ node.source
228
+ end
229
+ end
230
+
201
231
  def allow_modifier?
202
232
  cop_config['AllowModifier']
203
233
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Check for parentheses around stabby lambda arguments.
6
+ # Checks for parentheses around stabby lambda arguments.
7
7
  # There are two different styles. Defaults to `require_parentheses`.
8
8
  #
9
9
  # @example EnforcedStyle: require_parentheses (default)
@@ -100,7 +100,7 @@ module RuboCop
100
100
  node.receiver.str_type? &&
101
101
  node.first_argument.str_type? &&
102
102
  node.multiline? &&
103
- node.source =~ /\+\s*\n/
103
+ node.source.match?(/\+\s*\n/)
104
104
  end
105
105
 
106
106
  def find_topmost_plus_node(node)
@@ -81,7 +81,7 @@ module RuboCop
81
81
 
82
82
  content = *sym
83
83
  content = content.map { |c| c.is_a?(AST::Node) ? c.source : c }.join
84
- content_without_delimiter_pairs = content.gsub(/(\[[^\s\[\]]*\])|(\([^\s\(\)]*\))/, '')
84
+ content_without_delimiter_pairs = content.gsub(/(\[[^\s\[\]]*\])|(\([^\s()]*\))/, '')
85
85
 
86
86
  content.include?(' ') || DELIMITERS.any? do |delimiter|
87
87
  content_without_delimiter_pairs.include?(delimiter)
@@ -270,7 +270,7 @@ module RuboCop
270
270
  end
271
271
 
272
272
  def allow_if_method_has_argument?(send_node)
273
- !!cop_config.fetch('AllowMethodsWithArguments', false) && !send_node.arguments.count.zero?
273
+ !!cop_config.fetch('AllowMethodsWithArguments', false) && send_node.arguments.any?
274
274
  end
275
275
 
276
276
  def allow_comments?
@@ -97,7 +97,7 @@ module RuboCop
97
97
  pipes = tokens.select { |token| token.type == :tPIPE }
98
98
  begin_pos, end_pos = pipes.map { |pipe| tokens.index(pipe) }
99
99
 
100
- tokens[begin_pos + 1..end_pos - 1]
100
+ tokens[(begin_pos + 1)..(end_pos - 1)]
101
101
  end
102
102
  end
103
103
  end
@@ -79,7 +79,7 @@ module RuboCop
79
79
  parent = parent.parent if parent&.begin_type?
80
80
  return false if parent.nil?
81
81
 
82
- parent.type?(:if, :while, :until) && parent.modifier_form?
82
+ parent.basic_conditional? && parent.modifier_form?
83
83
  end
84
84
 
85
85
  def capture_with_block!
@@ -71,6 +71,8 @@ module RuboCop
71
71
  end
72
72
  end
73
73
 
74
+ BRANCH_NODES = %i[if case case_match rescue].freeze
75
+
74
76
  def variable_table
75
77
  @variable_table ||= VariableTable.new(self)
76
78
  end
@@ -236,11 +238,16 @@ module RuboCop
236
238
  end
237
239
 
238
240
  def process_loop(node)
239
- if POST_CONDITION_LOOP_TYPES.include?(node.type)
241
+ if node.post_condition_loop?
240
242
  # See the comment at the end of file for this behavior.
241
243
  condition_node, body_node = *node
242
244
  process_node(body_node)
243
245
  process_node(condition_node)
246
+ elsif node.for_type?
247
+ # In `for item in items` the rightmost expression is evaluated first.
248
+ process_node(node.collection)
249
+ process_node(node.variable)
250
+ process_node(node.body) if node.body
244
251
  else
245
252
  process_children(node)
246
253
  end
@@ -296,7 +303,7 @@ module RuboCop
296
303
  variable_table.accessible_variables.each { |variable| variable.reference!(node) }
297
304
  end
298
305
 
299
- # Mark all assignments which are referenced in the same loop
306
+ # Mark last assignments which are referenced in the same loop
300
307
  # as referenced by ignoring AST order since they would be referenced
301
308
  # in next iteration.
302
309
  def mark_assignments_as_referenced_in_loop(node)
@@ -308,13 +315,12 @@ module RuboCop
308
315
  # would be skipped here.
309
316
  next unless variable
310
317
 
311
- variable.assignments.each do |assignment|
312
- next if assignment_nodes_in_loop.none? do |assignment_node|
313
- assignment_node.equal?(assignment.node)
314
- end
315
-
316
- assignment.reference!(node)
318
+ loop_assignments = variable.assignments.select do |assignment|
319
+ assignment_nodes_in_loop.include?(assignment.node)
317
320
  end
321
+ next unless loop_assignments.any?
322
+
323
+ reference_assignments(loop_assignments, node)
318
324
  end
319
325
  end
320
326
 
@@ -354,6 +360,17 @@ module RuboCop
354
360
  end
355
361
  end
356
362
 
363
+ def reference_assignments(loop_assignments, loop_node)
364
+ # If inside a branching statement, mark all as referenced.
365
+ # Otherwise, mark only the last assignment as referenced.
366
+ # Note that `rescue` must be considered as branching because of
367
+ # the `retry` keyword.
368
+ loop_assignments.each do |assignment|
369
+ assignment.reference!(loop_node) if assignment.node.each_ancestor(*BRANCH_NODES).any?
370
+ end
371
+ loop_assignments.last&.reference!(loop_node)
372
+ end
373
+
357
374
  def scanned_node?(node)
358
375
  scanned_nodes.include?(node)
359
376
  end
@@ -7,6 +7,7 @@ require 'yard'
7
7
  # @api private
8
8
  class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
9
9
  include ::RuboCop::Cop::Documentation
10
+
10
11
  CopData = Struct.new(
11
12
  :cop, :description, :example_objects, :safety_objects, :see_objects, :config, keyword_init: true
12
13
  )
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Formatter
5
5
  # This formatter displays a YAML configuration file where all cops that
6
6
  # detected any offenses are configured to not detect the offense.
7
- class DisabledConfigFormatter < BaseFormatter
7
+ class DisabledConfigFormatter < BaseFormatter # rubocop:disable Metrics/ClassLength
8
8
  include PathUtil
9
9
 
10
10
  HEADING = <<~COMMENTS
@@ -17,6 +17,22 @@ module RuboCop
17
17
  # versions of RuboCop, may require this file to be generated again.
18
18
  COMMENTS
19
19
 
20
+ EXCLUDED_CONFIG_KEYS = %w[
21
+ AutoCorrect
22
+ Description
23
+ Enabled
24
+ Exclude
25
+ Include
26
+ Reference
27
+ References
28
+ Safe
29
+ SafeAutoCorrect
30
+ StyleGuide
31
+ VersionAdded
32
+ VersionChanged
33
+ VersionRemoved
34
+ ].freeze
35
+
20
36
  @config_to_allow_offenses = {}
21
37
  @detected_styles = {}
22
38
 
@@ -163,10 +179,7 @@ module RuboCop
163
179
  end
164
180
 
165
181
  def cop_config_params(default_cfg, cfg)
166
- default_cfg.keys -
167
- %w[Description StyleGuide Reference References Enabled Exclude Safe
168
- SafeAutoCorrect VersionAdded VersionChanged VersionRemoved] -
169
- cfg.keys
182
+ default_cfg.keys - EXCLUDED_CONFIG_KEYS - cfg.keys
170
183
  end
171
184
 
172
185
  def output_cop_param_comments(output_buffer, params, default_cfg)
@@ -22,7 +22,7 @@ module RuboCop
22
22
 
23
23
  @severest_offense = nil
24
24
 
25
- file_phrase = target_files.count == 1 ? 'file' : 'files'
25
+ file_phrase = target_files.one? ? 'file' : 'files'
26
26
 
27
27
  # 185/407 files |====== 45 ======> | ETA: 00:00:04
28
28
  # %c / %C | %w > %i | %e
@@ -6,6 +6,7 @@ module RuboCop
6
6
  class MarkdownFormatter < BaseFormatter
7
7
  include TextUtil
8
8
  include PathUtil
9
+
9
10
  attr_reader :files, :summary
10
11
 
11
12
  def initialize(output, options = {})
@@ -24,7 +24,7 @@ module RuboCop
24
24
 
25
25
  return unless output.tty?
26
26
 
27
- file_phrase = target_files.count == 1 ? 'file' : 'files'
27
+ file_phrase = target_files.one? ? 'file' : 'files'
28
28
 
29
29
  # 185/407 files |====== 45 ======> | ETA: 00:00:04
30
30
  # %c / %C | %w > %i | %e
@@ -9,6 +9,7 @@ module RuboCop
9
9
  # https://github.com/go-labs/rspec_pacman_formatter
10
10
  class PacmanFormatter < ClangStyleFormatter
11
11
  include TextUtil
12
+
12
13
  attr_accessor :progress_line
13
14
 
14
15
  FALLBACK_TERMINAL_WIDTH = 80
@@ -79,7 +79,7 @@ module RuboCop
79
79
  LanguageServer::Protocol::Interface::CodeDescription.new(href: doc_url)
80
80
  end
81
81
 
82
- # rubocop:disable Layout/LineLength, Metrics/MethodLength
82
+ # rubocop:disable Metrics/MethodLength
83
83
  def autocorrect_action
84
84
  LanguageServer::Protocol::Interface::CodeAction.new(
85
85
  title: "Autocorrect #{@offense.cop_name}",
@@ -98,7 +98,7 @@ module RuboCop
98
98
  is_preferred: true
99
99
  )
100
100
  end
101
- # rubocop:enable Layout/LineLength, Metrics/MethodLength
101
+ # rubocop:enable Metrics/MethodLength
102
102
 
103
103
  # rubocop:disable Metrics/MethodLength
104
104
  def offense_replacements
@@ -120,7 +120,7 @@ module RuboCop
120
120
  end
121
121
  # rubocop:enable Metrics/MethodLength
122
122
 
123
- # rubocop:disable Layout/LineLength, Metrics/MethodLength
123
+ # rubocop:disable Metrics/MethodLength
124
124
  def disable_line_action
125
125
  LanguageServer::Protocol::Interface::CodeAction.new(
126
126
  title: "Disable #{@offense.cop_name} for this line",
@@ -138,7 +138,7 @@ module RuboCop
138
138
  )
139
139
  )
140
140
  end
141
- # rubocop:enable Layout/LineLength, Metrics/MethodLength
141
+ # rubocop:enable Metrics/MethodLength
142
142
 
143
143
  def line_disable_comment
144
144
  new_text = if @offense.source_line.include?(' # rubocop:disable ')