rubocop 1.57.2 → 1.62.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (188) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +4 -4
  4. data/assets/output.css.erb +159 -0
  5. data/assets/output.html.erb +1 -160
  6. data/config/default.yml +87 -15
  7. data/lib/rubocop/cli/command/auto_generate_config.rb +12 -3
  8. data/lib/rubocop/cli/command/lsp.rb +2 -2
  9. data/lib/rubocop/cli.rb +6 -1
  10. data/lib/rubocop/config.rb +4 -2
  11. data/lib/rubocop/config_finder.rb +12 -2
  12. data/lib/rubocop/config_loader.rb +0 -1
  13. data/lib/rubocop/config_obsoletion.rb +11 -8
  14. data/lib/rubocop/config_validator.rb +14 -7
  15. data/lib/rubocop/cop/autocorrect_logic.rb +6 -1
  16. data/lib/rubocop/cop/base.rb +17 -2
  17. data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
  18. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +4 -8
  19. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +5 -13
  20. data/lib/rubocop/cop/exclude_limit.rb +1 -1
  21. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +2 -2
  22. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +5 -1
  23. data/lib/rubocop/cop/internal_affairs/example_description.rb +4 -4
  24. data/lib/rubocop/cop/internal_affairs/method_name_end_with.rb +8 -6
  25. data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +19 -20
  26. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +53 -0
  27. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +123 -29
  28. data/lib/rubocop/cop/internal_affairs/redundant_expect_offense_arguments.rb +34 -0
  29. data/lib/rubocop/cop/internal_affairs.rb +2 -0
  30. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  31. data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +14 -7
  32. data/lib/rubocop/cop/layout/end_alignment.rb +8 -2
  33. data/lib/rubocop/cop/layout/extra_spacing.rb +4 -10
  34. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +22 -7
  35. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  36. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  37. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -1
  38. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +1 -1
  39. data/lib/rubocop/cop/layout/redundant_line_break.rb +16 -3
  40. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +4 -4
  41. data/lib/rubocop/cop/layout/single_line_block_chain.rb +5 -0
  42. data/lib/rubocop/cop/layout/space_around_operators.rb +50 -20
  43. data/lib/rubocop/cop/layout/space_before_block_braces.rb +19 -10
  44. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +1 -1
  45. data/lib/rubocop/cop/lint/assignment_in_condition.rb +4 -4
  46. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +2 -2
  47. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  48. data/lib/rubocop/cop/lint/debugger.rb +2 -1
  49. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
  50. data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
  51. data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -3
  52. data/lib/rubocop/cop/lint/float_comparison.rb +10 -0
  53. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +2 -1
  54. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +56 -0
  55. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +85 -0
  56. data/lib/rubocop/cop/lint/next_without_accumulator.rb +6 -21
  57. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -5
  58. data/lib/rubocop/cop/lint/number_conversion.rb +9 -4
  59. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +54 -6
  60. data/lib/rubocop/cop/lint/redundant_with_index.rb +3 -2
  61. data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
  62. data/lib/rubocop/cop/lint/rescue_type.rb +1 -3
  63. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -4
  64. data/lib/rubocop/cop/lint/script_permission.rb +3 -3
  65. data/lib/rubocop/cop/lint/self_assignment.rb +38 -0
  66. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -0
  67. data/lib/rubocop/cop/lint/symbol_conversion.rb +7 -2
  68. data/lib/rubocop/cop/lint/syntax.rb +6 -3
  69. data/lib/rubocop/cop/lint/to_enum_arguments.rb +7 -2
  70. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
  71. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +2 -2
  72. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  73. data/lib/rubocop/cop/lint/useless_times.rb +2 -2
  74. data/lib/rubocop/cop/lint/void.rb +20 -2
  75. data/lib/rubocop/cop/metrics/abc_size.rb +3 -3
  76. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  77. data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -0
  78. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  79. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  80. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  81. data/lib/rubocop/cop/naming/block_forwarding.rb +12 -4
  82. data/lib/rubocop/cop/naming/constant_name.rb +1 -2
  83. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  84. data/lib/rubocop/cop/naming/predicate_name.rb +2 -2
  85. data/lib/rubocop/cop/registry.rb +1 -1
  86. data/lib/rubocop/cop/security/open.rb +2 -2
  87. data/lib/rubocop/cop/style/access_modifier_declarations.rb +2 -2
  88. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  89. data/lib/rubocop/cop/style/arguments_forwarding.rb +152 -21
  90. data/lib/rubocop/cop/style/array_first_last.rb +64 -0
  91. data/lib/rubocop/cop/style/auto_resource_cleanup.rb +21 -14
  92. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +2 -2
  93. data/lib/rubocop/cop/style/case_like_if.rb +5 -5
  94. data/lib/rubocop/cop/style/class_check.rb +1 -0
  95. data/lib/rubocop/cop/style/class_vars.rb +3 -3
  96. data/lib/rubocop/cop/style/collection_compact.rb +18 -8
  97. data/lib/rubocop/cop/style/combinable_loops.rb +13 -7
  98. data/lib/rubocop/cop/style/commented_keyword.rb +5 -2
  99. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -0
  100. data/lib/rubocop/cop/style/conditional_assignment.rb +6 -7
  101. data/lib/rubocop/cop/style/date_time.rb +5 -4
  102. data/lib/rubocop/cop/style/each_for_simple_loop.rb +7 -7
  103. data/lib/rubocop/cop/style/each_with_object.rb +2 -2
  104. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  105. data/lib/rubocop/cop/style/eval_with_location.rb +3 -14
  106. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
  107. data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
  108. data/lib/rubocop/cop/style/for.rb +2 -0
  109. data/lib/rubocop/cop/style/hash_each_methods.rb +105 -11
  110. data/lib/rubocop/cop/style/hash_except.rb +2 -1
  111. data/lib/rubocop/cop/style/hash_syntax.rb +6 -2
  112. data/lib/rubocop/cop/style/identical_conditional_branches.rb +4 -1
  113. data/lib/rubocop/cop/style/inverse_methods.rb +14 -13
  114. data/lib/rubocop/cop/style/invertible_unless_condition.rb +44 -2
  115. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +8 -10
  116. data/lib/rubocop/cop/style/map_to_hash.rb +17 -7
  117. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +14 -5
  118. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -4
  119. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +20 -0
  120. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  121. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +2 -2
  122. data/lib/rubocop/cop/style/multiline_method_signature.rb +10 -1
  123. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +5 -3
  124. data/lib/rubocop/cop/style/next.rb +1 -1
  125. data/lib/rubocop/cop/style/nil_comparison.rb +2 -0
  126. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +1 -1
  127. data/lib/rubocop/cop/style/object_then.rb +5 -3
  128. data/lib/rubocop/cop/style/operator_method_call.rb +2 -2
  129. data/lib/rubocop/cop/style/parallel_assignment.rb +3 -5
  130. data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -0
  131. data/lib/rubocop/cop/style/raise_args.rb +4 -1
  132. data/lib/rubocop/cop/style/redundant_argument.rb +4 -3
  133. data/lib/rubocop/cop/style/redundant_assignment.rb +10 -2
  134. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +4 -3
  135. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +3 -3
  136. data/lib/rubocop/cop/style/redundant_each.rb +7 -4
  137. data/lib/rubocop/cop/style/redundant_fetch_block.rb +3 -3
  138. data/lib/rubocop/cop/style/redundant_line_continuation.rb +27 -7
  139. data/lib/rubocop/cop/style/redundant_parentheses.rb +33 -10
  140. data/lib/rubocop/cop/style/redundant_return.rb +7 -1
  141. data/lib/rubocop/cop/style/redundant_self.rb +17 -2
  142. data/lib/rubocop/cop/style/redundant_sort.rb +9 -8
  143. data/lib/rubocop/cop/style/redundant_sort_by.rb +2 -2
  144. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -1
  145. data/lib/rubocop/cop/style/sample.rb +3 -4
  146. data/lib/rubocop/cop/style/select_by_regexp.rb +7 -6
  147. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  148. data/lib/rubocop/cop/style/semicolon.rb +8 -0
  149. data/lib/rubocop/cop/style/single_argument_dig.rb +5 -2
  150. data/lib/rubocop/cop/style/slicing_with_range.rb +76 -10
  151. data/lib/rubocop/cop/style/string_chars.rb +1 -0
  152. data/lib/rubocop/cop/style/strip.rb +7 -4
  153. data/lib/rubocop/cop/style/super_with_args_parentheses.rb +35 -0
  154. data/lib/rubocop/cop/style/symbol_proc.rb +36 -0
  155. data/lib/rubocop/cop/style/unpack_first.rb +11 -14
  156. data/lib/rubocop/cop/utils/regexp_ranges.rb +1 -1
  157. data/lib/rubocop/cops_documentation_generator.rb +15 -3
  158. data/lib/rubocop/directive_comment.rb +10 -8
  159. data/lib/rubocop/ext/regexp_node.rb +9 -4
  160. data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
  161. data/lib/rubocop/formatter/html_formatter.rb +31 -12
  162. data/lib/rubocop/formatter/json_formatter.rb +0 -1
  163. data/lib/rubocop/formatter/offense_count_formatter.rb +12 -2
  164. data/lib/rubocop/formatter.rb +1 -1
  165. data/lib/rubocop/lsp/logger.rb +1 -1
  166. data/lib/rubocop/lsp/routes.rb +2 -2
  167. data/lib/rubocop/lsp/runtime.rb +1 -1
  168. data/lib/rubocop/lsp/server.rb +5 -2
  169. data/lib/rubocop/lsp/severity.rb +1 -1
  170. data/lib/rubocop/lsp.rb +29 -0
  171. data/lib/rubocop/magic_comment.rb +1 -1
  172. data/lib/rubocop/options.rb +11 -8
  173. data/lib/rubocop/path_util.rb +6 -2
  174. data/lib/rubocop/result_cache.rb +0 -1
  175. data/lib/rubocop/rspec/cop_helper.rb +8 -2
  176. data/lib/rubocop/rspec/expect_offense.rb +8 -8
  177. data/lib/rubocop/rspec/shared_contexts.rb +40 -15
  178. data/lib/rubocop/rspec/support.rb +2 -0
  179. data/lib/rubocop/runner.rb +10 -3
  180. data/lib/rubocop/server/cache.rb +1 -2
  181. data/lib/rubocop/server/client_command/exec.rb +0 -1
  182. data/lib/rubocop/server/server_command/exec.rb +0 -1
  183. data/lib/rubocop/target_finder.rb +84 -78
  184. data/lib/rubocop/target_ruby.rb +82 -80
  185. data/lib/rubocop/version.rb +18 -3
  186. data/lib/rubocop.rb +4 -0
  187. metadata +18 -10
  188. /data/lib/rubocop/formatter/{git_hub_actions_formatter.rb → github_actions_formatter.rb} +0 -0
