rubocop 1.58.0 → 1.60.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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +3 -3
  4. data/config/default.yml +9 -1
  5. data/lib/rubocop/config.rb +0 -2
  6. data/lib/rubocop/config_loader.rb +0 -1
  7. data/lib/rubocop/config_validator.rb +0 -2
  8. data/lib/rubocop/cop/base.rb +6 -0
  9. data/lib/rubocop/cop/exclude_limit.rb +1 -1
  10. data/lib/rubocop/cop/layout/end_alignment.rb +5 -1
  11. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +16 -1
  12. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -1
  13. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +1 -1
  14. data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -1
  15. data/lib/rubocop/cop/layout/single_line_block_chain.rb +1 -0
  16. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +2 -2
  17. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  18. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +2 -1
  19. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +56 -0
  20. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +23 -2
  21. data/lib/rubocop/cop/lint/next_without_accumulator.rb +6 -21
  22. data/lib/rubocop/cop/lint/number_conversion.rb +9 -4
  23. data/lib/rubocop/cop/lint/redundant_with_index.rb +2 -2
  24. data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
  25. data/lib/rubocop/cop/lint/self_assignment.rb +1 -0
  26. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -0
  27. data/lib/rubocop/cop/lint/syntax.rb +6 -3
  28. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +2 -2
  29. data/lib/rubocop/cop/lint/useless_times.rb +1 -1
  30. data/lib/rubocop/cop/metrics/abc_size.rb +3 -3
  31. data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -0
  32. data/lib/rubocop/cop/naming/block_forwarding.rb +10 -2
  33. data/lib/rubocop/cop/naming/constant_name.rb +1 -2
  34. data/lib/rubocop/cop/registry.rb +4 -4
  35. data/lib/rubocop/cop/security/open.rb +2 -2
  36. data/lib/rubocop/cop/style/arguments_forwarding.rb +16 -3
  37. data/lib/rubocop/cop/style/array_first_last.rb +1 -1
  38. data/lib/rubocop/cop/style/class_check.rb +1 -0
  39. data/lib/rubocop/cop/style/collection_compact.rb +7 -6
  40. data/lib/rubocop/cop/style/combinable_loops.rb +11 -0
  41. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -0
  42. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  43. data/lib/rubocop/cop/style/date_time.rb +5 -4
  44. data/lib/rubocop/cop/style/each_for_simple_loop.rb +7 -7
  45. data/lib/rubocop/cop/style/each_with_object.rb +2 -2
  46. data/lib/rubocop/cop/style/eval_with_location.rb +0 -11
  47. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
  48. data/lib/rubocop/cop/style/hash_each_methods.rb +29 -4
  49. data/lib/rubocop/cop/style/hash_except.rb +2 -1
  50. data/lib/rubocop/cop/style/identical_conditional_branches.rb +4 -1
  51. data/lib/rubocop/cop/style/invertible_unless_condition.rb +39 -2
  52. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +3 -2
  53. data/lib/rubocop/cop/style/map_to_hash.rb +9 -4
  54. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +14 -5
  55. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +1 -3
  56. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +20 -0
  57. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -3
  58. data/lib/rubocop/cop/style/next.rb +1 -1
  59. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +1 -1
  60. data/lib/rubocop/cop/style/operator_method_call.rb +2 -2
  61. data/lib/rubocop/cop/style/parallel_assignment.rb +2 -2
  62. data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -0
  63. data/lib/rubocop/cop/style/redundant_argument.rb +1 -0
  64. data/lib/rubocop/cop/style/redundant_each.rb +7 -4
  65. data/lib/rubocop/cop/style/redundant_fetch_block.rb +3 -3
  66. data/lib/rubocop/cop/style/redundant_line_continuation.rb +8 -1
  67. data/lib/rubocop/cop/style/redundant_parentheses.rb +15 -7
  68. data/lib/rubocop/cop/style/redundant_self.rb +17 -2
  69. data/lib/rubocop/cop/style/redundant_sort.rb +8 -7
  70. data/lib/rubocop/cop/style/redundant_sort_by.rb +2 -2
  71. data/lib/rubocop/cop/style/sample.rb +2 -1
  72. data/lib/rubocop/cop/style/select_by_regexp.rb +6 -5
  73. data/lib/rubocop/cop/style/single_argument_dig.rb +5 -2
  74. data/lib/rubocop/cop/style/slicing_with_range.rb +76 -10
  75. data/lib/rubocop/cop/style/string_chars.rb +1 -0
  76. data/lib/rubocop/cop/style/strip.rb +7 -4
  77. data/lib/rubocop/cop/style/symbol_proc.rb +36 -0
  78. data/lib/rubocop/cop/style/unpack_first.rb +11 -14
  79. data/lib/rubocop/cops_documentation_generator.rb +11 -1
  80. data/lib/rubocop/ext/regexp_node.rb +9 -4
  81. data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
  82. data/lib/rubocop/formatter/json_formatter.rb +0 -1
  83. data/lib/rubocop/formatter.rb +1 -1
  84. data/lib/rubocop/lsp/routes.rb +1 -1
  85. data/lib/rubocop/options.rb +0 -8
  86. data/lib/rubocop/rspec/shared_contexts.rb +6 -0
  87. data/lib/rubocop/rspec/support.rb +1 -0
  88. data/lib/rubocop/server/cache.rb +0 -1
  89. data/lib/rubocop/version.rb +1 -1
  90. data/lib/rubocop.rb +1 -0
  91. metadata +8 -7
  92. /data/lib/rubocop/formatter/{git_hub_actions_formatter.rb → github_actions_formatter.rb} +0 -0
