rubocop 1.57.2 → 1.60.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +4 -4
  4. data/config/default.yml +46 -3
  5. data/lib/rubocop/config.rb +0 -2
  6. data/lib/rubocop/config_loader.rb +0 -1
  7. data/lib/rubocop/config_obsoletion.rb +11 -8
  8. data/lib/rubocop/config_validator.rb +0 -2
  9. data/lib/rubocop/cop/base.rb +6 -0
  10. data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
  11. data/lib/rubocop/cop/exclude_limit.rb +1 -1
  12. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +2 -2
  13. data/lib/rubocop/cop/internal_affairs/example_description.rb +4 -4
  14. data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +19 -20
  15. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +53 -0
  16. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -2
  17. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  18. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  19. data/lib/rubocop/cop/layout/end_alignment.rb +5 -1
  20. data/lib/rubocop/cop/layout/extra_spacing.rb +4 -10
  21. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +22 -7
  22. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  23. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  24. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -1
  25. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +1 -1
  26. data/lib/rubocop/cop/layout/redundant_line_break.rb +7 -2
  27. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +4 -4
  28. data/lib/rubocop/cop/layout/single_line_block_chain.rb +5 -0
  29. data/lib/rubocop/cop/layout/space_around_operators.rb +50 -20
  30. data/lib/rubocop/cop/lint/assignment_in_condition.rb +4 -4
  31. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +2 -2
  32. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  33. data/lib/rubocop/cop/lint/debugger.rb +2 -1
  34. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
  35. data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -3
  36. data/lib/rubocop/cop/lint/float_comparison.rb +10 -0
  37. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +2 -1
  38. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +56 -0
  39. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +85 -0
  40. data/lib/rubocop/cop/lint/next_without_accumulator.rb +6 -21
  41. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -5
  42. data/lib/rubocop/cop/lint/number_conversion.rb +9 -4
  43. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +43 -0
  44. data/lib/rubocop/cop/lint/redundant_with_index.rb +2 -2
  45. data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
  46. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -4
  47. data/lib/rubocop/cop/lint/self_assignment.rb +38 -0
  48. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -0
  49. data/lib/rubocop/cop/lint/symbol_conversion.rb +7 -2
  50. data/lib/rubocop/cop/lint/syntax.rb +6 -3
  51. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
  52. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +2 -2
  53. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  54. data/lib/rubocop/cop/lint/useless_times.rb +1 -1
  55. data/lib/rubocop/cop/lint/void.rb +14 -1
  56. data/lib/rubocop/cop/metrics/abc_size.rb +3 -3
  57. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  58. data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -0
  59. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  60. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  61. data/lib/rubocop/cop/naming/block_forwarding.rb +12 -4
  62. data/lib/rubocop/cop/naming/constant_name.rb +1 -2
  63. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  64. data/lib/rubocop/cop/security/open.rb +2 -2
  65. data/lib/rubocop/cop/style/access_modifier_declarations.rb +2 -2
  66. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  67. data/lib/rubocop/cop/style/arguments_forwarding.rb +127 -17
  68. data/lib/rubocop/cop/style/array_first_last.rb +64 -0
  69. data/lib/rubocop/cop/style/auto_resource_cleanup.rb +21 -14
  70. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +2 -2
  71. data/lib/rubocop/cop/style/case_like_if.rb +4 -4
  72. data/lib/rubocop/cop/style/class_check.rb +1 -0
  73. data/lib/rubocop/cop/style/collection_compact.rb +18 -8
  74. data/lib/rubocop/cop/style/combinable_loops.rb +13 -7
  75. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -0
  76. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
  77. data/lib/rubocop/cop/style/date_time.rb +5 -4
  78. data/lib/rubocop/cop/style/each_for_simple_loop.rb +7 -7
  79. data/lib/rubocop/cop/style/each_with_object.rb +2 -2
  80. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  81. data/lib/rubocop/cop/style/eval_with_location.rb +3 -14
  82. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
  83. data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
  84. data/lib/rubocop/cop/style/hash_each_methods.rb +105 -11
  85. data/lib/rubocop/cop/style/hash_except.rb +2 -1
  86. data/lib/rubocop/cop/style/identical_conditional_branches.rb +4 -1
  87. data/lib/rubocop/cop/style/inverse_methods.rb +6 -5
  88. data/lib/rubocop/cop/style/invertible_unless_condition.rb +39 -2
  89. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +3 -2
  90. data/lib/rubocop/cop/style/map_to_hash.rb +17 -7
  91. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +14 -5
  92. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -4
  93. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +20 -0
  94. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  95. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +2 -2
  96. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -3
  97. data/lib/rubocop/cop/style/next.rb +1 -1
  98. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +1 -1
  99. data/lib/rubocop/cop/style/operator_method_call.rb +2 -2
  100. data/lib/rubocop/cop/style/parallel_assignment.rb +2 -2
  101. data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -0
  102. data/lib/rubocop/cop/style/redundant_argument.rb +3 -2
  103. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +3 -3
  104. data/lib/rubocop/cop/style/redundant_each.rb +7 -4
  105. data/lib/rubocop/cop/style/redundant_fetch_block.rb +3 -3
  106. data/lib/rubocop/cop/style/redundant_line_continuation.rb +10 -1
  107. data/lib/rubocop/cop/style/redundant_parentheses.rb +33 -10
  108. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  109. data/lib/rubocop/cop/style/redundant_self.rb +17 -2
  110. data/lib/rubocop/cop/style/redundant_sort.rb +9 -8
  111. data/lib/rubocop/cop/style/redundant_sort_by.rb +2 -2
  112. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -1
  113. data/lib/rubocop/cop/style/sample.rb +2 -1
  114. data/lib/rubocop/cop/style/select_by_regexp.rb +7 -6
  115. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  116. data/lib/rubocop/cop/style/semicolon.rb +8 -0
  117. data/lib/rubocop/cop/style/single_argument_dig.rb +5 -2
  118. data/lib/rubocop/cop/style/slicing_with_range.rb +76 -10
  119. data/lib/rubocop/cop/style/string_chars.rb +1 -0
  120. data/lib/rubocop/cop/style/strip.rb +7 -4
  121. data/lib/rubocop/cop/style/super_with_args_parentheses.rb +35 -0
  122. data/lib/rubocop/cop/style/symbol_proc.rb +36 -0
  123. data/lib/rubocop/cop/style/unpack_first.rb +11 -14
  124. data/lib/rubocop/cops_documentation_generator.rb +11 -1
  125. data/lib/rubocop/ext/regexp_node.rb +9 -4
  126. data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
  127. data/lib/rubocop/formatter/html_formatter.rb +1 -2
  128. data/lib/rubocop/formatter/json_formatter.rb +0 -1
  129. data/lib/rubocop/formatter.rb +1 -1
  130. data/lib/rubocop/lsp/routes.rb +1 -1
  131. data/lib/rubocop/options.rb +0 -8
  132. data/lib/rubocop/result_cache.rb +0 -1
  133. data/lib/rubocop/rspec/shared_contexts.rb +6 -0
  134. data/lib/rubocop/rspec/support.rb +1 -0
  135. data/lib/rubocop/runner.rb +1 -1
  136. data/lib/rubocop/server/cache.rb +1 -2
  137. data/lib/rubocop/server/client_command/exec.rb +0 -1
  138. data/lib/rubocop/server/server_command/exec.rb +0 -1
  139. data/lib/rubocop/version.rb +1 -1
  140. data/lib/rubocop.rb +4 -0
  141. metadata +15 -10
  142. /data/lib/rubocop/formatter/{git_hub_actions_formatter.rb → github_actions_formatter.rb} +0 -0
