rubocop 1.92.0 → 1.92.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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +1 -0
  3. data/lib/rubocop/cop/badge.rb +1 -1
  4. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +16 -1
  5. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +2 -2
  6. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +34 -0
  7. data/lib/rubocop/cop/layout/argument_alignment.rb +5 -1
  8. data/lib/rubocop/cop/layout/class_structure.rb +21 -7
  9. data/lib/rubocop/cop/layout/dot_position.rb +25 -15
  10. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +11 -7
  11. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +9 -3
  12. data/lib/rubocop/cop/layout/end_alignment.rb +3 -7
  13. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -1
  14. data/lib/rubocop/cop/layout/first_argument_indentation.rb +26 -1
  15. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +15 -0
  16. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +11 -5
  17. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +5 -3
  18. data/lib/rubocop/cop/layout/indentation_consistency.rb +22 -5
  19. data/lib/rubocop/cop/layout/line_length.rb +104 -6
  20. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +6 -4
  21. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +16 -5
  22. data/lib/rubocop/cop/layout/single_line_block_chain.rb +3 -3
  23. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -12
  24. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +6 -1
  25. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +4 -0
  26. data/lib/rubocop/cop/lint/number_conversion.rb +2 -0
  27. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +47 -1
  28. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -0
  29. data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
  30. data/lib/rubocop/cop/mixin/check_line_breakable.rb +7 -6
  31. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +3 -1
  32. data/lib/rubocop/cop/mixin/endless_method_body_help.rb +36 -0
  33. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +9 -4
  34. data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -2
  35. data/lib/rubocop/cop/mixin/visibility_help.rb +2 -1
  36. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
  37. data/lib/rubocop/cop/style/access_modifier_declarations.rb +30 -2
  38. data/lib/rubocop/cop/style/and_or.rb +3 -3
  39. data/lib/rubocop/cop/style/arguments_forwarding.rb +28 -2
  40. data/lib/rubocop/cop/style/block_delimiters.rb +6 -0
  41. data/lib/rubocop/cop/style/class_and_module_children.rb +2 -0
  42. data/lib/rubocop/cop/style/class_methods_definitions.rb +7 -0
  43. data/lib/rubocop/cop/style/collection_querying.rb +79 -46
  44. data/lib/rubocop/cop/style/combinable_loops.rb +4 -0
  45. data/lib/rubocop/cop/style/concat_array_literals.rb +13 -1
  46. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +53 -11
  47. data/lib/rubocop/cop/style/endless_method.rb +5 -1
  48. data/lib/rubocop/cop/style/for.rb +14 -1
  49. data/lib/rubocop/cop/style/guard_clause.rb +22 -21
  50. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +4 -0
  51. data/lib/rubocop/cop/style/if_unless_modifier.rb +19 -12
  52. data/lib/rubocop/cop/style/it_block_parameter.rb +63 -2
  53. data/lib/rubocop/cop/style/lambda.rb +16 -1
  54. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +36 -4
  55. data/lib/rubocop/cop/style/mixin_grouping.rb +12 -3
  56. data/lib/rubocop/cop/style/mutable_constant.rb +25 -7
  57. data/lib/rubocop/cop/style/parentheses_around_condition.rb +1 -1
  58. data/lib/rubocop/cop/style/raise_args.rb +19 -9
  59. data/lib/rubocop/cop/style/redundant_begin.rb +2 -1
  60. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -2
  61. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +24 -1
  62. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +28 -0
  63. data/lib/rubocop/cop/style/redundant_sort.rb +38 -4
  64. data/lib/rubocop/cop/style/require_order.rb +16 -8
  65. data/lib/rubocop/cop/style/rescue_modifier.rb +6 -0
  66. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +4 -0
  67. data/lib/rubocop/cop/style/single_line_methods.rb +5 -0
  68. data/lib/rubocop/cop/style/struct_inheritance.rb +6 -0
  69. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  70. data/lib/rubocop/lsp/routes.rb +23 -23
  71. data/lib/rubocop/server/cache.rb +4 -4
  72. data/lib/rubocop/target_ruby.rb +21 -9
  73. data/lib/rubocop/version.rb +1 -1
  74. metadata +2 -2