@@ -8,11 +8,26 @@ module RuboCop
8
8
  # This cop identifies places where `do_something(*args, &block)`
9
9
  # can be replaced by `do_something(...)`.
10
10
  #
11
+ # In Ruby 3.1, anonymous block forwarding has been added.
12
+ #
13
+ # This cop identifies places where `do_something(&block)` can be replaced
14
+ # by `do_something(&)`; if desired, this functionality can be disabled
15
+ # by setting `UseAnonymousForwarding: false`.
16
+ #
11
17
  # In Ruby 3.2, anonymous args/kwargs forwarding has been added.
12
18
  #
13
19
  # This cop also identifies places where `use_args(*args)`/`use_kwargs(**kwargs)` can be
14
20
  # replaced by `use_args(*)`/`use_kwargs(**)`; if desired, this functionality can be disabled
15
- # by setting UseAnonymousForwarding: false.
21
+ # by setting `UseAnonymousForwarding: false`.
22
+ #
23
+ # And this cop has `RedundantRestArgumentNames`, `RedundantKeywordRestArgumentNames`,
24
+ # and `RedundantBlockArgumentNames` options. This configuration is a list of redundant names
25
+ # that are sufficient for anonymizing meaningless naming.
26
+ #
27
+ # Meaningless names that are commonly used can be anonymized by default:
28
+ # e.g., `*args`, `**options`, `&block`, and so on.
29
+ #
30
+ # Names not on this list are likely to be meaningful and are allowed by default.
16
31
  #