@@ -81,6 +81,7 @@ module RuboCop
81
81
  cond = node.condition
82
82
 
83
83
  control_op_condition(cond) do |first_child, rest_children|
84
+ return if require_parentheses?(node, first_child)
84
85
  return if semicolon_separated_expressions?(first_child, rest_children)
85
86
  return if modifier_op?(first_child)
86
87
  return if parens_allowed?(cond)
@@ -92,6 +93,13 @@ module RuboCop
92
93
  end
93
94
  end
94
95
 
96
+ def require_parentheses?(node, condition_body)
97
+ return false if !node.while_type? && !node.until_type?
98
+ return false if !condition_body.block_type? && !condition_body.numblock_type?
99
+
100
+ condition_body.send_node.block_literal? && condition_body.keywords?
101
+ end
102
+
95
103
  def semicolon_separated_expressions?(first_exp, rest_exps)
96
104
  return false unless (second_exp = rest_exps.first)
97
105
 
@@ -67,12 +67,13 @@ module RuboCop
67
67
  return unless redundant_argument?(node)
68
68
 
69
69
  offense_range = argument_range(node)
70
- message = format(MSG, arg: node.arguments.first.source)
70
+ message = format(MSG, arg: node.first_argument.source)
71
71
 
72
72
  add_offense(offense_range, message: message) do |corrector|