@@ -85,7 +85,7 @@ module RuboCop
85
85
  /xi.freeze
86
86
 
87
87
  def self.autocorrect_incompatible_with
88
- [Style::MethodCallWithArgsParentheses]
88
+ [Style::MethodCallWithArgsParentheses, Style::MethodDefParentheses]
89
89
  end
90
90
 
91
91
  def on_block(node)
@@ -122,6 +122,7 @@ module RuboCop
122
122
  def on_new_investigation
123
123
  @breakable_range_by_line_index = {}
124
124
  @breakable_string_delimiters = {}
125
+ @breakable_strings = {}
125
126
  @endless_methods_by_line = {}
126
127
  @heredocs = nil
127
128
 
@@ -207,6 +208,7 @@ module RuboCop
207
208
  def check_for_breakable_node(node)
208
209
  breakable_node = extract_breakable_node(node, max)
209
210
  return if breakable_node.nil?
211
+ return if compact_nested_array_break?(node, breakable_node)
210
212
 
211
213
  line_index = breakable_node.first_line - 1
212
214
  range = breakable_node.source_range
@@ -217,6 +219,20 @@ module RuboCop
217
219
  breakable_range_by_line_index[line_index] = range
218
220
  end
219
221
 
222
+ def compact_nested_array_break?(node, breakable_node)
223
+ return false if !node.array_type? || !node.square_brackets?
224
+ return false unless breakable_node.equal?(node.children.first)
225
+ return false unless breakable_node.source.start_with?('[')
226
+
227
+ compact_array_brackets_enforced?
228
+ end
229
+
230
+ def compact_array_brackets_enforced?
231
+ return false unless config.cop_enabled?('Layout/SpaceInsideArrayLiteralBrackets')
232
+
233
+ config.for_cop('Layout/SpaceInsideArrayLiteralBrackets')['EnforcedStyle'] == 'compact'
234
+ end
235
+
220
236
  def check_for_breakable_semicolons(processed_source)
221
237
  tokens = processed_source.tokens.select { |t| t.type == :tSEMI }
222
238
  tokens.reverse_each do |token|
@@ -238,6 +254,7 @@ module RuboCop
238
254
  breakable_range_by_line_index[line_index] = range_between(pos, pos + 1)
239
255
  end
240
256
 
257
+ # rubocop:disable-next Metrics/AbcSize
241
258
  def check_for_breakable_str(node)
242
259
  line_index = node.loc.line - 1
243
260
  return if breakable_range_by_line_index[line_index]
@@ -248,6 +265,7 @@ module RuboCop
248
265
 
249
266
  breakable_range_by_line_index[line_index] = range_between(pos, pos + 1)
250
267
  breakable_string_delimiters[line_index] = delimiter
268
+ breakable_strings[line_index] = node
251
269
  end
252
270
 
253
271
  def check_for_breakable_dstr(node) # rubocop:disable Metrics/AbcSize
@@ -301,7 +319,32 @@ module RuboCop
301
319
  return unless (pos = breakable_string_range(node))
302
320
 
303
321
  content_begin_pos = node.loc?(:begin) ? node.loc.begin.end_pos : source_range.begin_pos