17
32
  # @example
18
33
  # # bad
@@ -32,22 +47,25 @@ module RuboCop
32
47
  #
33
48
  # @example UseAnonymousForwarding: true (default, only relevant for Ruby >= 3.2)
34
49
  # # bad
35
- # def foo(*args, **kwargs)
50
+ # def foo(*args, **kwargs, &block)
36
51
  # args_only(*args)
37
52
  # kwargs_only(**kwargs)
53
+ # block_only(&block)
38
54
  # end
39
55
  #
40
56
  # # good
41
- # def foo(*, **)
57
+ # def foo(*, **, &)
42
58
  # args_only(*)
43
59
  # kwargs_only(**)
60
+ # block_only(&)
44
61
  # end
45
62
  #
46
63
  # @example UseAnonymousForwarding: false (only relevant for Ruby >= 3.2)
47
64
  # # good
48
- # def foo(*args, **kwargs)
65
+ # def foo(*args, **kwargs, &block)
49
66
  # args_only(*args)
50
67
  # kwargs_only(**kwargs)
68
+ # block_only(&block)
51
69
  # end
52
70
  #
53
71
  # @example AllowOnlyRestArgument: true (default, only relevant for Ruby < 3.2)
@@ -72,6 +90,38 @@ module RuboCop
72
90
  # bar(**kwargs)
73
91
  # end
74
92
  #
