rubocop 1.75.8 → 1.81.1

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 (164) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -16
  3. data/config/default.yml +117 -26
  4. data/config/obsoletion.yml +6 -3
  5. data/exe/rubocop +1 -8
  6. data/lib/rubocop/cli/command/auto_generate_config.rb +2 -2
  7. data/lib/rubocop/cli.rb +18 -3
  8. data/lib/rubocop/config_loader.rb +4 -39
  9. data/lib/rubocop/config_store.rb +5 -0
  10. data/lib/rubocop/cop/autocorrect_logic.rb +4 -4
  11. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  12. data/lib/rubocop/cop/correctors/alignment_corrector.rb +7 -4
  13. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
  14. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
  15. data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
  16. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +0 -22
  17. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  18. data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
  19. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  20. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
  21. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +4 -1
  22. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
  23. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  24. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
  25. data/lib/rubocop/cop/layout/class_structure.rb +1 -1
  26. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  27. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  28. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +30 -12
  29. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +101 -0
  30. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -1
  31. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +8 -29
  32. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +1 -1
  33. data/lib/rubocop/cop/layout/line_length.rb +35 -6
  34. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +8 -4
  35. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +8 -0
  36. data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
  37. data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
  38. data/lib/rubocop/cop/layout/space_before_brackets.rb +2 -9
  39. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -2
  40. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  41. data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
  42. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -1
  43. data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
  44. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
  45. data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
  46. data/lib/rubocop/cop/lint/float_comparison.rb +4 -4
  47. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  48. data/lib/rubocop/cop/lint/literal_as_condition.rb +34 -28
  49. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -2
  50. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
  51. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  52. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
  53. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
  54. data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
  55. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  56. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
  57. data/lib/rubocop/cop/lint/self_assignment.rb +30 -4
  58. data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
  59. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
  60. data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
  61. data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
  62. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
  63. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
  64. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  65. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
  66. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
  67. data/lib/rubocop/cop/lint/void.rb +7 -0
  68. data/lib/rubocop/cop/message_annotator.rb +1 -1
  69. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  70. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  71. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
  72. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  73. data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
  74. data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
  75. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  76. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  77. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  78. data/lib/rubocop/cop/naming/method_name.rb +127 -13
  79. data/lib/rubocop/cop/naming/predicate_method.rb +319 -0
  80. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -4
  81. data/lib/rubocop/cop/security/eval.rb +2 -1
  82. data/lib/rubocop/cop/security/open.rb +1 -0
  83. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
  84. data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
  85. data/lib/rubocop/cop/style/arguments_forwarding.rb +11 -17
  86. data/lib/rubocop/cop/style/array_intersect.rb +98 -34
  87. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +47 -0
  88. data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
  89. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  90. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  91. data/lib/rubocop/cop/style/collection_querying.rb +167 -0
  92. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -2
  93. data/lib/rubocop/cop/style/dig_chain.rb +1 -1
  94. data/lib/rubocop/cop/style/double_negation.rb +1 -1
  95. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  96. data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
  97. data/lib/rubocop/cop/style/exponential_notation.rb +3 -2
  98. data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
  99. data/lib/rubocop/cop/style/hash_conversion.rb +16 -8
  100. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  101. data/lib/rubocop/cop/style/if_unless_modifier.rb +13 -6
  102. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  103. data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
  104. data/lib/rubocop/cop/style/it_assignment.rb +69 -12
  105. data/lib/rubocop/cop/style/it_block_parameter.rb +36 -15
  106. data/lib/rubocop/cop/style/map_to_hash.rb +1 -3
  107. data/lib/rubocop/cop/style/map_to_set.rb +1 -3
  108. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -6
  109. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
  110. data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
  111. data/lib/rubocop/cop/style/nil_comparison.rb +9 -7
  112. data/lib/rubocop/cop/style/parallel_assignment.rb +32 -20
  113. data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
  114. data/lib/rubocop/cop/style/redundant_begin.rb +34 -0
  115. data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
  116. data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
  117. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
  118. data/lib/rubocop/cop/style/redundant_format.rb +18 -3
  119. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  120. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  121. data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
  122. data/lib/rubocop/cop/style/redundant_parentheses.rb +55 -16
  123. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -0
  124. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
  125. data/lib/rubocop/cop/style/redundant_self.rb +8 -5
  126. data/lib/rubocop/cop/style/safe_navigation.rb +44 -12
  127. data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
  128. data/lib/rubocop/cop/style/sole_nested_conditional.rb +32 -2
  129. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
  130. data/lib/rubocop/cop/style/string_concatenation.rb +17 -13
  131. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  132. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  133. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
  134. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  135. data/lib/rubocop/cop/style/unless_else.rb +10 -9
  136. data/lib/rubocop/cop/utils/format_string.rb +10 -0
  137. data/lib/rubocop/cop/variable_force/variable.rb +1 -1
  138. data/lib/rubocop/cop/variable_force.rb +25 -8
  139. data/lib/rubocop/cops_documentation_generator.rb +1 -0
  140. data/lib/rubocop/formatter/disabled_config_formatter.rb +18 -5
  141. data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
  142. data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
  143. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  144. data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
  145. data/lib/rubocop/lsp/diagnostic.rb +25 -24
  146. data/lib/rubocop/lsp/routes.rb +65 -9
  147. data/lib/rubocop/lsp/runtime.rb +2 -2
  148. data/lib/rubocop/lsp/server.rb +2 -2
  149. data/lib/rubocop/lsp/stdin_runner.rb +0 -16
  150. data/lib/rubocop/pending_cops_reporter.rb +56 -0
  151. data/lib/rubocop/result_cache.rb +14 -12
  152. data/lib/rubocop/rspec/expect_offense.rb +9 -3
  153. data/lib/rubocop/runner.rb +6 -4
  154. data/lib/rubocop/server/cache.rb +4 -2
  155. data/lib/rubocop/server/client_command/base.rb +10 -0
  156. data/lib/rubocop/server/client_command/exec.rb +2 -1
  157. data/lib/rubocop/server/client_command/start.rb +11 -1
  158. data/lib/rubocop/target_finder.rb +9 -9
  159. data/lib/rubocop/target_ruby.rb +10 -1
  160. data/lib/rubocop/version.rb +1 -1
  161. data/lib/rubocop.rb +12 -1
  162. data/lib/ruby_lsp/rubocop/addon.rb +25 -10
  163. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
  164. metadata +22 -8
