rubocop 1.66.0 → 1.67.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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +15 -6
  4. data/config/internal_affairs.yml +11 -0
  5. data/lib/rubocop/cli/command/auto_generate_config.rb +6 -7
  6. data/lib/rubocop/cli/command/lsp.rb +2 -2
  7. data/lib/rubocop/comment_config.rb +4 -8
  8. data/lib/rubocop/config.rb +4 -16
  9. data/lib/rubocop/config_loader.rb +14 -8
  10. data/lib/rubocop/config_loader_resolver.rb +3 -3
  11. data/lib/rubocop/config_validator.rb +7 -10
  12. data/lib/rubocop/cop/base.rb +6 -2
  13. data/lib/rubocop/cop/bundler/gem_version.rb +1 -0
  14. data/lib/rubocop/cop/cop.rb +8 -0
  15. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
  16. data/lib/rubocop/cop/internal_affairs/cop_description.rb +0 -4
  17. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  18. data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +6 -21
  19. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +8 -1
  20. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +0 -5
  21. data/lib/rubocop/cop/internal_affairs.rb +16 -0
  22. data/lib/rubocop/cop/layout/access_modifier_indentation.rb +5 -1
  23. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  24. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  25. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +8 -0
  26. data/lib/rubocop/cop/layout/indentation_width.rb +4 -5
  27. data/lib/rubocop/cop/layout/leading_comment_space.rb +28 -1
  28. data/lib/rubocop/cop/lint/ambiguous_range.rb +4 -1
  29. data/lib/rubocop/cop/lint/big_decimal_new.rb +4 -7
  30. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
  31. data/lib/rubocop/cop/lint/duplicate_set_element.rb +74 -0
  32. data/lib/rubocop/cop/lint/ensure_return.rb +0 -3
  33. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  34. data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
  35. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +10 -4
  36. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +5 -14
  37. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +25 -2
  38. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -6
  39. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -1
  40. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +105 -41
  41. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  42. data/lib/rubocop/cop/lint/uri_regexp.rb +25 -7
  43. data/lib/rubocop/cop/mixin/percent_array.rb +1 -1
  44. data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -2
  45. data/lib/rubocop/cop/naming/inclusive_language.rb +12 -3
  46. data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
  47. data/lib/rubocop/cop/offense.rb +2 -2
  48. data/lib/rubocop/cop/style/access_modifier_declarations.rb +12 -2
  49. data/lib/rubocop/cop/style/accessor_grouping.rb +10 -2
  50. data/lib/rubocop/cop/style/arguments_forwarding.rb +46 -6
  51. data/lib/rubocop/cop/style/block_delimiters.rb +14 -1
  52. data/lib/rubocop/cop/style/collection_compact.rb +10 -10
  53. data/lib/rubocop/cop/style/combinable_loops.rb +7 -0
  54. data/lib/rubocop/cop/style/commented_keyword.rb +7 -1
  55. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  56. data/lib/rubocop/cop/style/data_inheritance.rb +1 -1
  57. data/lib/rubocop/cop/style/empty_else.rb +1 -0
  58. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  59. data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
  60. data/lib/rubocop/cop/style/guard_clause.rb +1 -1
  61. data/lib/rubocop/cop/style/hash_each_methods.rb +6 -0
  62. data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
  63. data/lib/rubocop/cop/style/if_inside_else.rb +1 -1
  64. data/lib/rubocop/cop/style/if_with_semicolon.rb +16 -5
  65. data/lib/rubocop/cop/style/lambda.rb +1 -1
  66. data/lib/rubocop/cop/style/magic_comment_format.rb +3 -8
  67. data/lib/rubocop/cop/style/map_into_array.rb +54 -10
  68. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +12 -7
  69. data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
  70. data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
  71. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
  72. data/lib/rubocop/cop/style/one_line_conditional.rb +4 -0
  73. data/lib/rubocop/cop/style/operator_method_call.rb +25 -6
  74. data/lib/rubocop/cop/style/redundant_begin.rb +4 -0
  75. data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
  76. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +1 -1
  77. data/lib/rubocop/cop/style/redundant_line_continuation.rb +3 -3
  78. data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -1
  79. data/lib/rubocop/cop/style/require_order.rb +1 -1
  80. data/lib/rubocop/cop/style/rescue_modifier.rb +13 -1
  81. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +54 -12
  82. data/lib/rubocop/cop/style/safe_navigation.rb +92 -50
  83. data/lib/rubocop/cop/style/select_by_regexp.rb +9 -6
  84. data/lib/rubocop/cop/style/semicolon.rb +1 -1
  85. data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
  86. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  87. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  88. data/lib/rubocop/cop/team.rb +8 -1
  89. data/lib/rubocop/cop/util.rb +1 -1
  90. data/lib/rubocop/cops_documentation_generator.rb +73 -34
  91. data/lib/rubocop/file_finder.rb +9 -4
  92. data/lib/rubocop/lsp/runtime.rb +2 -0
  93. data/lib/rubocop/lsp/server.rb +0 -1
  94. data/lib/rubocop/rspec/expect_offense.rb +1 -0
  95. data/lib/rubocop/runner.rb +3 -0
  96. data/lib/rubocop/server/cache.rb +6 -1
  97. data/lib/rubocop/server/core.rb +1 -0
  98. data/lib/rubocop/target_ruby.rb +12 -12
  99. data/lib/rubocop/version.rb +3 -1
  100. data/lib/rubocop/yaml_duplication_checker.rb +20 -26
  101. data/lib/rubocop.rb +2 -0
  102. metadata +12 -10