93
+ # @example RedundantRestArgumentNames: ['args', 'arguments'] (default)
94
+ # # bad
95
+ # def foo(*args)
96
+ # bar(*args)
97
+ # end
98
+ #
99
+ # # good
100
+ # def foo(*)
101
+ # bar(*)
102
+ # end
103
+ #
104
+ # @example RedundantKeywordRestArgumentNames: ['kwargs', 'options', 'opts'] (default)
105
+ # # bad
106
+ # def foo(**kwargs)
107
+ # bar(**kwargs)
108
+ # end
109
+ #
110
+ # # good
111
+ # def foo(**)
112
+ # bar(**)
113
+ # end
114
+ #
115
+ # @example RedundantBlockArgumentNames: ['blk', 'block', 'proc'] (default)
116
+ # # bad - But it is good with `EnforcedStyle: explicit` set for `Naming/BlockForwarding`.
117
+ # def foo(&block)
118
+ # bar(&block)
119
+ # end
120
+ #
121
+ # # good
122
+ # def foo(&)
123
+ # bar(&)
124
+ # end
75
125
  class ArgumentsForwarding < Base
76
126
  include RangeHelp
77
127
  extend AutoCorrector
@@ -85,17 +135,21 @@ module RuboCop
85
135
  FORWARDING_MSG = 'Use shorthand syntax `...` for arguments forwarding.'
86
136
  ARGS_MSG = 'Use anonymous positional arguments forwarding (`*`).'
87
137
  KWARGS_MSG = 'Use anonymous keyword arguments forwarding (`**`).'
138
+ BLOCK_MSG = 'Use anonymous block arguments forwarding (`&`).'
139
+
140
+ def self.autocorrect_incompatible_with
141
+ [Naming::BlockForwarding]
142
+ end
88
143
 
89
144
  def on_def(node)
90
145
  return unless node.body
91
146
 
92
- forwardable_args = extract_forwardable_args(node.arguments)
147
+ restarg, kwrestarg, blockarg = extract_forwardable_args(node.arguments)
148
+ forwardable_args = redundant_forwardable_named_args(restarg, kwrestarg, blockarg)
149
+ send_nodes = node.each_descendant(:send).to_a
93
150
 
94
151
  send_classifications = classify_send_nodes(
95
- node,
96
- node.each_descendant(:send).to_a,
97
- non_splat_or_block_pass_lvar_references(node.body),
98
- forwardable_args
152
+ node, send_nodes, non_splat_or_block_pass_lvar_references(node.body), forwardable_args
99
153
  )
100
154
 
101
155
  return if send_classifications.empty?
@@ -115,36 +169,71 @@ module RuboCop
115
169
  [args.find(&:restarg_type?), args.find(&:kwrestarg_type?), args.find(&:blockarg_type?)]
116
170
  end
117
171
 
172
+ def redundant_forwardable_named_args(restarg, kwrestarg, blockarg)
173
+ restarg_node = redundant_named_arg(restarg, 'RedundantRestArgumentNames', '*')
174
+ kwrestarg_node = redundant_named_arg(kwrestarg, 'RedundantKeywordRestArgumentNames', '**')
175
+ blockarg_node = redundant_named_arg(blockarg, 'RedundantBlockArgumentNames', '&')
176
+
177
+ [restarg_node, kwrestarg_node, blockarg_node]
178
+ end
179
+
118
180
  def only_forwards_all?(send_classifications)
119
181
  send_classifications.all? { |_, c, _, _| c == :all }
120
182
  end
121
183
 
184
+ # rubocop:disable Metrics/MethodLength
122
185
  def add_forward_all_offenses(node, send_classifications, forwardable_args)
123
- send_classifications.each do |send_node, _c, forward_rest, _forward_kwrest|
124
- register_forward_all_offense(send_node, send_node, forward_rest)
186
+ _rest_arg, _kwrest_arg, block_arg = *forwardable_args
187
+ registered_block_arg_offense = false
188
+
189
+ send_classifications.each do |send_node, _c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength
190
+ if !forward_rest && !forward_kwrest
191
+ # Prevents `anonymous block parameter is also used within block (SyntaxError)` occurs
192
+ # in Ruby 3.3.0.
193
+ if outside_block?(forward_block_arg)
194
+ register_forward_block_arg_offense(!forward_rest, node.arguments, block_arg)
195
+ register_forward_block_arg_offense(!forward_rest, send_node, forward_block_arg)
196
+ end
197
+ registered_block_arg_offense = true
198
+ break
199
+ else
200
+ register_forward_all_offense(send_node, send_node, forward_rest)
201
+ end
125
202
  end
126
203
 
204
+ return if registered_block_arg_offense
205
+
127
206
  rest_arg, _kwrest_arg, _block_arg = *forwardable_args
128
207
  register_forward_all_offense(node, node.arguments, rest_arg)
129
208
  end
209
+ # rubocop:enable Metrics/MethodLength
130
210
 
211
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
131
212
  def add_post_ruby_32_offenses(def_node, send_classifications, forwardable_args)
