rubocop 1.66.0 → 1.67.0

Sign up to get free protection for your applications and to get access to all the features.
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