@@ -186,12 +186,12 @@ module RuboCop
186
186
  rest_arg, kwrest_arg, _block_arg = *forwardable_args
187
187
 
188
188
  send_classifications.each do |send_node, _c, forward_rest, forward_kwrest|
189
- if forward_rest
189
+ if outside_block?(forward_rest)
190
190
  register_forward_args_offense(def_node.arguments, rest_arg)
191
191
  register_forward_args_offense(send_node, forward_rest)
192
192
  end
193
193
 
194
- if forward_kwrest
194
+ if outside_block?(forward_kwrest)
195
195
  register_forward_kwargs_offense(!forward_rest, def_node.arguments, kwrest_arg)
196
196
  register_forward_kwargs_offense(!forward_rest, send_node, forward_kwrest)
197
197
  end
@@ -250,6 +250,12 @@ module RuboCop
250
250
  redundant_arg_names.include?(arg.source) ? arg : nil
251
251
  end
252
252
 
253
+ def outside_block?(node)
254
+ return false unless node
255
+
256
+ node.each_ancestor(:block, :numblock).none?
257
+ end
258
+
253
259
  def register_forward_args_offense(def_arguments_or_send, rest_arg_or_splat)
254
260
  add_offense(rest_arg_or_splat, message: ARGS_MSG) do |corrector|
255
261
  add_parens_if_missing(def_arguments_or_send, corrector)
@@ -340,7 +346,7 @@ module RuboCop
340
346
  end
341
347
 
342
348
  def classification
343
- return nil unless forwarded_rest_arg || forwarded_kwrest_arg
349
+ return nil unless forwarded_rest_arg || forwarded_kwrest_arg || forwarded_block_arg
344
350
 
345
351
  if can_forward_all?
346
352
  :all
@@ -424,9 +430,16 @@ module RuboCop
424
430
  def no_additional_args?
425
431
  forwardable_count = [@rest_arg, @kwrest_arg, @block_arg].compact.size
426
432
 
433
+ return false if missing_rest_arg_or_kwrest_arg?
434
+
427
435
  @def_node.arguments.size == forwardable_count &&
428
436
  @send_node.arguments.size == forwardable_count
429
437
  end
438
+
439
+ def missing_rest_arg_or_kwrest_arg?
440
+ (@rest_arg_name && !forwarded_rest_arg) ||
441
+ (@kwrest_arg_name && !forwarded_kwrest_arg)
442
+ end
430
443
  end
431
444
  end
432
445
  end
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Identifies usages of `arr[0]` and `arr[-1]` and suggests to change
7
- # them to use `arr.first` and `arr.instead`.
7
+ # them to use `arr.first` and `arr.last` instead.
8
8
  #