132
213
  return unless use_anonymous_forwarding?
133
214
 
134
- rest_arg, kwrest_arg, _block_arg = *forwardable_args
215
+ rest_arg, kwrest_arg, block_arg = *forwardable_args
135
216
 
136
- send_classifications.each do |send_node, _c, forward_rest, forward_kwrest|
137
- if forward_rest
217
+ send_classifications.each do |send_node, _c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength
218
+ if outside_block?(forward_rest)
138
219
  register_forward_args_offense(def_node.arguments, rest_arg)
139
220
  register_forward_args_offense(send_node, forward_rest)
140
221
  end
141
222
 
142
- if forward_kwrest
223
+ if outside_block?(forward_kwrest)
143
224
  register_forward_kwargs_offense(!forward_rest, def_node.arguments, kwrest_arg)
144
225
  register_forward_kwargs_offense(!forward_rest, send_node, forward_kwrest)
145
226
  end
227
+
228
+ # Prevents `anonymous block parameter is also used within block (SyntaxError)` occurs
229
+ # in Ruby 3.3.0.
230
+ if outside_block?(forward_block_arg)
231
+ register_forward_block_arg_offense(!forward_rest, def_node.arguments, block_arg)
232
+ register_forward_block_arg_offense(!forward_rest, send_node, forward_block_arg)
233
+ end
146
234
  end
147
235
  end
236
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
148
237
 
149
238
  def non_splat_or_block_pass_lvar_references(body)
150
239
  body.each_descendant(:lvar, :lvasgn).filter_map do |lvar|
@@ -173,10 +262,7 @@ module RuboCop
173
262
 
174
263
  def classification_and_forwards(def_node, send_node, referenced_lvars, forwardable_args)
175
264
  classifier = SendNodeClassifier.new(
176
- def_node,
177
- send_node,
178
- referenced_lvars,
179
- forwardable_args,
265
+ def_node, send_node, referenced_lvars, forwardable_args,
180
266
  target_ruby_version: target_ruby_version,
181
267
  allow_only_rest_arguments: allow_only_rest_arguments?
182
268
  )
@@ -185,7 +271,28 @@ module RuboCop
185
271
 
186
272
  return unless classification
187
273
 
188
- [classification, classifier.forwarded_rest_arg, classifier.forwarded_kwrest_arg]
274
+ [
275
+ classification,
276
+ classifier.forwarded_rest_arg,
277
+ classifier.forwarded_kwrest_arg,
278
+ classifier.forwarded_block_arg
279
+ ]
280
+ end
281
+
282
+ def redundant_named_arg(arg, config_name, keyword)
283
+ return nil unless arg
284
+
285
+ redundant_arg_names = cop_config.fetch(config_name, []).map do |redundant_arg_name|
286
+ "#{keyword}#{redundant_arg_name}"
287
+ end << keyword
288
+
289
+ redundant_arg_names.include?(arg.source) ? arg : nil
290
+ end
291
+
292
+ def outside_block?(node)
293
+ return false unless node
294
+
295
+ node.each_ancestor(:block, :numblock).none?
189
296
  end
190
297
 
191
298
  def register_forward_args_offense(def_arguments_or_send, rest_arg_or_splat)
@@ -204,6 +311,16 @@ module RuboCop
204
311
  end
205
312
  end
206
313
 
314
+ def register_forward_block_arg_offense(add_parens, def_arguments_or_send, block_arg)
315
+ return if target_ruby_version <= 3.0 || block_arg.source == '&' || explicit_block_name?
316
+
317
+ add_offense(block_arg, message: BLOCK_MSG) do |corrector|
318
+ add_parens_if_missing(def_arguments_or_send, corrector) if add_parens
319
+
320
+ corrector.replace(block_arg, '&')
321
+ end
322
+ end
323
+
207
324
  def register_forward_all_offense(def_or_send, send_or_arguments, rest_or_splat)
208
325
  arg_range = arguments_range(def_or_send, rest_or_splat)
209
326
 
@@ -278,7 +395,7 @@ module RuboCop
278
395
  end
279
396
 
280
397
  def classification
281
- return nil unless forwarded_rest_arg || forwarded_kwrest_arg
398
+ return nil unless forwarded_rest_arg || forwarded_kwrest_arg || forwarded_block_arg
282
399
 
283
400
  if can_forward_all?
284
401
  :all
@@ -362,9 +479,23 @@ module RuboCop
362
479
  def no_additional_args?
363
480
  forwardable_count = [@rest_arg, @kwrest_arg, @block_arg].compact.size
364
481
 
482
+ return false if missing_rest_arg_or_kwrest_arg?
483
+
365
484
  @def_node.arguments.size == forwardable_count &&
366
485
  @send_node.arguments.size == forwardable_count
