rubocop 1.77.0 → 1.80.2

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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -3
  3. data/config/default.yml +36 -20
  4. data/exe/rubocop +1 -8
  5. data/lib/rubocop/cli.rb +17 -1
  6. data/lib/rubocop/config_loader.rb +1 -38
  7. data/lib/rubocop/cop/correctors/alignment_corrector.rb +6 -3
  8. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
  9. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  10. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
  11. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
  12. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +101 -0
  13. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +8 -29
  14. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +1 -1
  15. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -0
  16. data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
  17. data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
  18. data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
  19. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
  20. data/lib/rubocop/cop/lint/literal_as_condition.rb +15 -1
  21. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -2
  22. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
  23. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
  24. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
  25. data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
  26. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  27. data/lib/rubocop/cop/lint/self_assignment.rb +5 -4
  28. data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
  29. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
  30. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
  31. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
  32. data/lib/rubocop/cop/naming/method_name.rb +127 -13
  33. data/lib/rubocop/cop/naming/predicate_method.rb +30 -4
  34. data/lib/rubocop/cop/security/eval.rb +2 -1
  35. data/lib/rubocop/cop/security/open.rb +1 -0
  36. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
  37. data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
  38. data/lib/rubocop/cop/style/arguments_forwarding.rb +11 -17
  39. data/lib/rubocop/cop/style/array_intersect.rb +98 -34
  40. data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
  41. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  42. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  43. data/lib/rubocop/cop/style/dig_chain.rb +1 -1
  44. data/lib/rubocop/cop/style/exponential_notation.rb +1 -0
  45. data/lib/rubocop/cop/style/hash_conversion.rb +8 -9
  46. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  47. data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
  48. data/lib/rubocop/cop/style/it_assignment.rb +69 -12
  49. data/lib/rubocop/cop/style/it_block_parameter.rb +3 -1
  50. data/lib/rubocop/cop/style/map_to_hash.rb +1 -3
  51. data/lib/rubocop/cop/style/map_to_set.rb +1 -3
  52. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
  53. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
  54. data/lib/rubocop/cop/style/parallel_assignment.rb +32 -20
  55. data/lib/rubocop/cop/style/redundant_begin.rb +34 -0
  56. data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
  57. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
  58. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  59. data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
  60. data/lib/rubocop/cop/style/redundant_parentheses.rb +28 -11
  61. data/lib/rubocop/cop/style/safe_navigation.rb +20 -1
  62. data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
  63. data/lib/rubocop/cop/style/sole_nested_conditional.rb +30 -1
  64. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
  65. data/lib/rubocop/cop/style/string_concatenation.rb +17 -13
  66. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  67. data/lib/rubocop/cop/variable_force/variable.rb +1 -1
  68. data/lib/rubocop/cop/variable_force.rb +25 -8
  69. data/lib/rubocop/cops_documentation_generator.rb +1 -0
  70. data/lib/rubocop/formatter/disabled_config_formatter.rb +18 -5
  71. data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
  72. data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
  73. data/lib/rubocop/lsp/routes.rb +35 -6
  74. data/lib/rubocop/pending_cops_reporter.rb +56 -0
  75. data/lib/rubocop/result_cache.rb +14 -12
  76. data/lib/rubocop/runner.rb +6 -4
  77. data/lib/rubocop/server/cache.rb +4 -2
  78. data/lib/rubocop/server/client_command/base.rb +10 -0
  79. data/lib/rubocop/server/client_command/exec.rb +2 -1
  80. data/lib/rubocop/server/client_command/start.rb +11 -1
  81. data/lib/rubocop/target_finder.rb +9 -9
  82. data/lib/rubocop/version.rb +1 -1
  83. data/lib/rubocop.rb +3 -0
  84. metadata +9 -6
@@ -39,6 +39,26 @@ module RuboCop
39
39
  # # good
40
40
  # def foo_bar; end
41
41
  #
42
+ # # bad
43
+ # define_method :fooBar do
44
+ # end
45
+ #
46
+ # # good
47
+ # define_method :foo_bar do
48
+ # end
49
+ #
50
+ # # bad
51
+ # Struct.new(:fooBar)
52
+ #
53
+ # # good
54
+ # Struct.new(:foo_bar)
55
+ #
56
+ # # bad
57
+ # alias_method :fooBar, :some_method
58
+ #
59
+ # # good
60
+ # alias_method :foo_bar, :some_method
61
+ #
42
62
  # @example EnforcedStyle: camelCase