304
- pos.end_pos if pos.end_pos > content_begin_pos
322
+ if blank_left_part?(node, content_begin_pos, pos)
323
+ pos = breakable_string_range_at_limit(source_range)
324
+ end
325
+ pos.end_pos if pos && leaves_two_parts?(node, content_begin_pos, pos)
326
+ end
327
+
328
+ def leaves_two_parts?(node, content_begin_pos, pos)
329
+ pos.end_pos > content_begin_pos && !blank_rest?(node, pos.end_pos)
330
+ end
331
+
332
+ def blank_left_part?(node, content_begin_pos, pos)
333
+ return false unless pos.end_pos > content_begin_pos
334
+ return false unless starts_after_quote?(node)
335
+
336
+ blank_between?(content_begin_pos, pos.end_pos)
337
+ end
338
+
339
+ def starts_after_quote?(node)
340
+ return true if node.loc?(:begin)
341
+
342
+ quote = node.parent&.loc&.begin if node.parent&.dstr_type?
343
+ quote ? quote.end_pos == node.source_range.begin_pos : false
344
+ end
345
+
346
+ def blank_between?(begin_pos, end_pos)
347
+ range_between(begin_pos, end_pos).source.strip.empty?
305
348
  end
306
349
 
307
350
  # Locate where to break a string that is too long, ensuring that escape characters
@@ -325,11 +368,17 @@ module RuboCop
325
368
 
326
369
  def breakable_space_position(node, substr)
327
370
  limit = string_content_length(node) - 1
328
- space_pos = substr.rindex(/\s/)
329
- space_pos = substr[0, limit].rindex(/\s/) if space_pos && space_pos + 1 > limit
371
+ space_pos = space_before_word(node, substr.length - 1)
372
+ space_pos = space_before_word(node, limit - 1) if space_pos && space_pos + 1 > limit
330
373
  space_pos
331
374
  end
332
375
 
376
+ def space_before_word(node, max_pos)
377
+ return if max_pos.negative?
378
+
379
+ node.source.rindex(/\s(?=\S)/, max_pos)
380
+ end
381
+
333
382
  def escape_sequence_start(text)
334
383
  text.match(ESCAPE_SEQUENCE_TAIL)&.begin(1)
335
384
  end
@@ -339,10 +388,25 @@ module RuboCop
339
388
  return if adjustment.abs > source_range.size
340
389
 
341
390
  cut = source_range.adjust(end_pos: adjustment)
391
+ cut = cut.resize(size_before_whitespace_run(source_range.source, cut.size))
342
392
  escape_pos = escape_sequence_start(cut.source)
393
+
343
394
  escape_pos ? source_range.resize(escape_pos) : cut
344
395
  end
345
396
 
397
+ def size_before_whitespace_run(text, size)
398
+ return size if size.zero?
399
+ return size unless text[size]&.match?(/\s/)
400
+
401
+ text.rindex(/\S/, size - 1) || size
402
+ end
403
+
404
+ def blank_rest?(node, break_pos)
405
+ offset = break_pos - node.source_range.begin_pos
406
+
407
+ node.source[offset...string_content_length(node)].strip.empty?
408
+ end
409
+
346
410
  def string_content_length(node)
347
411
  content_end = node.loc?(:end) ? node.loc.end.begin_pos : node.source_range.end_pos
348
412
 
@@ -360,7 +424,9 @@ module RuboCop
360
424
  def opens_string?(begin_node)
361
425
  quote = begin_node.parent.loc.begin
362
426
 
363
- quote && quote.end_pos == begin_node.source_range.begin_pos
427
+ return false unless quote
428
+
429
+ blank_between?(quote.end_pos, begin_node.source_range.begin_pos)
364
430
  end
365
431
 
366
432
  def breakable_range_by_line_index
@@ -375,6 +441,10 @@ module RuboCop
375
441
  @breakable_string_delimiters ||= {}
376
442
  end
377
443
 
444
+ def breakable_strings
445
+ @breakable_strings ||= {}
446
+ end
447
+
378
448
  def heredocs
379
449
  @heredocs ||= extract_heredocs(processed_source.ast)
380
450
  end
@@ -431,10 +501,38 @@ module RuboCop
431
501
  "\n"
432
502
  end
433
503
 
434
- corrector.insert_before(breakable_range, insertion) unless breakable_range.nil?
504
+ break_ranges(line_index).each do |range|
505
+ corrector.insert_before(range, insertion)
506
+ end
435
507
  end
436
508
  end
437
509
 