367
486
  end
487
+
488
+ def missing_rest_arg_or_kwrest_arg?
489
+ (@rest_arg_name && !forwarded_rest_arg) ||
490
+ (@kwrest_arg_name && !forwarded_kwrest_arg)
491
+ end
492
+ end
493
+
494
+ def explicit_block_name?
495
+ block_forwarding_config = config.for_cop('Naming/BlockForwarding')
496
+ return false unless block_forwarding_config['Enabled']
497
+
498
+ block_forwarding_config['EnforcedStyle'] == 'explicit'
368
499
  end
369
500
  end
370
501
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Identifies usages of `arr[0]` and `arr[-1]` and suggests to change
7
+ # them to use `arr.first` and `arr.last` instead.
8
+ #
9
+ # The cop is disabled by default due to safety concerns.
10
+ #
11
+ # @safety
12
+ # This cop is unsafe because `[0]` or `[-1]` can be called on a Hash,
13
+ # which returns a value for `0` or `-1` key, but changing these to use
14
+ # `.first` or `.last` will return first/last tuple instead. Also, String
15
+ # does not implement `first`/`last` methods.
16
+ #
17
+ # @example
18
+ # # bad
19
+ # arr[0]
20
+ # arr[-1]
21
+ #
22
+ # # good
23
+ # arr.first
24
+ # arr.last
25
+ # arr[0] = 2
26
+ # arr[0][-2]
27
+ #
28
+ class ArrayFirstLast < Base
29
+ extend AutoCorrector
30
+
31
+ MSG = 'Use `%<preferred>s`.'
32
+ RESTRICT_ON_SEND = %i[[]].freeze
33
+
34
+ # rubocop:disable Metrics/AbcSize
35
+ def on_send(node)
36
+ return unless node.arguments.size == 1 && node.first_argument.int_type?
37
+
38
+ value = node.first_argument.value
39
+ return unless [0, -1].include?(value)
40
+
41
+ node = innermost_braces_node(node)
42
+ return if node.parent && brace_method?(node.parent)
43
+
44
+ preferred = (value.zero? ? 'first' : 'last')
45
+ add_offense(node.loc.selector, message: format(MSG, preferred: preferred)) do |corrector|
46
+ corrector.replace(node.loc.selector, ".#{preferred}")
47
+ end
48
+ end
49
+ # rubocop:enable Metrics/AbcSize
50
+
51
+ private
52
+
53
+ def innermost_braces_node(node)
54
+ node = node.receiver while node.receiver.send_type? && node.receiver.method?(:[])
55
+ node
56
+ end
57
+
58
+ def brace_method?(node)
59
+ node.send_type? && (node.method?(:[]) || node.method?(:[]=))
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -16,31 +16,38 @@ module RuboCop
16
16
  # File.open('file') do |f|
17
17
  # # ...
18
18
  # end
19
+ #
20
+ # # bad
21
+ # f = Tempfile.open('temp')
22
+ #
23
+ # # good
24
+ # Tempfile.open('temp') do |f|
25
+ # # ...
26
+ # end
19
27
  class AutoResourceCleanup < Base
20
- MSG = 'Use the block version of `%<class>s.%<method>s`.'
21
-
22
- TARGET_METHODS = { File: :open }.freeze
28
+ MSG = 'Use the block version of `%<current>s`.'
29
+ RESTRICT_ON_SEND = %i[open].freeze
23
30
 
24
- RESTRICT_ON_SEND = TARGET_METHODS.values.freeze
31
+ # @!method file_open_method?(node)
32
+ def_node_matcher :file_open_method?, <<~PATTERN
33
+ (send (const {nil? cbase} {:File :Tempfile}) :open ...)
34
+ PATTERN
25
35
 
26
36
  def on_send(node)
27
- TARGET_METHODS.each do |target_class, target_method|
28
- next if node.method_name != target_method
37
+ return if !file_open_method?(node) || cleanup?(node)
29
38
 
30
- target_receiver = s(:const, nil, target_class)
31
- next if node.receiver != target_receiver
39
+ current = node.receiver.source_range.begin.join(node.selector.end).source
32
40
 
33
- next if cleanup?(node)
34
-
35
- add_offense(node, message: format(MSG, class: target_class, method: target_method))
36
- end
41
+ add_offense(node, message: format(MSG, current: current))
37
42
  end
38
43
 
39
44
  private
40
45
 
41
46
  def cleanup?(node)
42
- parent = node.parent
43
- node.block_argument? || (parent && (parent.block_type? || !parent.lvasgn_type?))
47
+ return true if node.block_argument?
48
+ return false unless (parent = node.parent)
49
+
50
+ parent.block_type? || !parent.lvasgn_type?
44
51
  end