43
63
  # # bad
44
64
  # def foo_bar; end
@@ -46,6 +66,26 @@ module RuboCop
46
66
  # # good
47
67
  # def fooBar; end
48
68
  #
69
+ # # bad
70
+ # define_method :foo_bar do
71
+ # end
72
+ #
73
+ # # good
74
+ # define_method :fooBar do
75
+ # end
76
+ #
77
+ # # bad
78
+ # Struct.new(:foo_bar)
79
+ #
80
+ # # good
81
+ # Struct.new(:fooBar)
82
+ #
83
+ # # bad
84
+ # alias_method :foo_bar, :some_method
85
+ #
86
+ # # good
87
+ # alias_method :fooBar, :some_method
88
+ #
49
89
  # @example ForbiddenIdentifiers: ['def', 'super']
50
90
  # # bad
51
91
  # def def; end
@@ -66,13 +106,79 @@ module RuboCop
66
106
  MSG = 'Use %<style>s for method names.'
67
107
  MSG_FORBIDDEN = '`%<identifier>s` is forbidden, use another method name instead.'
68
108
 
109
+ OPERATOR_METHODS = %i[| ^ & <=> == === =~ > >= < <= << >> + - * /
110
+ % ** ~ +@ -@ !@ ~@ [] []= ! != !~ `].to_set.freeze
111
+
69
112
  # @!method sym_name(node)
70
113
  def_node_matcher :sym_name, '(sym $_name)'
71
114
 
72
115
  # @!method str_name(node)
73
116
  def_node_matcher :str_name, '(str $_name)'
74
117
 
118
+ # @!method new_struct?(node)
119
+ def_node_matcher :new_struct?, '(send (const {nil? cbase} :Struct) :new ...)'
120
+
121
+ # @!method define_data?(node)
122
+ def_node_matcher :define_data?, '(send (const {nil? cbase} :Data) :define ...)'
123
+
75
124
  def on_send(node)
125
+ if node.method?(:define_method) || node.method?(:define_singleton_method)
126
+ handle_define_method(node)
127
+ elsif new_struct?(node)
128
+ handle_new_struct(node)
129
+ elsif define_data?(node)
130
+ handle_define_data(node)
131
+ elsif node.method?(:alias_method)
132
+ handle_alias_method(node)
133
+ else
134
+ handle_attr_accessor(node)
135
+ end
136
+ end
137
+
138
+ def on_def(node)
139
+ return if node.operator_method? || matches_allowed_pattern?(node.method_name)
140
+
141
+ if forbidden_name?(node.method_name.to_s)
142
+ register_forbidden_name(node)
143
+ else
144
+ check_name(node, node.method_name, node.loc.name)
145
+ end
146
+ end
147
+ alias on_defs on_def
148
+
149
+ def on_alias(node)
150
+ handle_method_name(node.new_identifier, node.new_identifier.value)
151
+ end
152
+
153
+ private
154
+
155
+ def handle_define_method(node)
156
+ return unless node.first_argument&.type?(:str, :sym)
157
+
158
+ handle_method_name(node, node.first_argument.value)
159
+ end
160
+
161
+ def handle_new_struct(node)
162
+ arguments = node.first_argument&.str_type? ? node.arguments[1..] : node.arguments
163
+ arguments.select { |argument| argument.type?(:sym, :str) }.each do |name|
164
+ handle_method_name(name, name.value)
165
+ end
166
+ end
167
+
168
+ def handle_define_data(node)
169
+ node.arguments.select { |argument| argument.type?(:sym, :str) }.each do |name|
170
+ handle_method_name(name, name.value)
171
+ end
172
+ end
173
+
174
+ def handle_alias_method(node)
175
+ return unless node.arguments.size == 2
176
+ return unless node.first_argument.type?(:str, :sym)
177
+
178
+ handle_method_name(node.first_argument, node.first_argument.value)
179
+ end
180
+
181
+ def handle_attr_accessor(node)
76
182
  return unless (attrs = node.attribute_accessor?)
77
183
 
78
184
  attrs.last.each do |name_item|
@@ -87,45 +193,53 @@ module RuboCop
87
193
  end
88
194
  end
89
195
 
90
- def on_def(node)
91
- return if node.operator_method? || matches_allowed_pattern?(node.method_name)
196
+ def handle_method_name(node, name)
197
+ return if !name || matches_allowed_pattern?(name)
92
198
 
93
- if forbidden_name?(node.method_name.to_s)
199
+ if forbidden_name?(name.to_s)
94
200
  register_forbidden_name(node)
95
- else
96
- check_name(node, node.method_name, node.loc.name)
201
+ elsif !OPERATOR_METHODS.include?(name.to_sym)
202
+ check_name(node, name, range_position(node))
97
203
  end
98
204
  end
99
- alias on_defs on_def
100
-
101
- private
102
205
 
103
206
  def forbidden_name?(name)
104
207
  forbidden_identifier?(name) || forbidden_pattern?(name)
105
208
  end
106
209
 
210
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
107
211
  def register_forbidden_name(node)
108
212
  if node.any_def_type?
109
213
  name_node = node.loc.name
110
214
  method_name = node.method_name
111
- else
112
- attrs = node.attribute_accessor?
215
+ elsif node.literal?
216
+ name_node = node
217
+ method_name = node.value
218
+ elsif (attrs = node.attribute_accessor?)
113
219
  name_node = attrs.last.last
114
220
  method_name = attr_name(name_node)
221
+ else
222
+ name_node = node.first_argument
223
+ method_name = node.first_argument.value
115
224
  end
116
225
  message = format(MSG_FORBIDDEN, identifier: method_name)
117
226
  add_offense(name_node, message: message)
118
227
  end
228
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
119
229
 
120
230
  def attr_name(name_item)
121
231
  sym_name(name_item) || str_name(name_item)
122
232
  end
123
233
 
124
234
  def range_position(node)
125
- selector_end_pos = node.loc.selector.end_pos + 1
126
- expr_end_pos = node.source_range.end_pos
235
+ if node.loc.respond_to?(:selector)
236
+ selector_end_pos = node.loc.selector.end_pos + 1
237
+ expr_end_pos = node.source_range.end_pos
127
238
 
128
- range_between(selector_end_pos, expr_end_pos)
239
+ range_between(selector_end_pos, expr_end_pos)
240
+ else
241
+ node.source_range
242
+ end
129
243
  end
130
244
 
131
245
  def message(style)
@@ -14,7 +14,7 @@ module RuboCop
14
14
  # method calls are assumed to return boolean values. The cop does not make an assessment
15
15
  # if the return type is unknown (non-predicate method calls, variables, etc.).
16
16
  #
17
- # NOTE: Operator methods (`def ==`, etc.) are ignored.
17
+ # NOTE: The `initialize` method and operator methods (`def ==`, etc.) are ignored.
18
18
  #
19
19
  # By default, the cop runs in `conservative` mode, which allows a method to be named
20
20
  # with a question mark as long as at least one return value is boolean. In `aggressive`
@@ -26,6 +26,11 @@ module RuboCop
26
26
  # guidelines. By default, `call` is allowed. The cop also has `AllowedPatterns`
27
27
  # configuration to allow method names by regular expression.
28
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
+ #
29
34
  # The cop can furthermore be configured to allow all bang methods (method names
30
35
  # ending with `!`), with `AllowBangMethods: true` (default false).
31
36
  #
@@ -144,7 +149,8 @@ module RuboCop
144
149
  private
145
150
 
146
151
  def allowed?(node)
147
- allowed_method?(node.method_name) ||
152
+ node.method?(:initialize) ||
153
+ allowed_method?(node.method_name) ||
148
154
  matches_allowed_pattern?(node.method_name) ||
149
155
  allowed_bang_method?(node) ||
150
156
  node.operator_method? ||
@@ -164,7 +170,7 @@ module RuboCop
164
170
  def unknown_method_call?(value)
165
171
  return false unless value.call_type?
166
172
 
167
- !value.comparison_method? && !value.predicate_method? && !value.negation_method?
173
+ !method_returning_boolean?(value)
168
174
  end
169
175
 
170
176
  def return_values(node)
@@ -190,7 +196,13 @@ module RuboCop
190
196
 
191
197
  def boolean_return?(value)
192
198
  return true if value.boolean_type?
199
+
200
+ method_returning_boolean?(value)
201
+ end
202
+
203
+ def method_returning_boolean?(value)
193
204
  return false unless value.call_type?
205
+ return false if wayward_predicate?(value.method_name)
194
206
 
195
207
  value.comparison_method? || value.predicate_method? || value.negation_method?
196
208
  end
@@ -258,7 +270,10 @@ module RuboCop
258
270
  node.body ? [last_value(node.body)] : [s(:nil)]
259
271
  else
260
272
  # Branches with no value act as an implicit `nil`.
261
- node.branches.filter_map { |branch| branch ? last_value(branch) : s(:nil) }
273
+ branches = node.branches.map { |branch| branch ? last_value(branch) : s(:nil) }
274
+ # Missing else branches also act as an implicit `nil`.
275
+ branches.push(s(:nil)) unless node.else_branch
276
+ branches
262
277
  end
263
278
  end
264
279
 
@@ -275,6 +290,17 @@ module RuboCop
275
290
  def allow_bang_methods?
276
291
  cop_config.fetch('AllowBangMethods', false)
277
292
  end
293
+
294
+ # If a method ending in `?` is known to not return a boolean value,
295
+ # (for example, `Numeric#nonzero?`) it should be treated as a non-boolean
296
+ # value, despite the method naming.
297
+ def wayward_predicate?(name)
298
+ wayward_predicates.include?(name.to_s)
299
+ end
300
+
301
+ def wayward_predicates
302
+ Array(cop_config.fetch('WaywardPredicates', []))
303
+ end
278
304
  end
279
305
  end
280
306
  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
@@ -70,18 +70,25 @@ module RuboCop
70
70
  (send _ :& _))