510
+ def break_ranges(line_index)
511
+ return [] if breakable_range.nil?
512
+ return [breakable_range] unless (node = breakable_strings[line_index])&.loc?(:end)
513
+
514
+ string_break_ranges(node, breakable_string_delimiters[line_index])
515
+ end
516
+
517
+ def string_break_ranges(node, delimiter)
518
+ ranges = [breakable_range]
519
+ pos = breakable_range.begin_pos
520
+
521
+ while (pos = next_string_break_position(node, pos, delimiter))
522
+ ranges << range_between(pos, pos + 1)
523
+ end
524
+
525
+ ranges
526
+ end
527
+
528
+ def next_string_break_position(node, pos, delimiter)
529
+ rest = parse("#{delimiter}#{node.source_range.with(begin_pos: pos).source}").ast
530
+ return unless rest&.str_type?
531
+ return unless (rest_pos = breakable_string_position(rest))
532
+
533
+ pos + rest_pos - delimiter.length
534
+ end
535
+
438
536
  def excess_range(uri_range, line, line_index)
439
537
  excessive_position = if uri_range && uri_range.begin < max
440
538
  uri_range.end
@@ -106,11 +106,13 @@ module RuboCop
106
106
  ALWAYS_SAME_LINE_MESSAGE = 'Closing hash brace must be on the same ' \
107
107
  'line as the last hash element.'
108
108
 
109
- # Moving the closing brace while `Style/HashAsLastArrayItem` or
110
- # `Style/RedundantDoubleSplatHashBraces` removes the braces in the same pass leaves
111
- # the brace behind.
109
+ # Moving the closing brace after a method call value while
110
+ # `Layout/MultilineMethodCallBraceLayout` moves that call's closing parenthesis
111
+ # before a comment in the same pass places the brace after the comment.
112
+ # Moving it while `Style/HashAsLastArrayItem` or `Style/RedundantDoubleSplatHashBraces`
113
+ # removes the braces in the same pass leaves the brace behind.
112
114
  def self.autocorrect_incompatible_with
113
- [Style::HashAsLastArrayItem, Style::RedundantDoubleSplatHashBraces]
115
+ [Layout::MultilineMethodCallBraceLayout, Style::HashAsLastArrayItem, Style::RedundantDoubleSplatHashBraces]
114
116
  end
115
117
 
116
118
  def on_hash(node)
@@ -102,7 +102,7 @@ module RuboCop
102
102
  def autocorrect(corrector, node)
103
103
  if @send_node.block_node
104
104
  correct_selector_only(corrector, node)
105
- correct_block(corrector, @send_node.block_node)
105
+ correct_block(corrector, @send_node.block_node, node.first_line)
106
106
  else
107
107
  AlignmentCorrector.correct(corrector, processed_source, node, @column_delta)
108
108
  end
@@ -114,10 +114,21 @@ module RuboCop
114
114
  AlignmentCorrector.correct(corrector, processed_source, selector_range, @column_delta)
115
115
  end
116
116
 
117
- def correct_block(corrector, block_node)
118
- AlignmentCorrector.correct(corrector, processed_source, block_node.body, @column_delta)
119
- end_range = range_by_whole_lines(block_node.loc.end, include_final_newline: false)
120
- AlignmentCorrector.correct(corrector, processed_source, end_range, @column_delta)
117
+ def correct_block(corrector, block_node, selector_line)
118
+ lines_to_shift = block_lines(block_node) - [selector_line]
119
+
120
+ lines_to_shift.each do |line|
121
+ line_range = processed_source.buffer.line_range(line)
122
+
123
+ AlignmentCorrector.correct(corrector, processed_source, line_range, @column_delta)
124
+ end
125
+ end
126
+
127
+ def block_lines(block_node)
128
+ body = block_node.body
129
+ lines = body ? (body.first_line..body.last_line).to_a : []
130
+
131
+ (lines << block_node.loc.end.line).uniq
121
132
  end
122
133
 
123
134
  def relevant_node?(send_node)
@@ -25,10 +25,10 @@ module RuboCop
25
25
 