45
52
  end
46
53
  end
@@ -33,7 +33,7 @@ module RuboCop
33
33
  def on_class(class_node)
34
34
  @macros_to_rewrite[class_node] = Set.new
35
35
 
36
- find_macros(class_node.body).each do |_visibility, macros|
36
+ find_macros(class_node.body).each_value do |macros|
37
37
  bisected = find_bisection(macros)
38
38
  next unless bisected.any?
39
39
 
@@ -74,7 +74,7 @@ module RuboCop
74
74
  def find_macros(class_def)
75
75
  # Find all the macros (`attr_reader`, `attr_writer`, etc.) in the class body
76
76
  # and turn them into `Macro` objects so that they can be processed.
77
- return [] if !class_def || class_def.def_type?
77
+ return {} if !class_def || class_def.def_type?
78
78
 
79
79
  send_nodes =
80
80
  if class_def.send_type?
@@ -125,7 +125,7 @@ module RuboCop
125
125
  when :==, :eql?, :equal?
126
126
  find_target_in_equality_node(node)
127
127
  when :===
128
- node.arguments.first
128
+ node.first_argument
129
129
  when :include?, :cover?
130
130
  find_target_in_include_or_cover_node(node)
131
131
  when :match, :match?, :=~
@@ -134,7 +134,7 @@ module RuboCop
134
134
  end
135
135
 
136
136
  def find_target_in_equality_node(node)
137
- argument = node.arguments.first
137
+ argument = node.first_argument
138
138
  receiver = node.receiver
139
139
  return unless argument && receiver
140
140
 
@@ -152,7 +152,7 @@ module RuboCop
152
152
  end
153
153
 
154
154
  def find_target_in_match_node(node)
155
- argument = node.arguments.first
155
+ argument = node.first_argument
156
156
  receiver = node.receiver
157
157
  return unless receiver
158
158
 
@@ -185,7 +185,7 @@ module RuboCop
185
185
  def condition_from_send_node(node, target)
186
186
  case node.method_name
187
187
  when :is_a?
188
- node.arguments.first if node.receiver == target
188
+ node.first_argument if node.receiver == target
189
189
  when :==, :eql?, :equal?
190
190
  condition_from_equality_node(node, target)
191
191
  when :=~, :match, :match?
@@ -230,7 +230,7 @@ module RuboCop
230
230
 
231
231
  def branch_conditions(node)
232
232
  conditions = []
233
- while node&.if_type?
233
+ while node&.if_type? && !node.ternary?
234
234
  conditions << node.condition
235
235
  node = node.else_branch
236
236
  end
@@ -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?)
@@ -54,9 +54,9 @@ module RuboCop
54
54
  end
55
55
 
56
56
  def on_send(node)
57
- add_offense(
58
- node.first_argument, message: format(MSG, class_var: node.first_argument.source)
59
- )
57
+ return unless (first_argument = node.first_argument)
58
+
59
+ add_offense(first_argument, message: format(MSG, class_var: first_argument.source))
60
60
  end
61
61
  end
62
62
  end
@@ -23,6 +23,8 @@ module RuboCop
23
23
  # array.reject { |e| e.nil? }
24
24
  # array.delete_if { |e| e.nil? }
25
25
  # array.select { |e| !e.nil? }
26
+ # array.grep_v(nil)
27
+ # array.grep_v(NilClass)
26
28
  #
27
29
  # # good
28
30
  # array.compact
@@ -46,14 +48,14 @@ module RuboCop
46
48
  extend TargetRubyVersion
47
49
 
48
50
  MSG = 'Use `%<good>s` instead of `%<bad>s`.'
49
- RESTRICT_ON_SEND = %i[reject delete_if reject! select select!].freeze
51
+ RESTRICT_ON_SEND = %i[reject delete_if reject! select select! grep_v].freeze
50
52
  TO_ENUM_METHODS = %i[to_enum lazy].freeze
51
53
 
52
54
  minimum_target_ruby_version 2.4
53
55
 
54
56
  # @!method reject_method_with_block_pass?(node)
55
57
  def_node_matcher :reject_method_with_block_pass?, <<~PATTERN