@@ -0,0 +1,319 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Naming
6
+ # Checks that predicate methods end with `?` and non-predicate methods do not.
7
+ #
8
+ # The names of predicate methods (methods that return a boolean value) should end
9
+ # in a question mark. Methods that don't return a boolean, shouldn't
10
+ # end in a question mark.
11
+ #
12
+ # The cop assesses a predicate method as one that returns boolean values. Likewise,
13
+ # a method that only returns literal values is assessed as non-predicate. Other predicate
14
+ # method calls are assumed to return boolean values. The cop does not make an assessment
15
+ # if the return type is unknown (non-predicate method calls, variables, etc.).
16
+ #
17
+ # NOTE: The `initialize` method and operator methods (`def ==`, etc.) are ignored.
18
+ #
19
+ # By default, the cop runs in `conservative` mode, which allows a method to be named
20
+ # with a question mark as long as at least one return value is boolean. In `aggressive`
21
+ # mode, methods with a question mark will register an offense if any known non-boolean
22
+ # return values are detected.
23
+ #
24
+ # The cop also has `AllowedMethods` configuration in order to prevent the cop from
25
+ # registering an offense from a method name that does not confirm to the naming
26
+ # guidelines. By default, `call` is allowed. The cop also has `AllowedPatterns`
27
+ # configuration to allow method names by regular expression.
28
+ #
29
+ # Although returning a call to another predicate method is treated as a boolean value,
30
+ # certain method names can be known to not return a boolean, despite ending in a `?`
31
+ # (for example, `Numeric#nonzero?` returns `self` or `nil`). These methods can be
32
+ # configured using `NonBooleanPredicates`.
33
+ #
34
+ # The cop can furthermore be configured to allow all bang methods (method names
35
+ # ending with `!`), with `AllowBangMethods: true` (default false).
36
+ #
37
+ # @example Mode: conservative (default)
38
+ # # bad
39
+ # def foo
40
+ # bar == baz
41
+ # end
42
+ #
43
+ # # good
44
+ # def foo?
45
+ # bar == baz
46
+ # end
47
+ #
48
+ # # bad
49
+ # def foo?
50
+ # 5
51
+ # end
52
+ #
53
+ # # good
54
+ # def foo
55
+ # 5
56
+ # end
57
+ #
58
+ # # bad
59
+ # def foo
60
+ # x == y
61
+ # end
62
+ #
63
+ # # good
64
+ # def foo?
65
+ # x == y
66
+ # end
67
+ #
68
+ # # bad
69
+ # def foo
70
+ # !x
71
+ # end
72
+ #
73
+ # # good
74
+ # def foo?
75
+ # !x
76
+ # end
77
+ #
78
+ # # bad - returns the value of another predicate method
79
+ # def foo
80
+ # bar?
81
+ # end
82
+ #
83
+ # # good
84
+ # def foo?
85
+ # bar?
86
+ # end
87
+ #
88
+ # # good - operator method
89
+ # def ==(other)
90
+ # hash == other.hash
91
+ # end
92
+ #
93
+ # # good - at least one return value is boolean
94
+ # def foo?
95
+ # return unless bar?
96
+ # true
97
+ # end
98
+ #
99
+ # # ok - return type is not known
100
+ # def foo?
101
+ # bar
102
+ # end
103
+ #
104
+ # # ok - return type is not known
105
+ # def foo
106
+ # bar?
107
+ # end
108
+ #
109
+ # @example Mode: aggressive
110
+ # # bad - the method returns nil in some cases
111
+ # def foo?
112
+ # return unless bar?
113
+ # true
114
+ # end
115
+ #
116
+ # @example AllowedMethods: [call] (default)
117
+ # # good
118
+ # def call
119
+ # foo == bar
120
+ # end
121
+ #
122
+ # @example AllowedPatterns: [\Afoo]
123
+ # # good
124
+ # def foo?
125
+ # 'foo'
126
+ # end
127
+ #
128
+ # @example AllowBangMethods: false (default)
129
+ # # bad
130
+ # def save!
131
+ # true
132
+ # end
133
+ #
134
+ # @example AllowBangMethods: true
135
+ # # good
136
+ # def save!
137
+ # true
138
+ # end
139
+ #
140
+ class PredicateMethod < Base
141
+ include AllowedMethods
142
+ include AllowedPattern
143
+
144
+ MSG_PREDICATE = 'Predicate method names should end with `?`.'
145
+ MSG_NON_PREDICATE = 'Non-predicate method names should not end with `?`.'
146
+
147
+ def on_def(node)
148
+ return if allowed?(node)
149
+
150
+ return_values = return_values(node.body)
151
+ return if acceptable?(return_values)
152
+
153
+ if node.predicate_method? && potential_non_predicate?(return_values)
154
+ add_offense(node.loc.name, message: MSG_NON_PREDICATE)
155
+ elsif !node.predicate_method? && all_return_values_boolean?(return_values)
156
+ add_offense(node.loc.name, message: MSG_PREDICATE)
157
+ end
158
+ end
159
+ alias on_defs on_def
160
+
161
+ private
162
+
163
+ def allowed?(node)
164
+ node.method?(:initialize) ||
165
+ allowed_method?(node.method_name) ||
166
+ matches_allowed_pattern?(node.method_name) ||
167
+ allowed_bang_method?(node) ||
168
+ node.operator_method? ||
169
+ node.body.nil?
170
+ end
171
+
172
+ def acceptable?(return_values)
173
+ # In `conservative` mode, if the method returns `super`, `zsuper`, or a
174
+ # non-comparison method call, the method name is acceptable.
175
+ return false unless conservative?
176
+
177
+ return_values.any? do |value|
178
+ value.type?(:super, :zsuper) || unknown_method_call?(value)
179
+ end
180
+ end
181
+
182
+ def unknown_method_call?(value)
183
+ return false unless value.call_type?
184
+
185
+ !method_returning_boolean?(value)
186
+ end
187
+
188
+ def return_values(node)
189
+ # Collect all the (implicit and explicit) return values of a node
190
+ return_values = Set.new(node.begin_type? ? [] : [extract_return_value(node)])
191
+
192
+ node.each_descendant(:return) do |return_node|
193
+ return_values << extract_return_value(return_node)
194
+ end
195
+
196
+ last_value = last_value(node)
197
+ return_values << last_value if last_value
198
+
199
+ process_return_values(return_values)
200
+ end
201
+
202
+ def all_return_values_boolean?(return_values)
203
+ values = return_values.reject { |value| value.type?(:super, :zsuper) }
204
+ return false if values.empty?
205
+
206
+ values.all? { |value| boolean_return?(value) }
207
+ end
208
+
209
+ def boolean_return?(value)
210
+ return true if value.boolean_type?
211
+
212
+ method_returning_boolean?(value)
213
+ end
214
+
215
+ def method_returning_boolean?(value)
216
+ return false unless value.call_type?
217
+ return false if wayward_predicate?(value.method_name)
218
+
219
+ value.comparison_method? || value.predicate_method? || value.negation_method?
220
+ end
221
+
222
+ def potential_non_predicate?(return_values)
223
+ # Assumes a method to be non-predicate if all return values are non-boolean literals.
224
+ #
225
+ # In `Mode: conservative`, if any of the return values is a boolean,
226
+ # the method name is acceptable.
227
+ # In `Mode: aggressive`, all return values must be booleans for a predicate
228
+ # method, or else an offense will be registered.
229
+ return false if conservative? && return_values.any? { |value| boolean_return?(value) }
230
+
231
+ return_values.any? do |value|
232
+ value.literal? && !value.boolean_type?
233
+ end
234
+ end
235
+
236
+ def extract_return_value(node)
237
+ return node unless node.return_type?
238
+
239
+ # `return` without a value is a `nil` return.
240
+ return s(:nil) if node.arguments.empty?
241
+
242
+ # When there's a multiple return, it cannot be a predicate
243
+ # so just return an `array` sexp for simplicity.
244
+ return s(:array) unless node.arguments.one?
245
+
246
+ node.first_argument
247
+ end
248
+
249
+ def last_value(node)
250
+ value = node.begin_type? ? node.children.last : node
251
+ value&.return_type? ? extract_return_value(value) : value
252
+ end
253
+
254
+ def process_return_values(return_values)
255
+ return_values.flat_map do |value|
256
+ if value.conditional?
257
+ process_return_values(extract_conditional_branches(value))
258
+ elsif and_or?(value)
259
+ process_return_values(extract_and_or_clauses(value))
260
+ else
261
+ value
262
+ end
263
+ end
264
+ end
265
+
266
+ def and_or?(node)
267
+ node.type?(:and, :or)
268
+ end
269
+
270
+ def extract_and_or_clauses(node)
271
+ # Recursively traverse an `and` or `or` node to collect all clauses within
272
+ return node unless and_or?(node)
273
+
274
+ [extract_and_or_clauses(node.lhs), extract_and_or_clauses(node.rhs)].flatten
275
+ end
276
+
277
+ def extract_conditional_branches(node)
278
+ return node unless node.conditional?
279
+
280
+ if node.type?(:while, :until)
281
+ # If there is no body, act as implicit `nil`.
282
+ node.body ? [last_value(node.body)] : [s(:nil)]
283
+ else
284
+ # Branches with no value act as an implicit `nil`.
285
+ branches = node.branches.map { |branch| branch ? last_value(branch) : s(:nil) }
286
+ # Missing else branches also act as an implicit `nil`.
287
+ branches.push(s(:nil)) unless node.else_branch
288
+ branches
289
+ end
290
+ end
291
+
292
+ def conservative?
293
+ cop_config.fetch('Mode', :conservative).to_sym == :conservative
294
+ end
295
+
296
+ def allowed_bang_method?(node)
297
+ return false unless allow_bang_methods?
298
+
299
+ node.bang_method?
300
+ end
301
+
302
+ def allow_bang_methods?
303
+ cop_config.fetch('AllowBangMethods', false)
304
+ end
305
+
306
+ # If a method ending in `?` is known to not return a boolean value,
307
+ # (for example, `Numeric#nonzero?`) it should be treated as a non-boolean
308
+ # value, despite the method naming.
309
+ def wayward_predicate?(name)
310
+ wayward_predicates.include?(name.to_s)
311
+ end
312
+
313
+ def wayward_predicates
314
+ Array(cop_config.fetch('WaywardPredicates', []))
315
+ end
316
+ end
317
+ end
318
+ end
319
+ end
@@ -100,12 +100,12 @@ module RuboCop
100
100
  # # good