26
26
  MSG = 'Put method call on a separate line if chained to a single line block.'
27
27
 
28
- # `Style/FloatDivision` and `Style/NilComparison` rewrite a method chained to the block
29
- # in the same pass, which must not start a line of its own.
28
+ # These cops rewrite a method chained to the block in the same pass,
29
+ # which must not start a line of its own.
30
30
  def self.autocorrect_incompatible_with
31
- [Style::FloatDivision, Style::MapToHash, Style::NilComparison]
31
+ [Style::FloatDivision, Style::MapToHash, Style::NilComparison, Style::RedundantSort]
32
32
  end
33
33
 
34
34
  def on_send(node)
@@ -108,14 +108,9 @@ module RuboCop
108
108
  end
109
109
 
110
110
  def left_ref_bracket(node, tokens)
111
- current_token = tokens.reverse.find(&:left_ref_bracket?)
112
- previous_token = previous_token(current_token)
111
+ bracket_pos = node.loc.selector.begin_pos
113
112
 
114
- if node.method?(:[]=) || (previous_token && !previous_token.right_bracket?)
115
- tokens.find(&:left_ref_bracket?)
116
- else
117
- current_token
118
- end
113
+ tokens.find { |token| token.left_ref_bracket? && token.begin_pos == bracket_pos }
119
114
  end
120
115
 
121
116
  def closing_bracket(tokens, opening_bracket)
@@ -129,11 +124,6 @@ module RuboCop
129
124
  end
130
125
  end
131
126
 
132
- def previous_token(current_token)
133
- index = processed_source.tokens.index(current_token)
134
- index.nil? || index.zero? ? nil : processed_source.tokens[index - 1]
135
- end
136
-
137
127
  def empty_config
138
128
  cop_config['EnforcedStyleForEmptyBrackets']
139
129
  end
@@ -55,7 +55,8 @@ module RuboCop
55
55
  return node if first_argument_is_regexp?(node) || !node.parent
56
56
 
57
57
  if (node.parent.send_type? && node.receiver) ||
58
- method_chain_to_regexp_receiver?(node, regexp_receiver)
58
+ method_chain_to_regexp_receiver?(node, regexp_receiver) ||
59
+ part_of_hash_argument?(node)
59
60
  node = find_offense_node(node.parent, regexp_receiver)
60
61
  end
61
62
 
@@ -72,6 +73,10 @@ module RuboCop
72
73
 
73
74
  parent.parent && parent_receiver.receiver == regexp_receiver
74
75
  end
76
+
77
+ def part_of_hash_argument?(node)
78
+ node.pair_type? || node.parent.pair_type? || (node.hash_type? && !node.braces?)
79
+ end
75
80
  end
76
81
  end
77
82
  end
@@ -34,6 +34,10 @@ module RuboCop
34
34
 
35
35
  MSG = 'Put a method call with a HEREDOC receiver on the same line as the HEREDOC opening.'
36
36
 
37
+ def self.autocorrect_incompatible_with
38
+ [Style::MethodCallWithArgsParentheses]
39
+ end
40
+
37
41
  def on_send(node)
38
42
  heredoc = heredoc_node_descendent_receiver(node)
39
43
  return unless heredoc
@@ -140,6 +140,8 @@ module RuboCop
140
140
  corrected_method: correct_sym_method(to_method)
141
141
  )
142
142
  add_offense(node, message: message) do |corrector|
143
+ next if part_of_ignored_node?(node)
144
+
143
145
  remove_parentheses(corrector, node) if node.parenthesized?
144
146
 
145
147
  corrector.replace(sym_node, correct_sym_method(to_method))
@@ -60,7 +60,7 @@ module RuboCop
60
60
 
61
61
  rest_operands = grouped_operands[begin_of_rest_operands..]
62
62
  rest_operands.each do |operand|
63
- next if already_appropriate_call?(operand, dot_op)
63
+ next unless correction_needed?(operand, dot_op, grouped_operands)
64
64
 