56
- (send !nil? {:reject :delete_if :reject!}
58
+ (call !nil? {:reject :delete_if :reject!}
57
59
  (block_pass
58
60
  (sym :nil?)))
59
61
  PATTERN
@@ -61,24 +63,29 @@ module RuboCop
61
63
  # @!method reject_method?(node)
62
64
  def_node_matcher :reject_method?, <<~PATTERN
63
65
  (block
64
- (send
66
+ (call
65
67
  !nil? {:reject :delete_if :reject!})
66
68
  $(args ...)
67
- (send
69
+ (call
68
70
  $(lvar _) :nil?))
69
71
  PATTERN
70
72
 
71
73
  # @!method select_method?(node)
72
74
  def_node_matcher :select_method?, <<~PATTERN
73
75
  (block
74
- (send
76
+ (call
75
77
  !nil? {:select :select!})
76
78
  $(args ...)
77
- (send
78
- (send
79
+ (call
80
+ (call
79
81
  $(lvar _) :nil?) :!))
80
82
  PATTERN
81
83
 
84
+ # @!method grep_v_with_nil?(node)
85
+ def_node_matcher :grep_v_with_nil?, <<~PATTERN
86
+ (send _ :grep_v {(nil) (const {nil? cbase} :NilClass)})
87
+ PATTERN
88
+
82
89
  def on_send(node)
83
90
  return unless (range = offense_range(node))
84
91
  return if allowed_receiver?(node.receiver)
@@ -91,11 +98,13 @@ module RuboCop
91
98
 
92
99
  add_offense(range, message: message) { |corrector| corrector.replace(range, good) }
93
100
  end
101
+ alias on_csend on_send
94
102
 
95
103
  private
96
104
 
105
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
97
106
  def offense_range(node)
98
- if reject_method_with_block_pass?(node)
107
+ if reject_method_with_block_pass?(node) || grep_v_with_nil?(node)
99
108
  range(node, node)
100
109
  else
101
110
  block_node = node.parent
@@ -109,6 +118,7 @@ module RuboCop
109
118
  range(node, block_node)
110
119
  end
111
120
  end
121
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
112
122
 
113
123
  def to_enum_method?(node)
114
124
  return false unless node.receiver.send_type?
@@ -59,8 +59,6 @@ module RuboCop
59
59
  class CombinableLoops < Base
60
60
  extend AutoCorrector
61
61
 
62
- include RangeHelp
63
-
64
62
  MSG = 'Combine this loop with the previous loop.'
65
63
 
66
64
  def on_block(node)
@@ -105,11 +103,19 @@ module RuboCop
105
103
  end
106
104
 
107
105
  def combine_with_left_sibling(corrector, node)
108
- corrector.replace(
109
- node.left_sibling.body,
110
- "#{node.left_sibling.body.source}\n#{node.body.source}"
111
- )
112
- corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
106
+ corrector.remove(node.left_sibling.body.source_range.end.join(node.left_sibling.loc.end))
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)
113
119
  end
114
120
  end
115
121
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../../directive_comment'
4
+
3
5
  module RuboCop
4
6
  module Cop
5
7
  module Style
@@ -49,8 +51,9 @@ module RuboCop
49
51
  KEYWORDS = %w[begin class def end module].freeze
50
52
  KEYWORD_REGEXES = KEYWORDS.map { |w| /^\s*#{w}\s/ }.freeze
51
53
 
52
- ALLOWED_COMMENTS = %w[:nodoc: :yields: rubocop:disable rubocop:todo].freeze
53
- ALLOWED_COMMENT_REGEXES = ALLOWED_COMMENTS.map { |c| /#\s*#{c}/ }.freeze
54
+ ALLOWED_COMMENTS = %w[:nodoc: :yields:].freeze
55
+ ALLOWED_COMMENT_REGEXES = (ALLOWED_COMMENTS.map { |c| /#\s*#{c}/ } +
56
+ [DirectiveComment::DIRECTIVE_COMMENT_REGEXP]).freeze
54
57
 
55
58
  REGEXP = /(?<keyword>\S+).*#/.freeze
56
59
 
@@ -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
 
@@ -115,8 +115,8 @@ module RuboCop
115
115
  end
116
116
 
117
117
  # Check for `if` and `case` statements where each branch is used for
118
- # assignment to the same variable when using the return of the
119
- # condition can be used instead.
118
+ # both the assignment and comparison of the same variable
119
+ # when using the return of the condition can be used instead.
120
120
  #
121
121
  # @example EnforcedStyle: assign_to_condition (default)
122
122
  # # bad
@@ -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
 
@@ -460,9 +460,8 @@ module RuboCop
460
460
 
461
461
  def assignment(node)
462
462
  *_, condition = *node
463
- Parser::Source::Range.new(node.source_range.source_buffer,
464
- node.source_range.begin_pos,
465
- condition.source_range.begin_pos)
463
+
464
+ node.source_range.begin.join(condition.source_range.begin)
466
465
  end
467
466
 
468
467
  def correct_if_branches(corrector, cop, node)
@@ -534,7 +533,7 @@ module RuboCop
534
533
  end
535
534
 
536
535
  def element_assignment?(node)
537
- node.send_type? && node.method_name != :[]=
536
+ node.send_type? && !node.method?(:[]=)
538
537
  end
539
538
 
540
539
  def extract_branches(node)