71
71
  PATTERN
72
72
 
73
+ # rubocop:disable Metrics/AbcSize
73
74
  def on_send(node)
74
75
  return unless node.receiver&.begin_type?
75
76
  return unless (preferred_method = preferred_method(node))
76
77
 
77
78
  bit_operation = node.receiver.children.first
78
79
  lhs, _operator, rhs = *bit_operation
79
- preferred = "#{lhs.source}.#{preferred_method}(#{rhs.source})"
80
+
81
+ preferred = if preferred_method == 'allbits?' && lhs.source == node.first_argument.source
82
+ "#{rhs.source}.allbits?(#{lhs.source})"
83
+ else
84
+ "#{lhs.source}.#{preferred_method}(#{rhs.source})"
85
+ end
80
86
 
81
87
  add_offense(node, message: format(MSG, preferred: preferred)) do |corrector|
82
88
  corrector.replace(node, preferred)
83
89
  end
84
90
  end
91
+ # rubocop:enable Metrics/AbcSize
85
92
 
86
93
  private
87
94
 
@@ -4,7 +4,7 @@
4
4
  module RuboCop
5
5
  module Cop
6
6
  module Style
7
- # Check for uses of braces or do/end around single line or
7
+ # Checks for uses of braces or do/end around single line or
8
8
  # multi-line blocks.
9
9
  #
10
10
  # Methods that can be either procedural or functional and cannot be
@@ -111,7 +111,7 @@ module RuboCop
111
111
  end
112
112
  end
113
113
 
114
- # Check for `if` and `case` statements where each branch is used for
114
+ # Checks for `if` and `case` statements where each branch is used for
115
115
  # both the assignment and comparison of the same variable
116
116
  # when using the return of the condition can be used instead.
117
117
  #
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Check for chained `dig` calls that can be collapsed into a single `dig`.
6
+ # Checks for chained `dig` calls that can be collapsed into a single `dig`.
7
7
  #
8
8
  # @safety
9
9
  # This cop is unsafe because it cannot be guaranteed that the receiver
@@ -59,6 +59,7 @@ module RuboCop
59
59
  #
60
60
  class ExponentialNotation < Base
61
61
  include ConfigurableEnforcedStyle
62
+
62
63
  MESSAGES = {
63
64
  scientific: 'Use a mantissa >= 1 and < 10.',
64
65
  engineering: 'Use an exponent divisible by 3 and a mantissa >= 0.1 and < 1000.',