73
73
  corrector.remove(offense_range)
74
74
  end
75
75
  end
76
+ alias on_csend on_send
76
77
 
77
78
  private
78
79
 
@@ -80,7 +81,7 @@ module RuboCop
80
81
  redundant_argument = redundant_arg_for_method(node.method_name.to_s)
81
82
  return false if redundant_argument.nil?
82
83
 
83
- node.arguments.first == redundant_argument
84
+ node.first_argument == redundant_argument
84
85
  end
85
86
 
86
87
  def redundant_arg_for_method(method_name)
@@ -25,20 +25,20 @@ module RuboCop
25
25
  MSG = 'Remove the redundant double splat and braces, use keyword arguments directly.'
26
26
  MERGE_METHODS = %i[merge merge!].freeze
27
27
 
28
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
28
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
29
29
  def on_hash(node)
30
30
  return if node.pairs.empty? || node.pairs.any?(&:hash_rocket?)
31
31
  return unless (parent = node.parent)
32
32
  return unless parent.call_type? || parent.kwsplat_type?
33
33
  return unless mergeable?(parent)
34
34
  return unless (kwsplat = node.each_ancestor(:kwsplat).first)
35
- return if allowed_double_splat_receiver?(kwsplat)
35
+ return if !node.braces? || allowed_double_splat_receiver?(kwsplat)
36
36
 
37
37
  add_offense(kwsplat) do |corrector|
38
38
  autocorrect(corrector, node, kwsplat)
39
39
  end
40
40
  end
41
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
41
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
42
42
 
43
43
  private
44
44
 
@@ -56,6 +56,7 @@ module RuboCop
56
56
  end
57
57
  end
58
58
  end
59
+ alias on_csend on_send
59
60
 
60
61
  private
61
62
 
@@ -64,7 +65,7 @@ module RuboCop
64
65
  return if node.last_argument&.block_pass_type?
65
66
 
66
67
  if node.method?(:each) && !node.parent&.block_type?
67
- ancestor_node = node.each_ancestor(:send).detect do |ancestor|
68
+ ancestor_node = node.each_ancestor(:send, :csend).detect do |ancestor|
68
69
  ancestor.receiver == node &&
69
70
  (RESTRICT_ON_SEND.include?(ancestor.method_name) || ancestor.method?(:reverse_each))
70
71
  end
@@ -83,10 +84,12 @@ module RuboCop
83
84
  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
84
85
 
85
86
  def range(node)
86
- if node.method?(:each)
87
- node.loc.dot.join(node.loc.selector)
87
+ return node.selector unless node.method?(:each)
88
+
89
+ if node.parent.call_type?
90
+ node.selector.join(node.parent.loc.dot)
88
91
  else
89
- node.loc.selector
92
+ node.loc.dot.join(node.selector)
90
93
  end
91
94
  end
92
95
 
@@ -47,7 +47,7 @@ module RuboCop
47
47
  # @!method redundant_fetch_block_candidate?(node)
48
48
  def_node_matcher :redundant_fetch_block_candidate?, <<~PATTERN