9
9
  # The cop is disabled by default due to safety concerns.
10
10
  #
@@ -40,6 +40,7 @@ module RuboCop
40
40
  corrector.replace(node.loc.selector, replacement)
41
41
  end
42
42
  end
43
+ alias on_csend on_send
43
44
 
44
45
  def message(node)
45
46
  if node.method?(:is_a?)
@@ -53,7 +53,7 @@ module RuboCop
53
53
 
54
54
  # @!method reject_method_with_block_pass?(node)
55
55
  def_node_matcher :reject_method_with_block_pass?, <<~PATTERN
56
- (send !nil? {:reject :delete_if :reject!}
56
+ (call !nil? {:reject :delete_if :reject!}
57
57
  (block_pass
58
58
  (sym :nil?)))
59
59
  PATTERN
@@ -61,21 +61,21 @@ module RuboCop
61
61
  # @!method reject_method?(node)
62
62
  def_node_matcher :reject_method?, <<~PATTERN
63
63
  (block
64
- (send
64
+ (call
65
65
  !nil? {:reject :delete_if :reject!})
66
66
  $(args ...)
67
- (send
67
+ (call
68
68
  $(lvar _) :nil?))
69
69
  PATTERN
70
70
 
71
71
  # @!method select_method?(node)
72
72
  def_node_matcher :select_method?, <<~PATTERN
73
73
  (block
74
- (send
74
+ (call
75
75
  !nil? {:select :select!})
76
76
  $(args ...)
77
- (send
78
- (send
77
+ (call
78
+ (call
79
79
  $(lvar _) :nil?) :!))
80
80
  PATTERN
81
81
 
@@ -91,6 +91,7 @@ module RuboCop
91
91
 
92
92
  add_offense(range, message: message) { |corrector| corrector.replace(range, good) }
93
93
  end
94
+ alias on_csend on_send
94
95
 
95
96
  private
96
97
 
@@ -105,6 +105,17 @@ module RuboCop
105
105
  def combine_with_left_sibling(corrector, node)
106
106
  corrector.remove(node.left_sibling.body.source_range.end.join(node.left_sibling.loc.end))
107
107
  corrector.remove(node.source_range.begin.join(node.body.source_range.begin))
108
+
109
+ correct_end_of_block(corrector, node)
110
+ end
111
+
112
+ def correct_end_of_block(corrector, node)
113
+ return unless node.left_sibling.respond_to?(:braces?)
114
+ return if node.right_sibling&.block_type? || node.right_sibling&.numblock_type?
115
+
116
+ end_of_block = node.left_sibling.braces? ? '}' : ' end'
117
+ corrector.remove(node.loc.end)
118
+ corrector.insert_before(node.source_range.end, end_of_block)
108
119
  end
109
120
  end
110
121
  end
@@ -63,6 +63,7 @@ module RuboCop
63
63
  end
64
64
  end
65
65
  # rubocop:enable Metrics
66
+ alias on_csend on_send
66
67
 
67
68
  private
68
69
 
@@ -233,7 +233,7 @@ module RuboCop
233
233
  PATTERN
234
234
 
235
235
  ASSIGNMENT_TYPES.each do |type|
236
- define_method "on_#{type}" do |node|
236
+ define_method :"on_#{type}" do |node|
237
237
  return if part_of_ignored_node?(node)
238
238
  return if node.parent&.shorthand_asgn?
239
239
 
@@ -49,12 +49,12 @@ module RuboCop
49
49
  class DateTime < Base
50
50
  extend AutoCorrector
51
51
 
52
- CLASS_MSG = 'Prefer Time over DateTime.'
53
- COERCION_MSG = 'Do not use #to_datetime.'
52
+ CLASS_MSG = 'Prefer `Time` over `DateTime`.'
53
+ COERCION_MSG = 'Do not use `#to_datetime`.'
54
54
 
55
55
  # @!method date_time?(node)
56
56
  def_node_matcher :date_time?, <<~PATTERN
57
- (send (const {nil? (cbase)} :DateTime) ...)
57
+ (call (const {nil? (cbase)} :DateTime) ...)
58
58
  PATTERN
59
59
 
60
60
  # @!method historic_date?(node)
@@ -64,7 +64,7 @@ module RuboCop
64
64
 
65
65
  # @!method to_datetime?(node)
66
66
  def_node_matcher :to_datetime?, <<~PATTERN
67
- (send _ :to_datetime)
67
+ (call _ :to_datetime)
68
68
  PATTERN
69
69
 
70
70
  def on_send(node)
@@ -75,6 +75,7 @@ module RuboCop
75
75
 
76
76
  add_offense(node, message: message) { |corrector| autocorrect(corrector, node) }
77
77
  end
78
+ alias on_csend on_send
78
79
 
79
80
  private
80
81
 
@@ -32,27 +32,27 @@ module RuboCop
32
32
 
33
33
  send_node = node.send_node
34
34
 
35
- range = send_node.receiver.source_range.join(send_node.loc.selector)
36
-
37
- add_offense(range) do |corrector|
35
+ add_offense(send_node) do |corrector|
38
36
  range_type, min, max = each_range(node)
39
37
 
40
38
  max += 1 if range_type == :irange
41
39
 
42
- corrector.replace(node.send_node, "#{max - min}.times")
40
+ corrector.replace(send_node, "#{max - min}.times")
43
41
  end
44
42
  end
45
43
 
46
44
  private
47
45
 
48
46
  def offending?(node)
47
+ return false unless node.arguments.empty?
48
+
49
49
  each_range_with_zero_origin?(node) || each_range_without_block_argument?(node)
50
50
  end
51
51
 
52
52
  # @!method each_range(node)
53
53
  def_node_matcher :each_range, <<~PATTERN
54
54
  (block
55
- (send
55
+ (call
56
56
  (begin
57
57
  (${irange erange}
58
58
  (int $_) (int $_)))
@@ -64,7 +64,7 @@ module RuboCop
64
64
  # @!method each_range_with_zero_origin?(node)
65
65
  def_node_matcher :each_range_with_zero_origin?, <<~PATTERN
66
66
  (block
67
- (send
67
+ (call
68
68
  (begin
69
69
  ({irange erange}
70
70
  (int 0) (int _)))
@@ -76,7 +76,7 @@ module RuboCop
76
76
  # @!method each_range_without_block_argument?(node)
77
77
  def_node_matcher :each_range_without_block_argument?, <<~PATTERN
78
78
  (block
79
- (send
79
+ (call
80
80
  (begin
81
81
  ({irange erange}
82
82
  (int _) (int _)))
@@ -58,12 +58,12 @@ module RuboCop
58
58
 
59
59
  # @!method each_with_object_block_candidate?(node)
60
60
  def_node_matcher :each_with_object_block_candidate?, <<~PATTERN
61
- (block $(send _ {:inject :reduce} _) $_ $_)
61
+ (block $(call _ {:inject :reduce} _) $_ $_)
62
62
  PATTERN
63
63
 
64
64
  # @!method each_with_object_numblock_candidate?(node)
65
65
  def_node_matcher :each_with_object_numblock_candidate?, <<~PATTERN
66
- (numblock $(send _ {:inject :reduce} _) 2 $_)
66
+ (numblock $(call _ {:inject :reduce} _) 2 $_)
67
67
  PATTERN
68
68
 
69
69
  def autocorrect_block(corrector, node, return_value)
@@ -128,17 +128,6 @@ module RuboCop
128
128
  node.method?(:eval) ? node.arguments.size >= 2 : true
129
129
  end
130
130
 
131
- # FIXME: It's a Style/ConditionalAssignment's false positive.
132
- # rubocop:disable Style/ConditionalAssignment
133
- def with_lineno?(node)
134
- if node.method?(:eval)
135
- node.arguments.size == 4
136
- else
137
- node.arguments.size == 3
138
- end
139
- end
140
- # rubocop:enable Style/ConditionalAssignment
141
-
142
131
  def add_offense_for_incorrect_line(method_name, line_node, sign, line_diff)
143
132
  expected = expected_line(sign, line_diff)
144
133
  message = format(MSG_INCORRECT_LINE,
@@ -30,7 +30,7 @@ module RuboCop
30
30
 
31
31
  # @!method exact_regexp_match(node)
32
32
  def_node_matcher :exact_regexp_match, <<~PATTERN
33
- (send
33
+ (call
34
34
  _ {:=~ :=== :!~ :match :match?}
35
35
  (regexp
36
36
  (str $_)
@@ -49,6 +49,7 @@ module RuboCop
49
49
  corrector.replace(node, prefer)
50
50
  end
51
51
  end
52
+ alias on_csend on_send
52
53
 
53
54
  private
54
55
 
@@ -58,18 +58,20 @@ module RuboCop
58
58
 
59
59
  # rubocop:disable Metrics/AbcSize
60
60
  def on_block(node)
61
+ return unless handleable?(node)
62
+
61
63
  kv_each(node) do |target, method|
62
64
  register_kv_offense(target, method) and return
63
65
  end
64
66
 
65
67
  return unless (key, value = each_arguments(node))
66
68
 
67
- if unused_block_arg_exist?(node, value.source)
69
+ if unused_block_arg_exist?(node, value)
68
70
  message = message('each_key', node.method_name, value.source)
69
71
  unused_range = key.source_range.end.join(value.source_range.end)
70
72
 
71
73
  register_each_args_offense(node, message, 'each_key', unused_range)
72
- elsif unused_block_arg_exist?(node, key.source)
74
+ elsif unused_block_arg_exist?(node, key)
73
75
  message = message('each_value', node.method_name, key.source)
74
76
  unused_range = key.source_range.begin.join(value.source_range.begin)
75
77
 
@@ -88,6 +90,12 @@ module RuboCop
88
90
 
89
91
  private
90
92
 
93
+ def handleable?(node)
94
+ return false unless (root_receiver = root_receiver(node))
95
+
96
+ !root_receiver.literal? || root_receiver.hash_type?
97
+ end
98
+
91
99
  def register_kv_offense(target, method)
92
100
  return unless (parent_receiver = target.receiver.receiver)
93
101
  return if allowed_receiver?(parent_receiver)
@@ -99,8 +107,16 @@ module RuboCop
99
107
  end
100
108
  end
101
109
 
102
- def unused_block_arg_exist?(node, block_arg_source)
103
- node.body.each_descendant(:lvar).map(&:source).none?(block_arg_source)
110
+ def unused_block_arg_exist?(node, block_arg)
111
+ lvar_sources = node.body.each_descendant(:lvar).map(&:source)
112
+
113
+ if block_arg.mlhs_type?
114
+ block_arg.each_descendant(:arg, :restarg).all? do |block_arg|
115
+ lvar_sources.none?(block_arg.source.delete_prefix('*'))
116
+ end
117
+ else
118
+ lvar_sources.none?(block_arg.source.delete_prefix('*'))
119
+ end
104
120
  end
105
121
 
106
122
  def message(prefer, method_name, unused_code)
@@ -127,6 +143,15 @@ module RuboCop
127
143
  end
128
144
  end
129
145
 
146
+ def root_receiver(node)
147
+ receiver = node.receiver
148
+ if receiver&.receiver
149
+ root_receiver(receiver)
150
+ else
151
+ receiver
152
+ end
153
+ end
154
+
130
155
  def format_message(method_name, current)
131
156
  format(MSG, prefer: "each_#{method_name[0..-2]}", current: current)
132
157
  end
@@ -43,7 +43,7 @@ module RuboCop
43
43
  # @!method bad_method_with_poro?(node)
44
44
  def_node_matcher :bad_method_with_poro?, <<~PATTERN
45
45
  (block
46
- (send _ _)
46
+ (call _ _)
47
47
  (args
48
48
  $(arg _)
49
49
  (arg _))
@@ -86,6 +86,7 @@ module RuboCop
86
86
  corrector.replace(range, preferred_method)
87
87
  end
88
88
  end
89
+ alias on_csend on_send
89
90
 
90
91
  private
91
92
 
@@ -158,7 +158,10 @@ module RuboCop
158
158
  if head.assignment?
159
159
  # The `send` node is used instead of the `indexasgn` node, so `name` cannot be used.
160
160
  # https://github.com/rubocop/rubocop-ast/blob/v1.29.0/lib/rubocop/ast/node/indexasgn_node.rb
161
- assigned_value = head.send_type? ? head.receiver.source : head.name.to_s
161
+ #
162
+ # FIXME: It would be better to update `RuboCop::AST::OpAsgnNode` or its subclasses to
163
+ # handle `self.foo ||= value` as a solution, instead of using `head.node_parts[0].to_s`.
164
+ assigned_value = head.send_type? ? head.receiver.source : head.node_parts[0].to_s
162
165
 
163
166
  return if condition_variable == assigned_value
164
167
  end
@@ -51,7 +51,7 @@ module RuboCop
51
51
  class InvertibleUnlessCondition < Base
52
52
  extend AutoCorrector
53
53
 
54
- MSG = 'Favor `if` with inverted condition over `unless`.'
54
+ MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
55
55
 
56
56
  def on_if(node)
57
57
  return unless node.unless?
@@ -59,7 +59,10 @@ module RuboCop
59
59
  condition = node.condition
60
60
  return unless invertible?(condition)
61
61
 
62
- add_offense(node) do |corrector|
62
+ message = format(MSG, prefer: "#{node.inverse_keyword} #{preferred_condition(condition)}",
63
+ current: "#{node.keyword} #{condition.source}")
64
+
65
+ add_offense(node, message: message) do |corrector|
63
66
  corrector.replace(node.loc.keyword, node.inverse_keyword)
64
67
  autocorrect(corrector, condition)
65
68
  end
@@ -88,6 +91,40 @@ module RuboCop
88
91
  (argument.const_type? && argument.short_name.to_s.upcase != argument.short_name.to_s)
89
92
  end
90
93
 
94
+ def preferred_condition(node)
95
+ case node.type
96
+ when :begin then "(#{preferred_condition(node.children.first)})"
97
+ when :send then preferred_send_condition(node)
98
+ when :or, :and then preferred_logical_condition(node)
99
+ end
100
+ end
101
+
102
+ def preferred_send_condition(node)
103
+ receiver_source = node.receiver.source
104
+ return receiver_source if node.method?(:!)
105
+
106
+ inverse_method_name = inverse_methods[node.method_name]
107
+ return "#{receiver_source}.#{inverse_method_name}" unless node.arguments?
108
+
109
+ argument_list = node.arguments.map(&:source).join(', ')
110
+ if node.operator_method?
111
+ return "#{receiver_source} #{inverse_method_name} #{argument_list}"
112
+ end
113
+
114
+ if node.parenthesized?
115
+ return "#{receiver_source}.#{inverse_method_name}(#{argument_list})"
116
+ end
117
+
118
+ "#{receiver_source}.#{inverse_method_name} #{argument_list}"
119
+ end
120
+
121
+ def preferred_logical_condition(node)
122
+ preferred_lhs = preferred_condition(node.lhs)
123
+ preferred_rhs = preferred_condition(node.rhs)
124
+
125
+ "#{preferred_lhs} #{node.inverse_operator} #{preferred_rhs}"
126
+ end
127
+
91
128
  def autocorrect(corrector, node)
92
129
  case node.type
93
130
  when :begin
@@ -44,9 +44,9 @@ module RuboCop
44
44
 
45
45
  # @!method map_and_compact?(node)
46
46
  def_node_matcher :map_and_compact?, <<~RUBY
47
- (send
47
+ (call
48
48
  (block
49
- (send _ :map)
49
+ (call _ :map)
50
50
  (args
51
51
  $(arg _))
52
52
  {
@@ -85,6 +85,7 @@ module RuboCop
85
85
  end
86
86
  end
87
87
  end
88
+ alias on_csend on_send
88
89
 
89
90
  private
90
91
 
@@ -37,8 +37,8 @@ module RuboCop
37
37
  MSG = 'Pass a block to `to_h` instead of calling `%<method>s%<dot>sto_h`.'
38
38
  RESTRICT_ON_SEND = %i[to_h].freeze
39
39
 
40
- # @!method map_to_h?(node)
41
- def_node_matcher :map_to_h?, <<~PATTERN
40
+ # @!method map_to_h(node)
41
+ def_node_matcher :map_to_h, <<~PATTERN
42
42
  {
43
43
  $(call ({block numblock} $(call _ {:map :collect}) ...) :to_h)
44
44
  $(call $(call _ {:map :collect} (block_pass sym)) :to_h)
@@ -50,9 +50,9 @@ module RuboCop
50
50
  end
51
51
 
52
52
  def on_send(node)
53
- return unless (to_h_node, map_node = map_to_h?(node))
53
+ return unless (to_h_node, map_node = map_to_h(node))
54
54
 
55
- message = format(MSG, method: map_node.loc.selector.source, dot: map_node.loc.dot.source)
55
+ message = format(MSG, method: map_node.loc.selector.source, dot: to_h_node.loc.dot.source)
56
56
  add_offense(map_node.loc.selector, message: message) do |corrector|
57
57
  # If the `to_h` call already has a block, do not autocorrect.
58
58
  next if to_h_node.block_node
@@ -64,12 +64,17 @@ module RuboCop
64
64
 
65
65
  private
66
66
 
67
+ # rubocop:disable Metrics/AbcSize
67
68
  def autocorrect(corrector, to_h, map)
68
69
  removal_range = range_between(to_h.loc.dot.begin_pos, to_h.loc.selector.end_pos)
69
70
 
70
71
  corrector.remove(range_with_surrounding_space(removal_range, side: :left))
72
+ if (map_dot = map.loc.dot)
73
+ corrector.replace(map_dot, to_h.loc.dot.source)
74
+ end
71
75
  corrector.replace(map.loc.selector, 'to_h')
72
76
  end
77
+ # rubocop:enable Metrics/AbcSize
73
78
  end
74
79
  end
75
80
  end
@@ -86,6 +86,7 @@ module RuboCop
86
86
 
87
87
  def legitimate_call_with_parentheses?(node) # rubocop:disable Metrics/PerceivedComplexity
88
88
  call_in_literals?(node) ||
89
+ node.parent&.when_type? ||
89
90
  call_with_ambiguous_arguments?(node) ||
90
91
  call_in_logical_operators?(node) ||
91
92
  call_in_optional_arguments?(node) ||
@@ -126,23 +127,31 @@ module RuboCop
126
127
 
127
128
  def call_with_ambiguous_arguments?(node) # rubocop:disable Metrics/PerceivedComplexity
128
129
  call_with_braced_block?(node) ||
130
+ call_in_argument_with_block?(node) ||
129
131
  call_as_argument_or_chain?(node) ||
130
132
  call_in_match_pattern?(node) ||
131
133
  hash_literal_in_arguments?(node) ||
132
134
  node.descendants.any? do |n|
133
- n.forwarded_args_type? || ambiguous_literal?(n) || logical_operator?(n) ||
134
- call_with_braced_block?(n)
135
+ n.forwarded_args_type? || n.block_type? ||
136
+ ambiguous_literal?(n) || logical_operator?(n)
135
137
  end
136
138
  end
137
139
 
138
140
  def call_with_braced_block?(node)
139
- (node.send_type? || node.super_type?) && node.block_node&.braces?
141
+ (node.call_type? || node.super_type?) && node.block_node&.braces?
142
+ end
143
+
144
+ def call_in_argument_with_block?(node)
145
+ parent = node.parent&.block_type? && node.parent&.parent
146
+ return false unless parent
147
+
148
+ parent.call_type? || parent.super_type? || parent.yield_type?
140
149
  end
141
150
 
142
151
  def call_as_argument_or_chain?(node)
143
152
  node.parent &&
144
- ((node.parent.send_type? && !assigned_before?(node.parent, node)) ||
145
- node.parent.csend_type? || node.parent.super_type? || node.parent.yield_type?)
153
+ (node.parent.call_type? || node.parent.super_type? || node.parent.yield_type?) &&
154
+ !assigned_before?(node.parent, node)
146
155
  end
147
156
 
148
157
  def call_in_match_pattern?(node)
@@ -218,15 +218,13 @@ module RuboCop
218
218
  send(style, node) # call require_parentheses or omit_parentheses
219
219
  end
220
220
  alias on_csend on_send
221
- alias on_super on_send
222
221
  alias on_yield on_send
223
222
 
224
223
  private
225
224
 
226
225
  def args_begin(node)
227
226
  loc = node.loc
228
- selector =
229
- node.super_type? || node.yield_type? ? loc.keyword : loc.selector
227
+ selector = node.yield_type? ? loc.keyword : loc.selector
230
228
 
231
229
  resize_by = args_parenthesized?(node) ? 2 : 1
232
230
  selector.end.resize(resize_by)
@@ -8,6 +8,9 @@ module RuboCop
8
8
  # This cop can be customized allowed methods with `AllowedMethods`.
9
9
  # By default, there are no methods to allowed.
10
10
  #
11
+ # NOTE: This cop allows the use of `it()` without arguments in blocks,
12
+ # as in `0.times { it() }`, following `Lint/ItWithoutArgumentsInBlock` cop.
13
+ #
11
14
  # @example
12
15
  # # bad
13
16
  # object.some_method()
@@ -30,15 +33,18 @@ module RuboCop
30
33
 
31
34
  MSG = 'Do not use parentheses for method calls with no arguments.'
32
35
 
36
+ # rubocop:disable Metrics/CyclomaticComplexity
33
37
  def on_send(node)
34
38
  return unless !node.arguments? && node.parenthesized?
35
39
  return if ineligible_node?(node)
36
40
  return if default_argument?(node)
37
41
  return if allowed_method_name?(node.method_name)
38
42
  return if same_name_assignment?(node)
43
+ return if parenthesized_it_method_in_block?(node)
39
44
 
40
45
  register_offense(node)
41
46
  end
47
+ # rubocop:enable Metrics/CyclomaticComplexity
42
48
 
43
49
  private
44
50
 
@@ -71,6 +77,20 @@ module RuboCop
71
77
  end
72
78
  end
73
79
 
80
+ # Respects `Lint/ItWithoutArgumentsInBlock` cop and the following Ruby 3.3's warning:
81
+ #
82
+ # $ ruby -e '0.times { begin; it; end }'
83
+ # -e:1: warning: `it` calls without arguments will refer to the first block param in
84
+ # Ruby 3.4; use it() or self.it
85
+ #
86
+ def parenthesized_it_method_in_block?(node)
87
+ return false unless node.method?(:it)
88
+ return false unless (block_node = node.each_ancestor(:block).first)
89
+ return false unless block_node.arguments.empty_and_without_delimiters?
90
+
91
+ !node.receiver && node.arguments.empty? && !node.block_literal?
92
+ end
93
+
74
94
  def any_assignment?(node)
75
95
  node.each_ancestor(*AST::Node::ASSIGNMENTS).any? do |asgn_node|
76
96
  # `obj.method = value` parses as (send ... :method= ...), and will
@@ -54,12 +54,10 @@ module RuboCop
54
54
  private
55
55
 
56
56
  def offense?(node)
57
- node.ternary? && node.multiline?
57
+ node.ternary? && node.multiline? && node.source != replacement(node)
58
58
  end
59
59
 
60
60
  def autocorrect(corrector, node)
61
- return unless offense?(node)
62
-
63
61
  corrector.replace(node, replacement(node))
64
62
  return unless (parent = node.parent)
65
63
  return unless (comments_in_condition = comments_in_condition(node))
@@ -66,7 +66,7 @@ module RuboCop
66
66
  end
67
67
 
68
68
  def on_block(node)
69
- return unless node.send_node.send_type? && node.send_node.enumerator_method?
69
+ return unless node.send_node.call_type? && node.send_node.enumerator_method?
70
70
 
71
71
  check(node)
72
72
  end
@@ -62,7 +62,7 @@ module RuboCop
62
62
  private
63
63
 
64
64
  def message(node)
65
- self.class.const_get("#{literal_type(node).upcase}_MSG")
65
+ self.class.const_get(:"#{literal_type(node).upcase}_MSG")
66
66
  end
67
67
 
68
68
  def literal_type(node)