65
65
  register_offense(operand, dot_op)
66
66
  end
@@ -100,12 +100,58 @@ module RuboCop
100
100
  end
101
101
  end
102
102
 
103
+ def correction_needed?(operand, dot_op, grouped_operands)
104
+ return false if already_appropriate_call?(operand, dot_op)
105
+
106
+ dot_op == '&.' || receiver_guaranteed?(operand, grouped_operands)
107
+ end
108
+
103
109
  def already_appropriate_call?(operand, dot_op)
104
110
  return true if operand.safe_navigation? && dot_op == '&.'
105
111
 
106
112
  (operand.dot? || operand.operator_method?) && dot_op == '.'
107
113
  end
108
114
 
115
+ def receiver_guaranteed?(operand, grouped_operands)
116
+ grouped_operands.take_while { |guard| !guard.equal?(operand) }.any? do |guard|
117
+ guaranteed_by?(guard, operand)
118
+ end
119
+ end
120
+
121
+ def guaranteed_by?(guard, operand)
122
+ return false unless (path = logical_path(guard, operand))
123
+
124
+ truthiness = path.last.first.and_type? ? :truthy : :falsey
125
+ path[0...-1].reverse_each do |node, child|
126
+ return false unless (truthiness = child_truthiness(node, child, truthiness))
127
+ end
128
+
129
+ !guard.csend_type? || truthiness == :truthy
130
+ end
131
+
132
+ def logical_path(guard, operand)
133
+ path = []
134
+ child = guard
135
+
136
+ guard.each_ancestor do |node|
137
+ unless node.begin_type?
138
+ return nil unless node.operator_keyword?
139
+
140
+ path << [node, child]
141
+ end
142
+ return path if operand.each_ancestor.any? { |ancestor| ancestor.equal?(node) }
143
+
144
+ child = node
145
+ end
146
+ end
147
+
148
+ def child_truthiness(node, child, truthiness)
149
+ deciding = node.and_type? ? :truthy : :falsey
150
+ return truthiness == deciding ? deciding : :unknown if node.lhs.equal?(child)
151
+
152
+ deciding if truthiness == deciding
153
+ end
154
+
109
155
  def register_offense(operand, dot_operator)
110
156
  offense_range = operand.operator_method? ? operand : operand.loc.dot
111
157
  message = dot_operator == '.' ? USE_DOT_MSG : USE_SAFE_NAVIGATION_MSG
@@ -185,6 +185,7 @@ module RuboCop
185
185
  ignore_node(key)
186
186
 
187
187
  next if requires_quotes?(key)
188
+ next if key.parent.value_omission?
188
189
  next if properly_quoted?(key.source, %("#{key.value}"))
189
190
 
190
191
  correction = %("#{key.value}")
@@ -35,15 +35,13 @@ module RuboCop
35
35
  ASSIGNMENT_TYPES = %i[lvasgn ivasgn cvasgn gvasgn].freeze
36
36
 
37
37
  def on_def(node)
38
- return unless node.body
38
+ return if !node.body || node.void_context?
39
39
 
40
40
  last_expr = last_expression(node.body)
41
41
  return unless setter_call_to_local_variable?(last_expr)
42
42
 
43
- tracker = MethodVariableTracker.new(node.body)
44
43
  receiver, = *last_expr
45
- variable_name, = *receiver
46
- return unless tracker.contain_local_object?(variable_name)
44
+ return unless contain_local_object?(node.body, receiver)
47
45
 
48
46
  loc_name = receiver.loc.name
49
47
 
@@ -66,6 +64,11 @@ module RuboCop
66
64
  expression.is_a?(Array) ? expression.last : expression
67
65
  end
68
66
 
67
+ def contain_local_object?(body, receiver)
68
+ variable_name, = *receiver
69
+ MethodVariableTracker.new(body).contain_local_object?(variable_name)
70
+ end
71
+
69
72
  # This class tracks variable assignments in a method body
70
73
  # and if a variable contains object passed as argument at the end of
71
74
  # the method.