49
49
  (block
50
- $(send _ :fetch _)
50
+ $(call _ :fetch _)
51
51
  (args)
52
52
  ${nil? #basic_literal? #const_type?})
53
53
  PATTERN
@@ -61,10 +61,10 @@ module RuboCop
61
61
  bad = build_bad_method(send, body)
62
62
 
63
63
  add_offense(range, message: format(MSG, good: good, bad: bad)) do |corrector|
64
- receiver, _, key = send.children
64
+ _, _, key = send.children
65
65
  default_value = body ? body.source : 'nil'
66
66
 
67
- corrector.replace(node, "#{receiver.source}.fetch(#{key.source}, #{default_value})")
67
+ corrector.replace(range, "fetch(#{key.source}, #{default_value})")
68
68
  end
69
69
  end
70
70
  end
@@ -94,7 +94,8 @@ module RuboCop
94
94
  !ends_with_backslash_without_comment?(range.source_line) ||
95
95
  string_concatenation?(range.source_line) ||
96
96
  start_with_arithmetic_operator?(processed_source[range.line]) ||
97
- inside_string_literal_or_method_with_argument?(range)
97
+ inside_string_literal_or_method_with_argument?(range) ||
98
+ leading_dot_method_chain_with_blank_line?(range)
98
99
  end
99
100
 
100
101
  def ends_with_backslash_without_comment?(source_line)
@@ -107,10 +108,18 @@ module RuboCop
107
108
 
108
109
  def inside_string_literal_or_method_with_argument?(range)
109
110
  processed_source.tokens.each_cons(2).any? do |token, next_token|
111
+ next if token.line == next_token.line
112
+
110
113
  inside_string_literal?(range, token) || method_with_argument?(token, next_token)
111
114
  end
112
115
  end
113
116
 
117
+ def leading_dot_method_chain_with_blank_line?(range)
118
+ return false unless range.source_line.strip.start_with?('.', '&.')
119
+
120
+ processed_source[range.line].strip.empty?
121
+ end
122
+
114
123
  def redundant_line_continuation?(range)
115
124
  return true unless (node = find_node_for_line(range.line))
116
125
  return false if argument_newline?(node)
@@ -17,12 +17,11 @@ module RuboCop
17
17
  include Parentheses
18
18
  extend AutoCorrector
19
19
 
20
+ ALLOWED_NODE_TYPES = %i[and or send splat kwsplat].freeze
21
+
20
22
  # @!method square_brackets?(node)
21
23
  def_node_matcher :square_brackets?, '(send {(send _recv _msg) str array hash} :[] ...)'
22
24
 
23
- # @!method range_end?(node)
24
- def_node_matcher :range_end?, '^^{irange erange}'
25
-
26
25
  # @!method method_node_and_args(node)
27
26
  def_node_matcher :method_node_and_args, '$(call _recv _msg $...)'
28
27
 
@@ -54,15 +53,16 @@ module RuboCop
54
53
  def ignore_syntax?(node)
55
54
  return false unless (parent = node.parent)
56
55
 
57
- parent.while_post_type? || parent.until_post_type? ||
58
- like_method_argument_parentheses?(parent)
56
+ parent.while_post_type? || parent.until_post_type? || parent.match_with_lvasgn_type? ||
57
+ like_method_argument_parentheses?(parent) || multiline_control_flow_statements?(node)
59
58
  end
60
59
 
61
60
  def allowed_expression?(node)
62
61
  allowed_ancestor?(node) ||
63
62
  allowed_method_call?(node) ||
64
63
  allowed_multiple_expression?(node) ||
65
- allowed_ternary?(node)
64
+ allowed_ternary?(node) ||
65
+ node.parent&.range_type?
66
66
  end
67
67
 
68
68
  def allowed_ancestor?(node)
@@ -104,6 +104,13 @@ module RuboCop
104
104
  !node.arithmetic_operation? && node.first_argument.begin_type?
105
105
  end
106
106
 
107
+ def multiline_control_flow_statements?(node)
108
+ return false unless (parent = node.parent)
109
+ return false if parent.single_line?
110
+
111
+ parent.return_type? || parent.next_type? || parent.break_type?
112
+ end
113
+
107
114
  def empty_parentheses?(node)
108
115
  # Don't flag `()`
109
116
  node.children.empty?
@@ -136,27 +143,44 @@ module RuboCop
136
143
  check_send(begin_node, node) if node.call_type?
137
144
  end
138
145
 
139
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
146
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
140
147
  def find_offense_message(begin_node, node)
141
148
  return 'a keyword' if keyword_with_redundant_parentheses?(node)
142
149
  return 'a literal' if disallowed_literal?(begin_node, node)
143
150
  return 'a variable' if node.variable?
144
151
  return 'a constant' if node.const_type?
152
+ if node.lambda_or_proc? && (node.braces? || node.send_node.lambda_literal?)
153
+ return 'an expression'
154
+ end
145
155
  return 'an interpolated expression' if interpolation?(begin_node)
146
156
 
147
- return if begin_node.chained? || !begin_node.parent.nil?
157
+ return if begin_node.chained?
148
158
 
149
159
  if node.and_type? || node.or_type?
160
+ return if node.semantic_operator? && begin_node.parent
161
+ return if node.multiline? && allow_in_multiline_conditions?
162
+ return if ALLOWED_NODE_TYPES.include?(begin_node.parent&.type)
163
+ return if begin_node.parent&.if_type? && begin_node.parent&.ternary?
164
+
150
165
  'a logical expression'
151
166
  elsif node.respond_to?(:comparison_method?) && node.comparison_method?
167
+ return unless begin_node.parent.nil?
168
+
152
169
  'a comparison expression'
153
170
  end
154
171
  end
155
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
172
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
156
173
 
157
174
  # @!method interpolation?(node)
158
175
  def_node_matcher :interpolation?, '[^begin ^^dstr]'
159
176
 
177
+ def allow_in_multiline_conditions?
178
+ parentheses_around_condition_config = config.for_cop('Style/ParenthesesAroundCondition')
179
+ return false unless parentheses_around_condition_config['Enabled']
180
+
181
+ !!parentheses_around_condition_config['AllowInMultilineConditions']
182
+ end
183
+
160
184
  def check_send(begin_node, node)
161
185
  return check_unary(begin_node, node) if node.unary_operation?
162
186
 
@@ -221,7 +245,6 @@ module RuboCop
221
245
  def method_call_with_redundant_parentheses?(node)
222
246
  return false unless node.call_type?
223
247
  return false if node.prefix_not?
224
- return false if range_end?(node)
225
248
 
226
249
  send_node, args = method_node_and_args(node)
227
250
 
@@ -58,7 +58,7 @@ module RuboCop
58
58
 
59
59
  MSG = 'Redundant `return` detected.'
60
60
  MULTI_RETURN_MSG = 'To return multiple values, use an array.'
61
- RESTRICT_ON_SEND = %i[define_method define_singleton_method].freeze
61
+ RESTRICT_ON_SEND = %i[define_method define_singleton_method lambda].freeze
62
62
 
63
63
  def on_send(node)
64
64
  return unless (parent = node.parent) && parent.block_type?
@@ -17,7 +17,8 @@ module RuboCop
17
17
  # protected scope, you cannot send private messages this way.
18
18
  #
19
19
  # Note we allow uses of `self` with operators because it would be awkward
20
- # otherwise.
20
+ # otherwise. Also allows the use of `self.it` without arguments in blocks,
21
+ # as in `0.times { self.it }`, following `Lint/ItWithoutArgumentsInBlock` cop.
21
22
  #
22
23
  # @example
23
24
  #
@@ -107,8 +108,8 @@ module RuboCop
107
108
  def on_send(node)
108
109
  return unless node.self_receiver? && regular_method_call?(node)
109
110
  return if node.parent&.mlhs_type?
110
-
111
111
  return if allowed_send_node?(node)
112
+ return if it_method_in_block?(node)
112
113
 
113
114
  add_offense(node.receiver) do |corrector|
114
115
  corrector.remove(node.receiver)
@@ -155,6 +156,20 @@ module RuboCop
155
156
  KERNEL_METHODS.include?(node.method_name)
156
157
  end
157
158
 
159
+ # Respects `Lint/ItWithoutArgumentsInBlock` cop and the following Ruby 3.3's warning:
160
+ #
161
+ # $ ruby -e '0.times { begin; it; end }'
162
+ # -e:1: warning: `it` calls without arguments will refer to the first block param in
163
+ # Ruby 3.4; use it() or self.it
164
+ #
165
+ def it_method_in_block?(node)
166
+ return false unless node.method?(:it)
167
+ return false unless (block_node = node.each_ancestor(:block).first)
168
+ return false unless block_node.arguments.empty_and_without_delimiters?
169
+
170
+ node.arguments.empty? && !node.block_literal?
171
+ end
172
+
158
173
  def regular_method_call?(node)
159
174
  !(node.operator_method? ||
160
175
  KEYWORDS.include?(node.method_name) ||
@@ -16,7 +16,7 @@ module RuboCop
16
16
  # This cop is unsafe, because `sort...last` and `max` may not return the
17
17
  # same element in all cases.
18
18
  #
19
- # In an enumerable where there are multiple elements where `a <=> b == 0`,
19
+ # In an enumerable where there are multiple elements where ``a <=> b == 0``,
20
20
  # or where the transformation done by the `sort_by` block has the
21
21
  # same result, `sort.last` and `max` (or `sort_by.last` and `max_by`)
22
22
  # will return different elements. `sort.last` will return the last
@@ -87,15 +87,15 @@ module RuboCop
87
87
  # @!method redundant_sort?(node)
88
88
  def_node_matcher :redundant_sort?, <<~MATCHER
89
89
  {
90
- (send $(send _ $:sort) ${:last :first})
91
- (send $(send _ $:sort) ${:[] :at :slice} {(int 0) (int -1)})
90
+ (call $(call _ $:sort) ${:last :first})
91
+ (call $(call _ $:sort) ${:[] :at :slice} {(int 0) (int -1)})
92
92
 
93
- (send $(send _ $:sort_by _) ${:last :first})
93
+ (call $(call _ $:sort_by _) ${:last :first})
94
94
  (send $(send _ $:sort_by _) ${:[] :at :slice} {(int 0) (int -1)})
95
95
 
96
- (send ({block numblock} $(send _ ${:sort_by :sort}) ...) ${:last :first})
97
- (send
98
- ({block numblock} $(send _ ${:sort_by :sort}) ...)
96
+ (call ({block numblock} $(call _ ${:sort_by :sort}) ...) ${:last :first})
97
+ (call
98
+ ({block numblock} $(call _ ${:sort_by :sort}) ...)
99
99
  ${:[] :at :slice} {(int 0) (int -1)}
100
100
  )
101
101
  }
@@ -108,6 +108,7 @@ module RuboCop
108
108
 
109
109
  register_offense(ancestor, sort_node, sorter, accessor)
110
110
  end
111
+ alias on_csend on_send
111
112
 
112
113
  private
113
114
 
@@ -180,7 +181,7 @@ module RuboCop
180
181
  end
181
182
 
182
183
  def arg_node(node)
183
- node.arguments.first
184
+ node.first_argument
184
185
  end
185
186
 
186
187
  def arg_value(node)
@@ -46,12 +46,12 @@ module RuboCop
46
46
 
47
47
  # @!method redundant_sort_by_block(node)
48
48
  def_node_matcher :redundant_sort_by_block, <<~PATTERN
49
- (block $(send _ :sort_by) (args (arg $_x)) (lvar _x))
49
+ (block $(call _ :sort_by) (args (arg $_x)) (lvar _x))
50
50
  PATTERN
51
51
 
52
52
  # @!method redundant_sort_by_numblock(node)
53
53
  def_node_matcher :redundant_sort_by_numblock, <<~PATTERN
54
- (numblock $(send _ :sort_by) 1 (lvar :_1))
54
+ (numblock $(call _ :sort_by) 1 (lvar :_1))
55
55
  PATTERN
56
56
 
57
57
  def sort_by_range(send, node)
@@ -133,7 +133,7 @@ module RuboCop
133
133
  end
134
134
 
135
135
  def percent_array_literal?(node)
136
- (percent_w_literal?(node) || percent_w_upper_literal?(node))
136
+ percent_w_literal?(node) || percent_w_upper_literal?(node)
137
137
  end
138
138
 
139
139
  def heredoc_with_disabled_interpolation?(node)
@@ -35,7 +35,7 @@ module RuboCop
35
35
 
36
36
  # @!method sample_candidate?(node)
37
37
  def_node_matcher :sample_candidate?, <<~PATTERN
38
- (send $(send _ :shuffle $...) ${:#{RESTRICT_ON_SEND.join(' :')}} $...)
38
+ (call $(call _ :shuffle $...) ${:#{RESTRICT_ON_SEND.join(' :')}} $...)
39
39
  PATTERN
40
40
 
41
41
  def on_send(node)
@@ -52,6 +52,7 @@ module RuboCop
52
52
  end
53
53
  end
54
54
  end
55
+ alias on_csend on_send
55
56
 
56
57
  private
57
58
 
@@ -55,8 +55,8 @@ module RuboCop
55
55
  # @!method regexp_match?(node)
56
56
  def_node_matcher :regexp_match?, <<~PATTERN
57
57
  {
58
- (block send (args (arg $_)) ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
59
- (numblock send $1 ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
58
+ (block call (args (arg $_)) ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
59
+ (numblock call $1 ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
60
60
  }
61
61
  PATTERN
62
62
 
@@ -64,9 +64,9 @@ module RuboCop
64
64
  # @!method creates_hash?(node)
65
65
  def_node_matcher :creates_hash?, <<~PATTERN
66
66
  {
67
- (send (const _ :Hash) {:new :[]} ...)
68
- (block (send (const _ :Hash) :new ...) ...)
69
- (send _ { :to_h :to_hash } ...)
67
+ (call (const _ :Hash) {:new :[]} ...)
68
+ (block (call (const _ :Hash) :new ...) ...)
69
+ (call _ { :to_h :to_hash } ...)
70
70
  }
71
71
  PATTERN
72
72
 
@@ -100,6 +100,7 @@ module RuboCop
100
100
  register_offense(node, block_node, regexp, replacement)
101
101
  end
102
102
  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
103
+ alias on_csend on_send
103
104
 
104
105
  private
105
106
 
@@ -146,7 +147,7 @@ module RuboCop
146
147
  return node.child_nodes.first if node.match_with_lvasgn_type?
147
148
 
148
149
  if node.receiver.lvar_type? &&
149
- (block.numblock_type? || node.receiver.source == block.arguments.first.source)
150
+ (block.numblock_type? || node.receiver.source == block.first_argument.source)
150
151
  node.first_argument
151
152
  elsif node.first_argument.lvar_type?
152
153
  node.receiver
@@ -16,7 +16,7 @@ module RuboCop
16
16
  extend AutoCorrector
17
17
 
18
18
  MSG = 'Use self-assignment shorthand `%<method>s=`.'
19
- OPS = %i[+ - * ** / | &].freeze
19
+ OPS = %i[+ - * ** / % ^ << >> | &].freeze
20
20
 
21
21
  def self.autocorrect_incompatible_with
22
22
  [Layout::SpaceAroundOperators]
@@ -80,6 +80,7 @@ module RuboCop
80
80
  processed_source.tokens.group_by(&:line)
81
81
  end
82
82
 
83
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
83
84
  def semicolon_position(tokens)
84
85
  if tokens.last.semicolon?
85
86
  -1
@@ -90,10 +91,13 @@ module RuboCop
90
91
  elsif exist_semicolon_after_left_curly_brace?(tokens) ||
91
92
  exist_semicolon_after_left_string_interpolation_brace?(tokens)
92
93
  2
94
+ elsif exist_semicolon_after_left_lambda_curly_brace?(tokens)
95
+ 3
93
96
  elsif exist_semicolon_before_right_string_interpolation_brace?(tokens)
94
97
  -4
95
98
  end
96
99
  end
100
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
97
101
 
98
102
  def exist_semicolon_before_right_curly_brace?(tokens)
99
103
  tokens[-2]&.right_curly_brace? && tokens[-3]&.semicolon?
@@ -103,6 +107,10 @@ module RuboCop
103
107
  tokens[1]&.left_curly_brace? && tokens[2]&.semicolon?
104
108
  end
105
109
 
110
+ def exist_semicolon_after_left_lambda_curly_brace?(tokens)
111
+ tokens[2]&.type == :tLAMBEG && tokens[3]&.semicolon?
112
+ end
113
+
106
114
  def exist_semicolon_before_right_string_interpolation_brace?(tokens)
107
115
  tokens[-3]&.type == :tSTRING_DEND && tokens[-4]&.semicolon?
108
116
  end
@@ -3,8 +3,11 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Sometimes using dig method ends up with just a single
7
- # argument. In such cases, dig should be replaced with [].
6
+ # Sometimes using `dig` method ends up with just a single
7
+ # argument. In such cases, dig should be replaced with `[]`.
8
+ #
9
+ # Since replacing `hash&.dig(:key)` with `hash[:key]` could potentially lead to error,
10
+ # calls to the `dig` method using safe navigation will be ignored.
8
11
  #
9
12
  # @safety
10
13
  # This cop is unsafe because it cannot be guaranteed that the receiver
@@ -3,8 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks that arrays are sliced with endless ranges instead of
7
- # `ary[start..-1]` on Ruby 2.6+.
6
+ # Checks that arrays are not sliced with the redundant `ary[0..-1]`, replacing it with `ary`,
7
+ # and ensures arrays are sliced with endless ranges instead of `ary[start..-1]` on Ruby 2.6+,
8
+ # and with beginless ranges instead of `ary[nil..end]` on Ruby 2.7+.
8
9
  #
9
10
  # @safety
10
11
  # This cop is unsafe because `x..-1` and `x..` are only guaranteed to
@@ -21,29 +22,94 @@ module RuboCop
21
22
  #
22
23
  # @example
23
24
  # # bad
24
- # items[1..-1]
25
+ # items[0..-1]
26
+ # items[0..nil]
27
+ # items[0...nil]
25
28
  #
26
29
  # # good
27
- # items[1..]
30
+ # items
31
+ #
32
+ # # bad
33
+ # items[1..-1] # Ruby 2.6+
34
+ # items[1..nil] # Ruby 2.6+
35
+ #
36
+ # # good
37
+ # items[1..] # Ruby 2.6+
38
+ #
39
+ # # bad
40
+ # items[nil..42] # Ruby 2.7+
41
+ #
42
+ # # good
43
+ # items[..42] # Ruby 2.7+
44
+ # items[0..42] # Ruby 2.7+
45
+ #
28
46
  class SlicingWithRange < Base
29
47
  extend AutoCorrector
30
48
  extend TargetRubyVersion
31
49
 
32
50
  minimum_target_ruby_version 2.6
33
51
 
34
- MSG = 'Prefer ary[n..] over ary[n..-1].'
52
+ MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
53
+ MSG_USELESS_RANGE = 'Remove the useless `%<prefer>s`.'
35
54
  RESTRICT_ON_SEND = %i[[]].freeze
36
55
 
56
+ # @!method range_from_zero_till_minus_one?(node)
57
+ def_node_matcher :range_from_zero_till_minus_one?, <<~PATTERN
58
+ {
59
+ (irange (int 0) {(int -1) nil})
60
+ (erange (int 0) nil)
61
+ }
62
+ PATTERN
63
+
37
64
  # @!method range_till_minus_one?(node)
38
- def_node_matcher :range_till_minus_one?, '(irange !nil? (int -1))'
65
+ def_node_matcher :range_till_minus_one?, <<~PATTERN
66
+ {
67
+ (irange !nil? {(int -1) nil})
68
+ (erange !nil? nil)
69
+ }
70
+ PATTERN
71
+
72
+ # @!method range_from_zero?(node)
73
+ def_node_matcher :range_from_zero?, <<~PATTERN
74
+ (irange nil !nil?)
75
+ PATTERN
39
76
 
40
77
  def on_send(node)
41
- return unless node.arguments.count == 1
42
- return unless range_till_minus_one?(node.arguments.first)
78
+ return unless node.arguments.one?
43
79
 
44
- add_offense(node.first_argument) do |corrector|
45
- corrector.remove(node.first_argument.end)
80
+ range_node = node.first_argument
81
+ selector = node.loc.selector
82
+ unless (message, removal_range = offense_message_with_removal_range(range_node, selector))
83
+ return
46
84
  end
85
+
86
+ add_offense(selector, message: message) do |corrector|
87
+ corrector.remove(removal_range)
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def offense_message_with_removal_range(range_node, selector)
94
+ if range_from_zero_till_minus_one?(range_node)
95
+ [format(MSG_USELESS_RANGE, prefer: selector.source), selector]
96
+ elsif range_till_minus_one?(range_node)
97
+ [
98
+ format(MSG, prefer: endless(range_node), current: selector.source), range_node.end
99
+ ]
100
+ elsif range_from_zero?(range_node) && target_ruby_version >= 2.7
101
+ [
102
+ format(MSG, prefer: beginless(range_node), current: selector.source), range_node.begin
103
+ ]
104
+ end
105
+ end
106
+
107
+ def endless(range_node)
108
+ "[#{range_node.begin.source}#{range_node.loc.operator.source}]"
109
+ end
110
+
111
+ def beginless(range_node)
112
+ "[#{range_node.loc.operator.source}#{range_node.end.source}]"
47
113
  end
48
114
  end
49
115
  end
@@ -35,6 +35,7 @@ module RuboCop
35
35
  corrector.replace(range, 'chars')
36
36
  end
37
37
  end
38
+ alias on_csend on_send
38
39
  end
39
40
  end
40
41
  end
@@ -22,20 +22,23 @@ module RuboCop
22
22
 
23
23
  # @!method lstrip_rstrip(node)
24
24
  def_node_matcher :lstrip_rstrip, <<~PATTERN
25
- {(send $(send _ $:rstrip) $:lstrip)
26
- (send $(send _ $:lstrip) $:rstrip)}
25
+ {
26
+ (call $(call _ :rstrip) :lstrip)
27
+ (call $(call _ :lstrip) :rstrip)
28
+ }
27
29
  PATTERN
28
30
 
29
31
  def on_send(node)
30
- lstrip_rstrip(node) do |first_send, method_one, method_two|
32
+ lstrip_rstrip(node) do |first_send|
31
33
  range = range_between(first_send.loc.selector.begin_pos, node.source_range.end_pos)
32
- message = format(MSG, methods: "#{method_one}.#{method_two}")
34
+ message = format(MSG, methods: range.source)
33
35
 
34
36
  add_offense(range, message: message) do |corrector|
35
37
  corrector.replace(range, 'strip')
36
38
  end
37
39
  end
38
40
  end
41
+ alias on_csend on_send
39
42
  end
40
43
  end
41
44
  end