101
101
  # def_node_matcher(:even?) { |value| }
102
102
  #
103
- class PredicateName < Base
103
+ class PredicatePrefix < Base
104
104
  include AllowedMethods
105
105
 
106
106
  # @!method dynamic_method_define(node)
107
107
  def_node_matcher :dynamic_method_define, <<~PATTERN
108
- (send nil? #method_definition_macros
108
+ (send nil? #method_definition_macro?
109
109
  (sym $_)
110
110
  ...)
111
111
  PATTERN
@@ -143,7 +143,7 @@ module RuboCop
143
143
  next if predicate_prefixes.include?(forbidden_prefix)
144
144
 
145
145
  raise ValidationError, <<~MSG.chomp
146
- The `Naming/PredicateName` cop is misconfigured. Prefix #{forbidden_prefix} must be included in NamePrefix because it is included in ForbiddenPrefixes.
146
+ The `Naming/PredicatePrefix` cop is misconfigured. Prefix #{forbidden_prefix} must be included in NamePrefix because it is included in ForbiddenPrefixes.
147
147
  MSG
148
148
  end
149
149
  end
@@ -195,7 +195,7 @@ module RuboCop
195
195
  cop_config['UseSorbetSigs']
196
196
  end
197
197
 
198
- def method_definition_macros(macro_name)
198
+ def method_definition_macro?(macro_name)
199
199
  cop_config['MethodDefinitionMacros'].include?(macro_name.to_s)
200
200
  end
201
201
  end
@@ -11,13 +11,14 @@ module RuboCop
11
11
  #
12
12
  # eval(something)
13
13
  # binding.eval(something)
14
+ # Kernel.eval(something)
14
15
  class Eval < Base
15
16
  MSG = 'The use of `eval` is a serious security risk.'
16
17
  RESTRICT_ON_SEND = %i[eval].freeze
17
18
 
18
19
  # @!method eval?(node)
19
20
  def_node_matcher :eval?, <<~PATTERN
20
- (send {nil? (send nil? :binding)} :eval $!str ...)
21
+ (send {nil? (send nil? :binding) (const {cbase nil?} :Kernel)} :eval $!str ...)
21
22
  PATTERN
22
23
 
23
24
  def on_send(node)
@@ -34,6 +34,7 @@ module RuboCop
34
34
  # # good (literal strings)
35
35
  # open("foo.text")
36
36
  # URI.open("http://example.com")
37
+ # URI.parse(url).open
37
38
  class Open < Base
38
39
  MSG = 'The use of `%<receiver>sopen` is a serious security risk.'
39
40
  RESTRICT_ON_SEND = %i[open].freeze
@@ -348,7 +348,7 @@ module RuboCop
348
348
  end
349
349
 
350
350
  def remove_modifier_node_within_begin(corrector, modifier_node, begin_node)
351
- def_node = begin_node.children[1]
351
+ def_node = begin_node.children[begin_node.children.index(modifier_node) + 1]
352
352
  range = modifier_node.source_range.begin.join(def_node.source_range.begin)
353
353
  corrector.remove(range)
354
354
  end
@@ -84,7 +84,10 @@ module RuboCop
84
84
 
85
85
  def autocorrect(corrector, node)
86
86
  if (preferred_accessors = preferred_accessors(node))
87
- corrector.replace(node, preferred_accessors)
87
+ corrector.replace(
88
+ grouped_style? ? node : range_with_trailing_argument_comment(node),
89
+ preferred_accessors
90
+ )
88
91
  else
89
92
  range = range_with_surrounding_space(node.source_range, side: :left)
90
93
  corrector.remove(range)
@@ -196,6 +199,15 @@ module RuboCop
196
199
  end
197
200
  end.join("\n")
198
201
  end
202
+
203
+ def range_with_trailing_argument_comment(node)
204
+ comment = processed_source.ast_with_comments[node.last_argument].last
205
+ if comment
206
+ add_range(node.source_range, comment.source_range)
207
+ else
208
+ node
209
+ end
210
+ end
199
211
  end
200
212
  end
201
213
  end
@@ -146,7 +146,6 @@ module RuboCop
146
146
  minimum_target_ruby_version 2.7
147
147
 
148
148
  FORWARDING_LVAR_TYPES = %i[splat kwsplat block_pass].freeze
149
- ADDITIONAL_ARG_TYPES = %i[lvar arg optarg].freeze
150
149
 
151
150
  FORWARDING_MSG = 'Use shorthand syntax `...` for arguments forwarding.'
152
151
  ARGS_MSG = 'Use anonymous positional arguments forwarding (`*`).'
@@ -198,9 +197,9 @@ module RuboCop
198
197
  send_classifications.all? { |_, c, _, _| all_classifications.include?(c) }
199
198
  end
200
199
 
201
- # rubocop:disable Metrics/MethodLength
200
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
202
201
  def add_forward_all_offenses(node, send_classifications, forwardable_args)
203
- _rest_arg, _kwrest_arg, block_arg = *forwardable_args
202
+ rest_arg, kwrest_arg, block_arg = *forwardable_args
204
203
  registered_block_arg_offense = false
205
204
 
206
205
  send_classifications.each do |send_node, c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength
@@ -212,16 +211,20 @@ module RuboCop
212
211
  registered_block_arg_offense = true
213
212
  break
214
213
  else
215
- register_forward_all_offense(send_node, send_node, forward_rest)
214
+ first_arg = forward_rest || forward_kwrest || forward_all_first_argument(send_node)
215
+ register_forward_all_offense(send_node, send_node, first_arg)
216
216
  end
217
217
  end
218
218
 
219
219
  return if registered_block_arg_offense
220
220
 
221
- rest_arg, _kwrest_arg, _block_arg = *forwardable_args
222
- register_forward_all_offense(node, node.arguments, rest_arg)
221
+ register_forward_all_offense(node, node.arguments, rest_arg || kwrest_arg)
222
+ end
223
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
224
+
225
+ def forward_all_first_argument(node)
226
+ node.arguments.reverse_each.find(&:forwarded_restarg_type?)
223
227
  end
224
- # rubocop:enable Metrics/MethodLength
225
228
 
226
229
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
227
230
  def add_post_ruby_32_offenses(def_node, send_classifications, forwardable_args)
@@ -361,18 +364,9 @@ module RuboCop
361
364
  end
362
365
  end
363
366
 
364
- # rubocop:disable Metrics/AbcSize
365
367
  def arguments_range(node, first_node)
366
- arguments = node.arguments.reject do |arg|
367
- next true if ADDITIONAL_ARG_TYPES.include?(arg.type) || arg.variable? || arg.call_type?
368
-
369
- arg.literal? && arg.each_descendant(:kwsplat).none?
370
- end
371
-
372
- start_node = first_node || arguments.first
373
- start_node.source_range.begin.join(arguments.last.source_range.end)
368
+ first_node.source_range.begin.join(node.last_argument.source_range.end)
374
369
  end
375
- # rubocop:enable Metrics/AbcSize
376
370
 
377
371
  def allow_only_rest_arguments?
378
372
  cop_config.fetch('AllowOnlyRestArgument', true)
@@ -5,12 +5,17 @@ module RuboCop
5
5
  module Style
6
6
  # In Ruby 3.1, `Array#intersect?` has been added.
7
7
  #
8
- # This cop identifies places where `(array1 & array2).any?`
9
- # or `(array1.intersection(array2)).any?` can be replaced by
10
- # `array1.intersect?(array2)`.
8
+ # This cop identifies places where:
11
9
  #
12
- # The `array1.intersect?(array2)` method is faster than
13
- # `(array1 & array2).any?` and is more readable.
10
+ # * `(array1 & array2).any?`
11
+ # * `(array1.intersection(array2)).any?`
12
+ # * `array1.any? { |elem| array2.member?(elem) }`
13
+ # * `(array1 & array2).count > 0`
14
+ # * `(array1 & array2).size > 0`
15
+ #
16
+ # can be replaced with `array1.intersect?(array2)`.
17
+ #
18
+ # `array1.intersect?(array2)` is faster and more readable.
14
19
  #
15
20
  # In cases like the following, compatibility is not ensured,
16
21
  # so it will not be detected when using block argument.
@@ -40,10 +45,27 @@ module RuboCop
40
45
  # array1.intersection(array2).empty?
41
46
  # array1.intersection(array2).none?
42
47
  #
48
+ # # bad
49
+ # array1.any? { |elem| array2.member?(elem) }
50
+ # array1.none? { |elem| array2.member?(elem) }
51
+ #
43
52
  # # good
44
53
  # array1.intersect?(array2)
45
54
  # !array1.intersect?(array2)
46
55
  #
56
+ # # bad
57
+ # (array1 & array2).count > 0
58
+ # (array1 & array2).count.positive?
59
+ # (array1 & array2).count != 0
60
+ #
61
+ # (array1 & array2).count == 0
62
+ # (array1 & array2).count.zero?
63
+ #
64
+ # # good
65
+ # array1.intersect?(array2)
66
+ #
67
+ # !array1.intersect?(array2)
68
+ #
47
69
  # @example AllCops:ActiveSupportExtensionsEnabled: false (default)
48
70
  # # good
49
71
  # (array1 & array2).present?
@@ -66,9 +88,11 @@ module RuboCop
66
88
  PREDICATES = %i[any? empty? none?].to_set.freeze
67
89
  ACTIVE_SUPPORT_PREDICATES = (PREDICATES + %i[present? blank?]).freeze
68
90
 
91
+ ARRAY_SIZE_METHODS = %i[count length size].to_set.freeze
92
+
69
93
  # @!method bad_intersection_check?(node, predicates)
70
94
  def_node_matcher :bad_intersection_check?, <<~PATTERN
71
- (call
95
+ $(call
72
96
  {
73
97
  (begin (send $_ :& $_))
74
98
  (call $_ :intersection $_)
@@ -77,53 +101,93 @@ module RuboCop
77
101
  )
78
102
  PATTERN
79
103
 
80
- MSG = 'Use `%<negated>s%<receiver>s%<dot>sintersect?(%<argument>s)` ' \
81
- 'instead of `%<existing>s`.'
82
- STRAIGHT_METHODS = %i[present? any?].freeze
83
- NEGATED_METHODS = %i[blank? empty? none?].freeze
104
+ # @!method intersection_size_check?(node, predicates)
105
+ def_node_matcher :intersection_size_check?, <<~PATTERN
106
+ (call
107
+ $(call
108
+ {
109
+ (begin (send $_ :& $_))
110
+ (call $_ :intersection $_)
111
+ }
112
+ %ARRAY_SIZE_METHODS
113
+ )
114
+ {$:> (int 0) | $:positive? | $:!= (int 0) | $:== (int 0) | $:zero?}
115
+ )
116
+ PATTERN
117
+
118
+ # @!method any_none_block_intersection(node)
119
+ def_node_matcher :any_none_block_intersection, <<~PATTERN
120
+ {
121
+ (block
122
+ (call $_receiver ${:any? :none?})
123
+ (args (arg _key))
124
+ (send $_argument :member? (lvar _key))
125
+ )
126
+ (numblock
127
+ (call $_receiver ${:any? :none?}) 1
128
+ (send $_argument :member? (lvar :_1))
129
+ )
130
+ (itblock
131
+ (call $_receiver ${:any? :none?}) :it
132
+ (send $_argument :member? (lvar :it))
133
+ )
134
+ }
135
+ PATTERN
136
+
137
+ MSG = 'Use `%<replacement>s` instead of `%<existing>s`.'
138
+ STRAIGHT_METHODS = %i[present? any? > positive? !=].freeze
139
+ NEGATED_METHODS = %i[blank? empty? none? == zero?].freeze
84
140
  RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
85
141
 
86
142
  def on_send(node)
87
143
  return if node.block_literal?
88
- return unless (receiver, argument, method_name = bad_intersection?(node))
89
-
90
- dot = node.loc.dot.source
91
- message = message(receiver.source, argument.source, method_name, dot, node.source)
144
+ return unless (dot_node, receiver, argument, method_name = bad_intersection?(node))
92
145
 
93
- add_offense(node, message: message) do |corrector|
94
- bang = straight?(method_name) ? '' : '!'
146
+ dot = dot_node.loc.dot.source
147
+ bang = straight?(method_name) ? '' : '!'
148
+ replacement = "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})"
95
149
 
96
- corrector.replace(node, "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})")
97
- end
150
+ register_offense(node, replacement)
98
151
  end
99
152
  alias on_csend on_send
100
153
 
154
+ def on_block(node)
155
+ return unless (receiver, method_name, argument = any_none_block_intersection(node))
156
+
157
+ dot = node.send_node.loc.dot.source
158
+ bang = method_name == :any? ? '' : '!'
159
+ replacement = "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})"
160
+
161
+ register_offense(node, replacement)
162
+ end
163
+ alias on_numblock on_block
164
+ alias on_itblock on_block
165
+
101
166
  private
102
167
 
103
168
  def bad_intersection?(node)
104
- predicates = if active_support_extensions_enabled?
105
- ACTIVE_SUPPORT_PREDICATES
106
- else
107
- PREDICATES
108
- end
169
+ bad_intersection_check?(node, bad_intersection_predicates) ||
170
+ intersection_size_check?(node)
171
+ end
109
172
 
110
- bad_intersection_check?(node, predicates)
173
+ def bad_intersection_predicates
174
+ if active_support_extensions_enabled?
175
+ ACTIVE_SUPPORT_PREDICATES
176
+ else
177
+ PREDICATES
178
+ end
111
179
  end
112
180
 
113
181
  def straight?(method_name)
114
182
  STRAIGHT_METHODS.include?(method_name.to_sym)
115
183
  end
116
184
 
117
- def message(receiver, argument, method_name, dot, existing)
118
- negated = straight?(method_name) ? '' : '!'
119
- format(
120
- MSG,
121
- negated: negated,
122
- receiver: receiver,
123
- argument: argument,
124
- dot: dot,
125
- existing: existing
126
- )
185
+ def register_offense(node, replacement)
186
+ message = format(MSG, replacement: replacement, existing: node.source)
187
+
188
+ add_offense(node, message: message) do |corrector|
189
+ corrector.replace(node, replacement)
190
+ end
127
191
  end
128
192
  end
129
193
  end