@@ -76,11 +76,7 @@ module RuboCop
76
76
  def extract_first_element_over_column_limit(node, elements, max)
77
77
  line = node.first_line
78
78
 
79
- # If a `send` or `csend` node is not parenthesized, don't move the first element, because it
80
- # can result in changed behavior or a syntax error.
81
- if node.call_type? && !node.parenthesized? && !first_argument_is_heredoc?(node)
82
- elements = elements.drop(1)
83
- end
79
+ elements = elements.drop(1) unless first_element_movable?(node)
84
80
 
85
81
  i = 0
86
82
  i += 1 while within_column_limit?(elements[i], max, line)
@@ -93,7 +89,12 @@ module RuboCop
93
89
  end
94
90
 
95
91
  # @api private
96
- def first_argument_is_heredoc?(node)
92
+ # If a method call or definition is not parenthesized, the first element cannot be moved,
93
+ # because it can result in changed behavior or a syntax error.
94
+ def first_element_movable?(node)
95
+ return !node.arguments.loc.begin.nil? if node.any_def_type?
96
+ return true if !node.call_type? || node.parenthesized?
97
+
97
98
  node.first_argument.respond_to?(:heredoc?) && node.first_argument.heredoc?
98
99
  end
99
100
 
@@ -5,6 +5,8 @@ module RuboCop
5
5
  # Checks for code on multiple lines that could be rewritten on a single line
6
6
  # without changing semantics or exceeding the `Max` parameter of `Layout/LineLength`.
7
7
  module CheckSingleLineSuitability
8
+ include LineLengthHelp
9
+
8
10
  def suitable_as_single_line?(node)
9
11
  !too_long?(node) &&
10
12
  !comment_within?(node) &&
@@ -21,7 +23,7 @@ module RuboCop
21
23
  return false unless max_line_length
22
24
 
23
25
  lines = processed_source.lines[(line_range.begin - 1)...line_range.end]
24
- to_single_line(lines.join("\n")).length > max_line_length
26
+ line_length(to_single_line(lines.join("\n"))) > max_line_length
25
27
  end
26
28
 
27
29
  def to_single_line(source)
@@ -45,12 +45,44 @@ module RuboCop
45
45
 
46
46
  def command_disallowed_in_endless_body?(body)
47
47
  return true if body.assignment? && command_call?(assigned_value(body))
48
+ return true if body_disallowed_in_argument?(body)
49
+ return true if negated_command_call?(body)
48
50
 
49
51
  body.each_node(:any_block).any? do |block|
50
52
  block.keywords? && command_call?(block.send_node)
51
53
  end
52
54
  end
53
55
 
56
+ def body_disallowed_in_argument?(body)
57
+ command_body_in_argument?(body) || do_end_block_body_in_command_argument?(body)
58
+ end
59
+
60
+ # `private def foo = bar baz` parses only on Ruby 4.0+, and only as the sole argument.
61
+ def command_body_in_argument?(body)
62
+ return false unless command_call?(body)
63
+
64
+ call = argument_receiving_call(body.parent)
65
+ return false if call.nil?
66
+
67
+ target_ruby_version < 4.0 || !call.arguments.one?
68
+ end
69
+
70
+ def argument_receiving_call(def_node)
71
+ call = def_node.parent
72
+ return unless call&.call_type?
73
+
74
+ call if call.arguments.any? { |argument| argument.equal?(def_node) }
75
+ end
76
+
77
+ def do_end_block_body_in_command_argument?(body)
78
+ call = argument_receiving_call(body.parent)
79
+ return false if call.nil? || call.parenthesized?
80
+
81
+ body.each_node(:any_block).any? do |block|
82
+ block.keywords? && !block.send_node.lambda_literal?
83
+ end
84
+ end
85
+
54
86
  def assigned_value(node)
55
87
  while node.assignment? || node.rescue_type?
56
88
  node = node.rescue_type? ? node.body : node.children.last
@@ -64,6 +96,10 @@ module RuboCop
64
96
 