@@ -68,6 +68,8 @@ module RuboCop
68
68
  # class Foo
69
69
  #
70
70
  # private :bar, :baz
71
+ # private *%i[qux quux]
72
+ # private *METHOD_NAMES
71
73
  #
72
74
  # end
73
75
  #
@@ -76,6 +78,8 @@ module RuboCop
76
78
  # class Foo
77
79
  #
78
80
  # private :bar, :baz
81
+ # private *%i[qux quux]
82
+ # private *METHOD_NAMES
79
83
  #
80
84
  # end
81
85
  #
@@ -128,7 +132,9 @@ module RuboCop
128
132
 
129
133
  # @!method access_modifier_with_symbol?(node)
130
134
  def_node_matcher :access_modifier_with_symbol?, <<~PATTERN
131
- (send nil? {:private :protected :public :module_function} (sym _))
135
+ (send nil? {:private :protected :public :module_function}
136
+ {(sym _) (splat {#percent_symbol_array? const})}
137
+ )
132
138
  PATTERN
133
139
 
134
140
  # @!method access_modifier_with_attr?(node)
@@ -170,6 +176,10 @@ module RuboCop
170
176
  end
171
177
  end
172
178
 
179
+ def percent_symbol_array?(node)
180
+ node.array_type? && node.percent_literal?(:symbol)
181
+ end
182
+
173
183
  def allow_modifiers_on_symbols?(node)
174
184
  cop_config['AllowModifiersOnSymbols'] && access_modifier_with_symbol?(node)
175
185
  end
@@ -218,7 +228,7 @@ module RuboCop
218
228
 
219
229
  def find_corresponding_def_node(node)
220
230
  if access_modifier_with_symbol?(node)
221
- method_name = node.first_argument.value
231
+ method_name = node.first_argument.respond_to?(:value) && node.first_argument.value
222
232
  node.parent.each_child_node(:def).find do |child|
223
233
  child.method?(method_name)
224
234
  end
@@ -10,6 +10,9 @@ module RuboCop
10
10
  # NOTE: If there is a method call before the accessor method it is always allowed
11
11
  # as it might be intended like Sorbet.
12
12
  #
13
+ # NOTE: If there is a RBS::Inline annotation comment just after the accessor method
14
+ # it is always allowed.
15
+ #
13
16
  # @example EnforcedStyle: grouped (default)
14
17
  # # bad
15
18
  # class Foo
@@ -92,7 +95,7 @@ module RuboCop
92
95
  comment_line?(processed_source[node.first_line - 2])
93
96
  end
94
97
 
95
- # rubocop:disable Metrics/CyclomaticComplexity
98
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
96
99
  def groupable_accessor?(node)
97
100
  return true unless (previous_expression = node.left_siblings.last)
98
101
 
@@ -105,11 +108,16 @@ module RuboCop
105
108
 
106
109
  return true unless previous_expression.send_type?
107
110
 
111
+ # Accessors with RBS::Inline annotations shouldn't be groupable.
112
+ return false if processed_source.comments.any? do |c|
113
+ same_line?(c, previous_expression) && c.text.start_with?('#:')
114
+ end
115
+
108
116
  previous_expression.attribute_accessor? ||
109
117
  previous_expression.access_modifier? ||
110
118
  node.first_line - previous_expression.last_line > 1 # there is a space between nodes
111
119
  end
112
- # rubocop:enable Metrics/CyclomaticComplexity
120
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
113
121
 
114
122
  def class_send_elements(class_node)
115
123
  class_def = class_node.body
@@ -180,7 +180,8 @@ module RuboCop
180
180
  end
181
181
 
182
182
  def only_forwards_all?(send_classifications)
183
- send_classifications.all? { |_, c, _, _| c == :all }
183
+ all_classifications = %i[all all_anonymous].freeze
184
+ send_classifications.all? { |_, c, _, _| all_classifications.include?(c) }
184
185
  end
185
186
 
186
187
  # rubocop:disable Metrics/MethodLength
@@ -188,8 +189,8 @@ module RuboCop
188
189
  _rest_arg, _kwrest_arg, block_arg = *forwardable_args
189
190
  registered_block_arg_offense = false
190
191
 
191
- send_classifications.each do |send_node, _c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength
192
- if !forward_rest && !forward_kwrest
192
+ send_classifications.each do |send_node, c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength
193
+ if !forward_rest && !forward_kwrest && c != :all_anonymous
193
194
  # Prevents `anonymous block parameter is also used within block (SyntaxError)` occurs
194
195
  # in Ruby 3.3.0.
195
196
  if outside_block?(forward_block_arg)
@@ -213,6 +214,7 @@ module RuboCop
213
214
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
214
215
  def add_post_ruby_32_offenses(def_node, send_classifications, forwardable_args)
215
216
  return unless use_anonymous_forwarding?
217
+ return if send_inside_block?(send_classifications)
216
218
 
217
219
  rest_arg, kwrest_arg, block_arg = *forwardable_args
218
220
 
@@ -355,8 +357,15 @@ module RuboCop
355
357
  cop_config.fetch('UseAnonymousForwarding', false)
356
358
  end
357
359
 
360
+ def send_inside_block?(send_classifications)
361
+ send_classifications.any? do |send_node, *|
362
+ send_node.each_ancestor(:block, :numblock).any?
363
+ end
364
+ end
365
+
358
366
  def add_parens_if_missing(node, corrector)
359
367
  return if parentheses?(node)
368
+ return if node.send_type? && node.method?(:[])
360
369
 
361
370
  add_parentheses(node, corrector)
362
371
  end
@@ -374,6 +383,23 @@ module RuboCop
374
383
  # @!method forwarded_block_arg?(node, block_name)
375
384
  def_node_matcher :forwarded_block_arg?, '(block_pass {(lvar %1) nil?})'
376
385
 
386
+ # @!method def_all_anonymous_args?(node)
387
+ def_node_matcher :def_all_anonymous_args?, <<~PATTERN
388
+ (
389
+ def _
390
+ (args ... (restarg) (kwrestarg) (blockarg nil?))
391
+ _
392
+ )
393
+ PATTERN
394
+
395
+ # @!method send_all_anonymous_args?(node)
396
+ def_node_matcher :send_all_anonymous_args?, <<~PATTERN
397
+ (
398
+ send _ _
399
+ ... (forwarded_restarg) (hash (forwarded_kwrestarg)) (block_pass nil?)
400
+ )
401
+ PATTERN
402
+
377
403
  def initialize(def_node, send_node, referenced_lvars, forwardable_args, **config)
378
404
  @def_node = def_node
379
405
  @send_node = send_node
@@ -405,7 +431,9 @@ module RuboCop
405
431
  def classification
406
432
  return nil unless forwarded_rest_arg || forwarded_kwrest_arg || forwarded_block_arg
407
433
 
408
- if can_forward_all?
434
+ if ruby_32_only_anonymous_forwarding?
435
+ :all_anonymous
436
+ elsif can_forward_all?
409
437
  :all
410
438
  else
411
439
  :rest_or_kwrest
@@ -414,16 +442,28 @@ module RuboCop
414
442
 
415
443
  private
416
444
 
445
+ # rubocop:disable Metrics/CyclomaticComplexity
417
446
  def can_forward_all?
418
447
  return false if any_arg_referenced?
419
- return false if ruby_32_missing_rest_or_kwest?
448
+ return false if ruby_30_or_lower_optarg?
449
+ return false if ruby_32_or_higher_missing_rest_or_kwest?
420
450
  return false unless offensive_block_forwarding?
421
451
  return false if additional_kwargs_or_forwarded_kwargs?
422
452
 
423
453
  no_additional_args? || (target_ruby_version >= 3.0 && no_post_splat_args?)
424
454
  end
455
+ # rubocop:enable Metrics/CyclomaticComplexity
456
+
457
+ # def foo(a = 41, ...) is a syntax error in 3.0.
458
+ def ruby_30_or_lower_optarg?
459
+ target_ruby_version <= 3.0 && @def_node.arguments.any?(&:optarg_type?)
460
+ end
461
+
462
+ def ruby_32_only_anonymous_forwarding?
463
+ def_all_anonymous_args?(@def_node) && send_all_anonymous_args?(@send_node)
464
+ end
425
465
 
426
- def ruby_32_missing_rest_or_kwest?
466
+ def ruby_32_or_higher_missing_rest_or_kwest?
427
467
  target_ruby_version >= 3.2 && !forwarded_rest_and_kwrest_args
428
468
  end
429
469
 
@@ -176,6 +176,10 @@ module RuboCop
176
176
 
177
177
  BRACES_REQUIRED_MESSAGE = "Brace delimiters `{...}` required for '%<method_name>s' method."
178
178
 
179
+ def self.autocorrect_incompatible_with
180
+ [Style::RedundantBegin]
181
+ end
182
+
179
183
  def on_send(node)
180
184
  return unless node.arguments?
181
185
  return if node.parenthesized?
@@ -341,8 +345,9 @@ module RuboCop
341
345
  end
342
346
  end
343
347
 
348
+ # rubocop:disable Metrics/CyclomaticComplexity
344
349
  def proper_block_style?(node)
345
- return true if require_braces?(node)
350
+ return true if require_braces?(node) || require_do_end?(node)
346
351
  return special_method_proper_block_style?(node) if special_method?(node.method_name)
347
352
 
348
353
  case style
@@ -352,6 +357,7 @@ module RuboCop
352
357
  when :always_braces then braces_style?(node)
353
358
  end
354
359
  end
360
+ # rubocop:enable Metrics/CyclomaticComplexity
355
361
 
356
362
  def require_braces?(node)
357
363
  return false unless node.braces?
@@ -361,6 +367,13 @@ module RuboCop
361
367
  end
362
368
  end
363
369
 
370
+ def require_do_end?(node)
371
+ return false if node.braces? || node.multiline?
372
+ return false unless (resbody = node.each_descendant(:resbody).first)
373
+
374
+ resbody.children.first&.array_type?
375
+ end
376
+
364
377
  def special_method?(method_name)
365
378
  allowed_method?(method_name) ||
366
379
  matches_allowed_pattern?(method_name) ||
@@ -21,6 +21,7 @@ module RuboCop
21
21
  # array.reject(&:nil?)
22
22
  # array.reject { |e| e.nil? }
23
23
  # array.select { |e| !e.nil? }
24
+ # array.filter { |e| !e.nil? }
24
25
  # array.grep_v(nil)
25
26
  # array.grep_v(NilClass)
26
27
  #
@@ -29,10 +30,9 @@ module RuboCop
29
30
  #
30
31
  # # bad
31
32
  # hash.reject!(&:nil?)
32
- # array.delete_if(&:nil?)
33
33
  # hash.reject! { |k, v| v.nil? }
34
- # array.delete_if { |e| e.nil? }
35
34
  # hash.select! { |k, v| !v.nil? }
35
+ # hash.filter! { |k, v| !v.nil? }
36
36
  #
37
37
  # # good
38
38
  # hash.compact!
@@ -48,14 +48,15 @@ module RuboCop
48
48
  extend TargetRubyVersion
49
49
 
50
50
  MSG = 'Use `%<good>s` instead of `%<bad>s`.'
51
- RESTRICT_ON_SEND = %i[reject delete_if reject! select select! grep_v].freeze
51
+ RESTRICT_ON_SEND = %i[reject reject! select select! filter filter! grep_v].freeze
52
52
  TO_ENUM_METHODS = %i[to_enum lazy].freeze
53
+ FILTER_METHODS = %i[filter filter!].freeze
53
54
 
54
55
  minimum_target_ruby_version 2.4
55
56
 
56
57
  # @!method reject_method_with_block_pass?(node)
57
58
  def_node_matcher :reject_method_with_block_pass?, <<~PATTERN
58
- (call !nil? {:reject :delete_if :reject!}
59
+ (call !nil? {:reject :reject!}
59
60
  (block_pass
60
61
  (sym :nil?)))
61
62
  PATTERN
@@ -64,7 +65,7 @@ module RuboCop
64
65
  def_node_matcher :reject_method?, <<~PATTERN
65
66
  (block
66
67
  (call
67
- !nil? {:reject :delete_if :reject!})
68
+ !nil? {:reject :reject!})
68
69
  $(args ...)
69
70
  (call
70
71
  $(lvar _) :nil?))
@@ -74,7 +75,7 @@ module RuboCop
74
75
  def_node_matcher :select_method?, <<~PATTERN
75
76
  (block
76
77
  (call
77
- !nil? {:select :select!})
78
+ !nil? {:select :select! :filter :filter!})
78
79
  $(args ...)
79
80
  (call
80
81
  (call
@@ -87,11 +88,10 @@ module RuboCop
87
88
  PATTERN
88
89
 
89
90
  def on_send(node)
91
+ return if target_ruby_version < 2.6 && FILTER_METHODS.include?(node.method_name)
90
92
  return unless (range = offense_range(node))
91
93
  return if allowed_receiver?(node.receiver)
92
- if (target_ruby_version <= 3.0 || node.method?(:delete_if)) && to_enum_method?(node)
93
- return
94
- end
94
+ return if target_ruby_version <= 3.0 && to_enum_method?(node)
95
95
 
96
96
  good = good_method_name(node)
97
97
  message = format(MSG, good: good, bad: range.source)
@@ -127,7 +127,7 @@ module RuboCop
127
127
  end
128
128
 
129
129
  def good_method_name(node)
130
- if node.bang_method? || node.method?(:delete_if)
130
+ if node.bang_method?
131
131
  'compact!'
132
132
  else
133
133
  'compact'
@@ -7,6 +7,9 @@ module RuboCop
7
7
  # can be combined into a single loop. It is very likely that combining them
8
8
  # will make the code more efficient and more concise.
9
9
  #
10
+ # NOTE: Autocorrection is not applied when the block variable names differ in separate loops,
11
+ # as it is impossible to determine which variable name should be prioritized.
12
+ #
10
13
  # @safety
11
14
  # The cop is unsafe, because the first loop might modify state that the
12
15
  # second loop depends on; these two aren't combinable.
@@ -61,6 +64,7 @@ module RuboCop
61
64
 
62
65
  MSG = 'Combine this loop with the previous loop.'
63
66
 
67
+ # rubocop:disable Metrics/CyclomaticComplexity
64
68
  def on_block(node)
65
69
  return unless node.parent&.begin_type?
66
70
  return unless collection_looping_method?(node)
@@ -68,9 +72,12 @@ module RuboCop
68
72
  return unless node.body && node.left_sibling.body
69
73
 
70
74
  add_offense(node) do |corrector|
75
+ next unless node.arguments == node.left_sibling.arguments
76
+
71
77
  combine_with_left_sibling(corrector, node)
72
78
  end
73
79
  end
80
+ # rubocop:enable Metrics/CyclomaticComplexity
74
81
 
75
82
  alias on_numblock on_block
76
83
 
@@ -10,7 +10,7 @@ module RuboCop
10
10
  #
11
11
  # Note that some comments
12
12
  # (`:nodoc:`, `:yields:`, `rubocop:disable` and `rubocop:todo`)
13
- # are allowed.
13
+ # and RBS::Inline annotation comments are allowed.
14
14
  #
15
15
  # Autocorrection removes comments from `end` keyword and keeps comments
16
16
  # for `class`, `module`, `def` and `begin` above the keyword.
@@ -82,6 +82,8 @@ module RuboCop
82
82
 
83
83
  def offensive?(comment)
84
84
  line = source_line(comment)
85
+ return false if rbs_inline_annotation?(line, comment)
86
+
85
87
  KEYWORD_REGEXES.any? { |r| r.match?(line) } &&
86
88
  ALLOWED_COMMENT_REGEXES.none? { |r| r.match?(line) }
87
89
  end
@@ -89,6 +91,10 @@ module RuboCop
89
91
  def source_line(comment)
90
92
  comment.source_range.source_line
91
93
  end
94
+
95
+ def rbs_inline_annotation?(line, comment)
96
+ comment.text.start_with?('#:') && line.start_with?(/\A\s*def\s/)
97
+ end
92
98
  end
93
99
  end
94
100
  end
@@ -73,7 +73,7 @@ module RuboCop
73
73
  elsif_branches << node.if_branch
74
74
 
75
75
  else_branch = node.else_branch
76
- if else_branch&.if_type? && else_branch&.elsif?
76
+ if else_branch&.if_type? && else_branch.elsif?
77
77
  expand_elsif(else_branch, elsif_branches)
78
78
  else
79
79
  elsif_branches << else_branch
@@ -36,7 +36,7 @@ module RuboCop
36
36
  def on_class(node)
37
37
  return unless data_define?(node.parent_class)
38
38
 
39
- add_offense(node.parent_class.source_range) do |corrector|
39
+ add_offense(node.parent_class) do |corrector|
40
40
  corrector.remove(range_with_surrounding_space(node.loc.keyword, newlines: false))
41
41
  corrector.replace(node.loc.operator, '=')
42
42
 
@@ -179,6 +179,7 @@ module RuboCop
179
179
 
180
180
  def comment_in_else?(node)
181
181
  node = node.parent while node.if_type? && node.elsif?
182
+ return false unless node.else?
182
183
 
183
184
  processed_source.contains_comment?(node.loc.else.join(node.source_range.end))
184
185
  end
@@ -34,7 +34,7 @@ module RuboCop
34
34
  def_node_matcher :array_node, '(send (const {nil? cbase} :Array) :new (array)?)'
35
35
 
36
36
  # @!method hash_node(node)
37
- def_node_matcher :hash_node, '(send (const {nil? cbase} :Hash) :new (array)?)'
37
+ def_node_matcher :hash_node, '(send (const {nil? cbase} :Hash) :new)'
38
38
 
39
39
  # @!method str_node(node)
40
40
  def_node_matcher :str_node, '(send (const {nil? cbase} :String) :new)'
@@ -136,7 +136,7 @@ module RuboCop
136
136
  actual: line_node.source,
137
137
  expected: expected)
138
138
 
139
- add_offense(line_node.source_range, message: message) do |corrector|
139
+ add_offense(line_node, message: message) do |corrector|
140
140
  corrector.replace(line_node, expected)
141
141
  end
142
142
  end
@@ -234,11 +234,11 @@ module RuboCop
234
234
  end
235
235
 
236
236
  def autocorrect_heredoc_argument(corrector, node, heredoc_branch, leave_branch, guard)
237
+ remove_whole_lines(corrector, node.loc.end)
237
238
  return unless node.else?
238
239
 
239
240
  remove_whole_lines(corrector, leave_branch.source_range)
240
241
  remove_whole_lines(corrector, node.loc.else)
241
- remove_whole_lines(corrector, node.loc.end)
242
242
  remove_whole_lines(corrector, range_of_branch_to_remove(node, guard))
243
243
  corrector.insert_after(
244
244
  heredoc_branch.last_argument.loc.heredoc_end, "\n#{leave_branch.source}"
@@ -57,6 +57,11 @@ module RuboCop
57
57
  (call $(call _ ${:keys :values}) :each (block_pass (sym _)))
58
58
  PATTERN
59
59
 
60
+ # @!method hash_mutated?(node, receiver)
61
+ def_node_matcher :hash_mutated?, <<~PATTERN
62
+ `(send %1 :[]= ...)
63
+ PATTERN
64
+
60
65
  def on_block(node)
61
66
  return unless handleable?(node)
62
67
 
@@ -103,6 +108,7 @@ module RuboCop
103
108
  def handleable?(node)
104
109
  return false if use_array_converter_method_as_preceding?(node)
105
110
  return false unless (root_receiver = root_receiver(node))
111
+ return false if hash_mutated?(node, root_receiver)
106
112
 
107
113
  !root_receiver.literal? || root_receiver.hash_type?
108
114
  end
@@ -68,7 +68,7 @@ module RuboCop
68
68
  # {a: 1, b: 2}
69
69
  # {:c => 3, 'd' => 4}
70
70
  #
71
- # @example EnforcedShorthandSyntax: always (default)
71
+ # @example EnforcedShorthandSyntax: always
72
72
  #
73
73
  # # bad
74
74
  # {foo: foo, bar: bar}
@@ -84,7 +84,7 @@ module RuboCop
84
84
  # # good
85
85
  # {foo: foo, bar: bar}
86
86
  #
87
- # @example EnforcedShorthandSyntax: either
87
+ # @example EnforcedShorthandSyntax: either (default)
88
88
  #
89
89
  # # good
90
90
  # {foo: foo, bar: bar}
@@ -71,7 +71,7 @@ module RuboCop
71
71
 
72
72
  else_branch = node.else_branch
73
73
 
74
- return unless else_branch&.if_type? && else_branch&.if?
74
+ return unless else_branch&.if_type? && else_branch.if?
75
75
  return if allow_if_modifier_in_else_branch?(else_branch)
76
76
 
77
77
  add_offense(else_branch.loc.keyword) do |corrector|
@@ -36,10 +36,12 @@ module RuboCop
36
36
 
37
37
  private
38
38
 
39
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
39
40
  def message(node)
40
41
  template = if node.if_branch&.begin_type?
41
42
  MSG_NEWLINE
42
- elsif node.else_branch&.if_type? || node.else_branch&.begin_type?
43
+ elsif node.else_branch&.if_type? || node.else_branch&.begin_type? ||
44
+ use_block_in_branches?(node)
43
45
  MSG_IF_ELSE
44
46
  else
45
47
  MSG_TERNARY
@@ -47,15 +49,20 @@ module RuboCop
47
49
 
48
50
  format(template, expr: node.condition.source)
49
51
  end
52
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
50
53
 
51
54
  def autocorrect(corrector, node)
52
- if node.if_branch&.begin_type? || node.else_branch&.begin_type?
55
+ if node.branches.compact.any?(&:begin_type?) || use_block_in_branches?(node)
53
56
  corrector.replace(node.loc.begin, "\n")
54
57
  else
55
58
  corrector.replace(node, replacement(node))
56
59
  end
57
60
  end
58
61
 
62
+ def use_block_in_branches?(node)
63
+ node.branches.compact.any? { |branch| branch.block_type? || branch.numblock_type? }
64
+ end
65
+
59
66
  def replacement(node)
60
67
  return correct_elsif(node) if node.else_branch&.if_type?
61
68
 
@@ -74,16 +81,14 @@ module RuboCop
74
81
  RUBY
75
82
  end
76
83
 
77
- # rubocop:disable Metrics/AbcSize
78
84
  def build_expression(expr)
79
- return expr.source if !expr.call_type? || expr.parenthesized? || expr.arguments.empty?
85
+ return expr.source unless require_argument_parentheses?(expr)
80
86
 
81
87
  method = expr.source_range.begin.join(expr.loc.selector.end)
82
88
  arguments = expr.first_argument.source_range.begin.join(expr.source_range.end)
83
89
 
84
90
  "#{method.source}(#{arguments.source})"
85
91
  end
86
- # rubocop:enable Metrics/AbcSize
87
92
 
88
93
  def build_else_branch(second_condition)
89
94
  result = <<~RUBY
@@ -104,6 +109,12 @@ module RuboCop
104
109
 
105
110
  result
106
111
  end
112
+
113
+ def require_argument_parentheses?(node)
114
+ return false unless node.call_type?
115
+
116
+ !node.parenthesized? && node.arguments.any? && !node.method?(:[]) && !node.method?(:[]=)
117
+ end
107
118
  end
108
119
  end
109
120
  end
@@ -68,7 +68,7 @@ module RuboCop
68
68
 
69
69
  return unless offending_selector?(node, selector)
70
70
 
71
- add_offense(node.send_node.source_range, message: message(node, selector)) do |corrector|
71
+ add_offense(node.send_node, message: message(node, selector)) do |corrector|
72
72
  if node.send_node.lambda_literal?
73
73
  LambdaLiteralToMethodCorrector.new(node).call(corrector)
74
74
  else
@@ -105,26 +105,21 @@ module RuboCop
105
105
 
106
106
  # Value object to extract source ranges for the different parts of a magic comment
107
107
  class CommentRange
108
+ extend SimpleForwardable
109
+
108
110
  DIRECTIVE_REGEXP = Regexp.union(MagicComment::KEYWORDS.map do |_, v|
109
111
  Regexp.new(v, Regexp::IGNORECASE)
110
112
  end).freeze
111
113
 
112
114
  VALUE_REGEXP = Regexp.new("(?:#{DIRECTIVE_REGEXP}:\s*)(.*?)(?=;|$)")
113
115
 
116
+ def_delegators :@comment, :text, :loc
114
117
  attr_reader :comment
115
118
 
116
119
  def initialize(comment)
117
120
  @comment = comment
118
121
  end
119
122
 
120
- def text
121
- @comment.text
122
- end
123
-
124
- def loc
125
- @comment.loc
126
- end
127
-
128
123
  # A magic comment can contain one directive (normal style) or
129
124
  # multiple directives (emacs style)
130
125
  def directives