65
97
  node.arguments? && !node.parenthesized? && !node.operator_method? && !node.setter_method?
66
98
  end
99
+
100
+ def negated_command_call?(node)
101
+ node.send_type? && node.prefix_bang? && command_call?(node.receiver)
102
+ end
67
103
  end
68
104
  end
69
105
  end
@@ -60,8 +60,9 @@ module RuboCop
60
60
 
61
61
  def deltas(first_pair, current_pair)
62
62
  key_delta = key_delta(first_pair, current_pair)
63
- separator_delta = separator_delta(first_pair, current_pair, key_delta)
64
- value_delta = value_delta(first_pair, current_pair) - key_delta - separator_delta
63
+ shift = separator_shift(current_pair, key_delta)
64
+ separator_delta = separator_delta(first_pair, current_pair, shift)
65
+ value_delta = value_delta(first_pair, current_pair) - shift - separator_delta
65
66
 
66
67
  { key: key_delta, separator: separator_delta, value: value_delta }
67
68
  end
@@ -78,9 +79,13 @@ module RuboCop
78
79
  pair.value && pair.key.last_line != pair.value.first_line
79
80
  end
80
81
 
81
- def separator_delta(first_pair, current_pair, key_delta)
82
+ def separator_shift(current_pair, key_delta)
83
+ current_pair.key.multiline? ? 0 : key_delta
84
+ end
85
+
86
+ def separator_delta(first_pair, current_pair, shift)
82
87
  if current_pair.hash_rocket?
83
- hash_rocket_delta(first_pair, current_pair) - key_delta
88
+ hash_rocket_delta(first_pair, current_pair) - shift
84
89
  else
85
90
  0
86
91
  end
@@ -98,7 +98,7 @@ module RuboCop
98
98
 
99
99
  # rubocop:disable-next Metrics/AbcSize
100
100
  def if_body_source(if_body)
101
- if if_body.call_type? && !if_body.method?(:[]=) && omitted_value_in_last_hash_arg?(if_body)
101
+ if unparenthesized_call_with_omitted_value?(if_body)
102
102
  "#{method_source(if_body)}(#{if_body.arguments.map(&:source).join(', ')})"
103
103
  elsif (value = wrapped_value(if_body))
104
104
  prefix = if_body.source_range.begin.join(value.source_range.begin).source
@@ -110,7 +110,8 @@ module RuboCop
110
110
  end
111
111
  end
112
112
 
113
- def omitted_value_in_last_hash_arg?(if_body)
113
+ def unparenthesized_call_with_omitted_value?(if_body)
114
+ return false if !if_body.call_type? || if_body.method?(:[]=) || if_body.parenthesized?
114
115
  return false unless (last_argument = if_body.last_argument)
115
116
 
116
117
  last_argument.hash_type? && last_argument.pairs.last&.value_omission?
@@ -63,9 +63,10 @@ module RuboCop
63
63
  (send nil? VISIBILITY_SCOPES def)
64
64
  PATTERN
65
65
 
66
+ # A modifier applies to every symbol it is given, not only to a sole argument.
66
67
  # @!method visibility_inline_on_method_name?(node, method_name:)
67
68
  def_node_matcher :visibility_inline_on_method_name?, <<~PATTERN
68
- (send nil? VISIBILITY_SCOPES (sym %method_name))
69
+ (send nil? VISIBILITY_SCOPES <(sym %method_name) ...>)
69
70
  PATTERN
70
71
  end
71
72
  end
@@ -128,7 +128,7 @@ module RuboCop
128
128
  if child_node.lvar_type?
129
129
  parent_node = child_node.parent
130
130
  if parent_node.respond_to?(:value_omission?) && parent_node.value_omission?
131
- corrector.insert_after(parent_node.loc.operator, " #{preferred_name}")
131
+ corrector.replace(parent_node.loc.operator, ": #{preferred_name}")
132
132
  else
133
133
  corrector.replace(child_node, preferred_name)
134
134
  end