rubocop-performance 1.18.0 → 1.23.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +3 -1
  4. data/config/default.yml +34 -18
  5. data/lib/rubocop/cop/mixin/sort_block.rb +2 -2
  6. data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +2 -1
  7. data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +43 -19
  8. data/lib/rubocop/cop/performance/block_given_with_explicit_block.rb +7 -0
  9. data/lib/rubocop/cop/performance/casecmp.rb +6 -0
  10. data/lib/rubocop/cop/performance/chain_array_allocation.rb +11 -4
  11. data/lib/rubocop/cop/performance/count.rb +7 -7
  12. data/lib/rubocop/cop/performance/delete_prefix.rb +5 -2
  13. data/lib/rubocop/cop/performance/delete_suffix.rb +5 -2
  14. data/lib/rubocop/cop/performance/detect.rb +3 -2
  15. data/lib/rubocop/cop/performance/double_start_end_with.rb +10 -10
  16. data/lib/rubocop/cop/performance/end_with.rb +6 -3
  17. data/lib/rubocop/cop/performance/fixed_size.rb +4 -3
  18. data/lib/rubocop/cop/performance/flat_map.rb +4 -3
  19. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +16 -10
  20. data/lib/rubocop/cop/performance/map_compact.rb +8 -6
  21. data/lib/rubocop/cop/performance/map_method_chain.rb +89 -0
  22. data/lib/rubocop/cop/performance/range_include.rb +8 -6
  23. data/lib/rubocop/cop/performance/redundant_block_call.rb +2 -0
  24. data/lib/rubocop/cop/performance/redundant_equality_comparison_block.rb +2 -2
  25. data/lib/rubocop/cop/performance/redundant_match.rb +30 -1
  26. data/lib/rubocop/cop/performance/redundant_merge.rb +2 -2
  27. data/lib/rubocop/cop/performance/redundant_split_regexp_argument.rb +2 -1
  28. data/lib/rubocop/cop/performance/redundant_string_chars.rb +1 -1
  29. data/lib/rubocop/cop/performance/regexp_match.rb +1 -8
  30. data/lib/rubocop/cop/performance/reverse_each.rb +2 -1
  31. data/lib/rubocop/cop/performance/reverse_first.rb +6 -13
  32. data/lib/rubocop/cop/performance/select_map.rb +3 -6
  33. data/lib/rubocop/cop/performance/size.rb +4 -3
  34. data/lib/rubocop/cop/performance/sort_reverse.rb +5 -4
  35. data/lib/rubocop/cop/performance/squeeze.rb +10 -3
  36. data/lib/rubocop/cop/performance/start_with.rb +6 -3
  37. data/lib/rubocop/cop/performance/string_bytesize.rb +45 -0
  38. data/lib/rubocop/cop/performance/string_identifier_argument.rb +58 -10
  39. data/lib/rubocop/cop/performance/string_include.rb +11 -5
  40. data/lib/rubocop/cop/performance/string_replacement.rb +2 -1
  41. data/lib/rubocop/cop/performance/sum.rb +12 -10
  42. data/lib/rubocop/cop/performance/times_map.rb +14 -2
  43. data/lib/rubocop/cop/performance/unfreeze_string.rb +6 -3
  44. data/lib/rubocop/cop/performance/uri_default_parser.rb +1 -1
  45. data/lib/rubocop/cop/performance_cops.rb +2 -0
  46. data/lib/rubocop/performance/version.rb +1 -1
  47. data/lib/rubocop-performance.rb +8 -0
  48. metadata +16 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f4926be80598b4880037ffd36eef32c9025051af7f0398f69a34439c135c9d87
4
- data.tar.gz: ba3d738da529cbc70c33e8c5d4baa4c6c8013a5498c834f2e28dc87a8811c15a
3
+ metadata.gz: 332ca643753a81ff66c4a6050117f7a479bd3aaa3f186c361a596d06b16aa9b5
4
+ data.tar.gz: 2fb149f85f986d9d0302e6fbe4c7389f312bb93a15a93cec49ffd74495853c78
5
5
  SHA512:
6
- metadata.gz: 1b9a64379554a660c30bffea5a0fc8a8d36a9e31efef6ccb4576f4df432d2f270d91d76b0e7e7638740e6cf83a50b94b4dfe37d93e5366d0171b8a0f4e5bff50
7
- data.tar.gz: d27b9db647eb1a53f3e80b0ec20cd43b37b0244127a28f7fbdacefcf9477205a7f4d373c0a27d882fcb43f902d4ab398ad73ea3f51cdd21e5d422cd419d621e9
6
+ metadata.gz: e03b8e178f4cde75ef5d82aa7483da512f2816afd82c97b414a4bfcd7afacf589ebb033d6bed40d0968217ec0ff24cb69e2a6c4f28a52ecbdb31cef5599e5386
7
+ data.tar.gz: 410a72e5e85fe6cdc8002b51c8b185dc4d2a1dea5f3a5889c146364c2a20b5b50256c2a90fef59604137dd75d83f20c17ec65991eac6b8dab15e046184e1f006
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-23 Bozhidar Batsov
1
+ Copyright (c) 2012-25 Bozhidar Batsov
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # RuboCop Performance
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/rubocop-performance.svg)](https://badge.fury.io/rb/rubocop-performance)
4
- [![CircleCI](https://circleci.com/gh/rubocop/rubocop-performance.svg?style=svg)](https://circleci.com/gh/rubocop/rubocop-performance)
4
+ [![CI](https://github.com/rubocop/rubocop-performance/actions/workflows/test.yml/badge.svg)](https://github.com/rubocop/rubocop-performance/actions/workflows/test.yml)
5
5
  [![Discord](https://img.shields.io/badge/chat-on%20discord-7289da.svg?sanitize=true)](https://discord.gg/wJjWvGRDmm)
6
6
 
7
7
  Performance optimization analysis for your projects, as an extension to [RuboCop](https://github.com/rubocop/rubocop).
@@ -53,6 +53,8 @@ $ rubocop --require rubocop-performance
53
53
  ### Rake task
54
54
 
55
55
  ```ruby
56
+ require 'rubocop/rake_task'
57
+
56
58
  RuboCop::RakeTask.new do |task|
57
59
  task.requires << 'rubocop-performance'
58
60
  end
data/config/default.yml CHANGED
@@ -6,7 +6,7 @@ Performance:
6
6
 
7
7
  Performance/AncestorsInclude:
8
8
  Description: 'Use `A <= B` instead of `A.ancestors.include?(B)`.'
9
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#ancestorsinclude-vs--code'
9
+ Reference: 'https://github.com/fastruby/fast-ruby#ancestorsinclude-vs--code'
10
10
  Enabled: 'pending'
11
11
  Safe: false
12
12
  VersionAdded: '1.7'
@@ -32,8 +32,11 @@ Performance/BindCall:
32
32
 
33
33
  Performance/BlockGivenWithExplicitBlock:
34
34
  Description: 'Check block argument explicitly instead of using `block_given?`.'
35
- Enabled: pending
35
+ # This cop was created due to a mistake in microbenchmark.
36
+ # https://github.com/rubocop/rubocop-performance/issues/385
37
+ Enabled: false
36
38
  VersionAdded: '1.9'
39
+ VersionChanged: '1.22'
37
40
 
38
41
  Performance/Caller:
39
42
  Description: >-
@@ -54,10 +57,11 @@ Performance/CaseWhenSplat:
54
57
  Performance/Casecmp:
55
58
  Description: >-
56
59
  Use `casecmp` rather than `downcase ==`, `upcase ==`, `== downcase`, or `== upcase`..
57
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringcasecmp-vs-stringdowncase---code'
58
- Enabled: true
60
+ Reference: 'https://github.com/fastruby/fast-ruby#stringcasecmp-vs--stringcasecmp-vs-stringdowncase---code'
61
+ Enabled: false
59
62
  Safe: false
60
63
  VersionAdded: '0.36'
64
+ VersionChanged: '1.21'
61
65
 
62
66
  Performance/ChainArrayAllocation:
63
67
  Description: >-
@@ -122,7 +126,7 @@ Performance/Detect:
122
126
  Description: >-
123
127
  Use `detect` instead of `select.first`, `find_all.first`, `filter.first`,
124
128
  `select.last`, `find_all.last`, and `filter.last`.
125
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code'
129
+ Reference: 'https://github.com/fastruby/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code'
126
130
  # This cop has known compatibility issues with `ActiveRecord` and other
127
131
  # frameworks. `ActiveRecord` does not implement a `detect` method and `find`
128
132
  # has its own meaning. Correcting `ActiveRecord` methods with this cop
@@ -145,7 +149,7 @@ Performance/DoubleStartEndWith:
145
149
 
146
150
  Performance/EndWith:
147
151
  Description: 'Use `end_with?` instead of a regex match anchored to the end of a string.'
148
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end'
152
+ Reference: 'https://github.com/fastruby/fast-ruby#stringmatch-vs-stringmatch-vs-stringstart_withstringend_with-code-start-code-end'
149
153
  # This will change to a new method call which isn't guaranteed to be on the
150
154
  # object. Switching these methods has to be done with knowledge of the types
151
155
  # of the variables which rubocop doesn't have.
@@ -165,7 +169,7 @@ Performance/FlatMap:
165
169
  Use `Enumerable#flat_map`
166
170
  instead of `Enumerable#map...Array#flatten(1)`
167
171
  or `Enumerable#collect..Array#flatten(1)`.
168
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code'
172
+ Reference: 'https://github.com/fastruby/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code'
169
173
  Enabled: true
170
174
  VersionAdded: '0.30'
171
175
  EnabledForFlattenWithoutParams: false
@@ -176,7 +180,7 @@ Performance/FlatMap:
176
180
 
177
181
  Performance/InefficientHashSearch:
178
182
  Description: 'Use `key?` or `value?` instead of `keys.include?` or `values.include?`.'
179
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#hashkey-instead-of-hashkeysinclude-code'
183
+ Reference: 'https://github.com/fastruby/fast-ruby#hashkey-instead-of-hashkeysinclude-code'
180
184
  Enabled: true
181
185
  VersionAdded: '0.56'
182
186
  Safe: false
@@ -193,9 +197,15 @@ Performance/MapCompact:
193
197
  SafeAutoCorrect: false
194
198
  VersionAdded: '1.11'
195
199
 
200
+ Performance/MapMethodChain:
201
+ Description: 'Checks if the `map` method is used in a chain.'
202
+ Enabled: pending
203
+ Safe: false
204
+ VersionAdded: '1.19'
205
+
196
206
  Performance/MethodObjectAsBlock:
197
207
  Description: 'Use block explicitly instead of block-passing a method object.'
198
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#normal-way-to-apply-method-vs-method-code'
208
+ Reference: 'https://github.com/fastruby/fast-ruby#normal-way-to-apply-method-vs-method-code'
199
209
  Enabled: pending
200
210
  VersionAdded: '1.9'
201
211
 
@@ -207,7 +217,7 @@ Performance/OpenStruct:
207
217
 
208
218
  Performance/RangeInclude:
209
219
  Description: 'Use `Range#cover?` instead of `Range#include?` (or `Range#member?`).'
210
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#cover-vs-include-code'
220
+ Reference: 'https://github.com/fastruby/fast-ruby#cover-vs-include-code'
211
221
  Enabled: true
212
222
  VersionAdded: '0.36'
213
223
  VersionChanged: '1.7'
@@ -215,7 +225,7 @@ Performance/RangeInclude:
215
225
 
216
226
  Performance/RedundantBlockCall:
217
227
  Description: 'Use `yield` instead of `block.call`.'
218
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#proccall-and-block-arguments-vs-yieldcode'
228
+ Reference: 'https://github.com/fastruby/fast-ruby#proccall-and-block-arguments-vs-yieldcode'
219
229
  Enabled: true
220
230
  VersionAdded: '0.36'
221
231
 
@@ -238,7 +248,7 @@ Performance/RedundantMatch:
238
248
 
239
249
  Performance/RedundantMerge:
240
250
  Description: 'Use Hash#[]=, rather than Hash#merge! with a single key-value pair.'
241
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#hashmerge-vs-hash-code'
251
+ Reference: 'https://github.com/fastruby/fast-ruby#hashmerge-vs-hash-code'
242
252
  Enabled: true
243
253
  Safe: false
244
254
  VersionAdded: '0.36'
@@ -265,13 +275,13 @@ Performance/RegexpMatch:
265
275
  Description: >-
266
276
  Use `match?` instead of `Regexp#match`, `String#match`, `Symbol#match`,
267
277
  `Regexp#===`, or `=~` when `MatchData` is not used.
268
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#regexp-vs-stringmatch-vs-string-vs-stringmatch-code-'
278
+ Reference: 'https://github.com/fastruby/fast-ruby#regexp-vs-regexpmatch-vs-regexpmatch-vs-stringmatch-vs-string-vs-stringmatch-code-'
269
279
  Enabled: true
270
280
  VersionAdded: '0.47'
271
281
 
272
282
  Performance/ReverseEach:
273
283
  Description: 'Use `reverse_each` instead of `reverse.each`.'
274
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code'
284
+ Reference: 'https://github.com/fastruby/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code'
275
285
  Enabled: true
276
286
  VersionAdded: '0.30'
277
287
 
@@ -289,7 +299,7 @@ Performance/Size:
289
299
  Description: >-
290
300
  Use `size` instead of `count` for counting
291
301
  the number of elements in `Array` and `Hash`.
292
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#arraylength-vs-arraysize-vs-arraycount-code'
302
+ Reference: 'https://github.com/fastruby/fast-ruby#arraylength-vs-arraysize-vs-arraycount-code'
293
303
  Enabled: true
294
304
  VersionAdded: '0.30'
295
305
 
@@ -300,13 +310,13 @@ Performance/SortReverse:
300
310
 
301
311
  Performance/Squeeze:
302
312
  Description: "Use `squeeze('a')` instead of `gsub(/a+/, 'a')`."
303
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#remove-extra-spaces-or-other-contiguous-characters-code'
313
+ Reference: 'https://github.com/fastruby/fast-ruby#remove-extra-spaces-or-other-contiguous-characters-code'
304
314
  Enabled: 'pending'
305
315
  VersionAdded: '1.7'
306
316
 
307
317
  Performance/StartWith:
308
318
  Description: 'Use `start_with?` instead of a regex match anchored to the beginning of a string.'
309
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end'
319
+ Reference: 'https://github.com/fastruby/fast-ruby#stringmatch-vs-stringmatch-vs-stringstart_withstringend_with-code-start-code-end'
310
320
  # This will change to a new method call which isn't guaranteed to be on the
311
321
  # object. Switching these methods has to be done with knowledge of the types
312
322
  # of the variables which rubocop doesn't have.
@@ -316,6 +326,12 @@ Performance/StartWith:
316
326
  VersionAdded: '0.36'
317
327
  VersionChanged: '1.10'
318
328
 
329
+ Performance/StringBytesize:
330
+ Description: "Use `String#bytesize` instead of calculating the size of the bytes array."
331
+ Safe: false
332
+ Enabled: 'pending'
333
+ VersionAdded: '1.23'
334
+
319
335
  Performance/StringIdentifierArgument:
320
336
  Description: 'Use symbol identifier argument instead of string identifier argument.'
321
337
  Enabled: pending
@@ -333,7 +349,7 @@ Performance/StringReplacement:
333
349
  Use `tr` instead of `gsub` when you are replacing the same
334
350
  number of characters. Use `delete` instead of `gsub` when
335
351
  you are deleting characters.
336
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringgsub-vs-stringtr-code'
352
+ Reference: 'https://github.com/fastruby/fast-ruby#stringgsub-vs-stringtr-code'
337
353
  Enabled: true
338
354
  VersionAdded: '0.33'
339
355
 
@@ -9,14 +9,14 @@ module RuboCop
9
9
 
10
10
  def_node_matcher :sort_with_block?, <<~PATTERN
11
11
  (block
12
- $(send _ :sort)
12
+ $(call _ :sort)
13
13
  (args (arg $_a) (arg $_b))
14
14
  $send)
15
15
  PATTERN
16
16
 
17
17
  def_node_matcher :sort_with_numblock?, <<~PATTERN
18
18
  (numblock
19
- $(send _ :sort)
19
+ $(call _ :sort)
20
20
  $_arg_count
21
21
  $send)
22
22
  PATTERN
@@ -39,7 +39,7 @@ module RuboCop
39
39
  RESTRICT_ON_SEND = SLICE_METHODS
40
40
 
41
41
  def_node_matcher :endless_range_slice?, <<~PATTERN
42
- (send $_ $%SLICE_METHODS $#endless_range?)
42
+ (call $!{str dstr xstr} $%SLICE_METHODS $#endless_range?)
43
43
  PATTERN
44
44
 
45
45
  def_node_matcher :endless_range?, <<~PATTERN
@@ -59,6 +59,7 @@ module RuboCop
59
59
  end
60
60
  end
61
61
  end
62
+ alias on_csend on_send
62
63
 
63
64
  private
64
65
 
@@ -3,50 +3,74 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # Identifies places where numeric argument to BigDecimal should be
7
- # converted to string. Initializing from String is faster
8
- # than from Numeric for BigDecimal.
6
+ # Identifies places where a float argument to BigDecimal should be converted to a string.
7
+ # Initializing from String is faster than from Float for BigDecimal.
8
+ #
9
+ # Also identifies places where an integer string argument to BigDecimal should be converted to
10
+ # an integer. Initializing from Integer is faster than from String for BigDecimal.
9
11
  #
10
12
  # @example
11
13
  # # bad
12
- # BigDecimal(1, 2)
13
- # 4.to_d(6)
14
14
  # BigDecimal(1.2, 3, exception: true)
15
15
  # 4.5.to_d(6, exception: true)
16
16
  #
17
17
  # # good
18
- # BigDecimal('1', 2)
19
- # BigDecimal('4', 6)
20
18
  # BigDecimal('1.2', 3, exception: true)
21
19
  # BigDecimal('4.5', 6, exception: true)
22
20
  #
21
+ # # bad
22
+ # BigDecimal('1', 2)
23
+ # BigDecimal('4', 6)
24
+ #
25
+ # # good
26
+ # BigDecimal(1, 2)
27
+ # 4.to_d(6)
28
+ #
23
29
  class BigDecimalWithNumericArgument < Base
24
30
  extend AutoCorrector
31
+ extend TargetRubyVersion
32
+
33
+ minimum_target_ruby_version 3.1
25
34
 
26
- MSG = 'Convert numeric literal to string and pass it to `BigDecimal`.'
35
+ MSG_FROM_FLOAT_TO_STRING = 'Convert float literal to string and pass it to `BigDecimal`.'
36
+ MSG_FROM_INTEGER_TO_STRING = 'Convert string literal to integer and pass it to `BigDecimal`.'
27
37
  RESTRICT_ON_SEND = %i[BigDecimal to_d].freeze
28
38
 
29
- def_node_matcher :big_decimal_with_numeric_argument?, <<~PATTERN
30
- (send nil? :BigDecimal $numeric_type? ...)
39
+ def_node_matcher :big_decimal_with_numeric_argument, <<~PATTERN
40
+ (send nil? :BigDecimal ${float_type? str_type?} ...)
31
41
  PATTERN
32
42
 
33
- def_node_matcher :to_d?, <<~PATTERN
34
- (send [!nil? $numeric_type?] :to_d ...)
43
+ def_node_matcher :to_d, <<~PATTERN
44
+ (send [!nil? ${float_type? str_type?}] :to_d ...)
35
45
  PATTERN
36
46
 
47
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
37
48
  def on_send(node)
38
- if (numeric = big_decimal_with_numeric_argument?(node))
39
- add_offense(numeric.source_range) do |corrector|
40
- corrector.wrap(numeric, "'", "'")
49
+ if (numeric = big_decimal_with_numeric_argument(node))
50
+ if numeric.numeric_type?
51
+ add_offense(numeric, message: MSG_FROM_FLOAT_TO_STRING) do |corrector|
52
+ corrector.wrap(numeric, "'", "'")
53
+ end
54
+ elsif numeric.value.match?(/\A\d+\z/)
55
+ add_offense(numeric, message: MSG_FROM_INTEGER_TO_STRING) do |corrector|
56
+ corrector.replace(numeric, numeric.value)
57
+ end
41
58
  end
42
- elsif (numeric_to_d = to_d?(node))
43
- add_offense(numeric_to_d.source_range) do |corrector|
44
- big_decimal_args = node.arguments.map(&:source).unshift("'#{numeric_to_d.source}'").join(', ')
59
+ elsif (numeric_to_d = to_d(node))
60
+ if numeric_to_d.numeric_type?
61
+ add_offense(numeric_to_d, message: MSG_FROM_FLOAT_TO_STRING) do |corrector|
62
+ big_decimal_args = node.arguments.map(&:source).unshift("'#{numeric_to_d.source}'").join(', ')
45
63
 
46
- corrector.replace(node, "BigDecimal(#{big_decimal_args})")
64
+ corrector.replace(node, "BigDecimal(#{big_decimal_args})")
65
+ end
66
+ elsif numeric_to_d.value.match?(/\A\d+\z/)
67
+ add_offense(numeric_to_d, message: MSG_FROM_INTEGER_TO_STRING) do |corrector|
68
+ corrector.replace(node, "#{numeric_to_d.value}.to_d")
69
+ end
47
70
  end
48
71
  end
49
72
  end
73
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
50
74
  end
51
75
  end
52
76
  end
@@ -6,6 +6,9 @@ module RuboCop
6
6
  # Identifies unnecessary use of a `block_given?` where explicit check
7
7
  # of block argument would suffice.
8
8
  #
9
+ # NOTE: This cop produces code with significantly worse performance when a
10
+ # block is being passed to the method and as such should not be enabled.
11
+ #
9
12
  # @example
10
13
  # # bad
11
14
  # def method(&block)
@@ -47,6 +50,10 @@ module RuboCop
47
50
  corrector.replace(node, block_arg_name)
48
51
  end
49
52
  end
53
+
54
+ def self.autocorrect_incompatible_with
55
+ [Lint::UnusedMethodArgument]
56
+ end
50
57
  end
51
58
  end
52
59
  end
@@ -6,6 +6,12 @@ module RuboCop
6
6
  # Identifies places where a case-insensitive string comparison
7
7
  # can better be implemented using `casecmp`.
8
8
  #
9
+ # This cop is disabled by default because `String#casecmp` only works with
10
+ # ASCII characters. See https://github.com/rubocop/rubocop/issues/9753.
11
+ #
12
+ # If you are working only with ASCII characters, then this cop can be
13
+ # safely enabled.
14
+ #
9
15
  # @safety
10
16
  # This cop is unsafe because `String#casecmp` and `String#casecmp?` behave
11
17
  # differently when using Non-ASCII characters.
@@ -19,8 +19,6 @@ module RuboCop
19
19
  # array.map! { |x| x.downcase }
20
20
  # array
21
21
  class ChainArrayAllocation < Base
22
- include RangeHelp
23
-
24
22
  # These methods return a new array but only sometimes. They must be
25
23
  # called with an argument. For example:
26
24
  #
@@ -54,7 +52,7 @@ module RuboCop
54
52
  def_node_matcher :chain_array_allocation?, <<~PATTERN
55
53
  (send {
56
54
  (send _ $%RETURN_NEW_ARRAY_WHEN_ARGS {int lvar ivar cvar gvar send})
57
- (block (send _ $%ALWAYS_RETURNS_NEW_ARRAY) ...)
55
+ ({block numblock} (send _ $%ALWAYS_RETURNS_NEW_ARRAY) ...)
58
56
  (send _ $%RETURNS_NEW_ARRAY ...)
59
57
  } $%HAS_MUTATION_ALTERNATIVE ...)
60
58
  PATTERN
@@ -62,12 +60,21 @@ module RuboCop
62
60
  def on_send(node)
63
61
  chain_array_allocation?(node) do |fm, sm|
64
62
  return if node.each_descendant(:send).any? { |descendant| descendant.method?(:lazy) }
63
+ return if node.method?(:select) && !enumerable_select_method?(node.receiver)
65
64
 
66
- range = range_between(node.loc.dot.begin_pos, node.source_range.end_pos)
65
+ range = node.loc.selector.begin.join(node.source_range.end)
67
66
 
68
67
  add_offense(range, message: format(MSG, method: fm, second_method: sm))
69
68
  end
70
69
  end
70
+
71
+ private
72
+
73
+ def enumerable_select_method?(node)
74
+ # NOTE: `QueryMethods#select` in Rails accepts positional arguments, whereas `Enumerable#select` does not.
75
+ # This difference can be utilized to reduce the knowledge requirements related to `select`.
76
+ (node.block_type? || node.numblock_type?) && node.send_node.arguments.empty?
77
+ end
71
78
  end
72
79
  end
73
80
  end
@@ -9,8 +9,7 @@ module RuboCop
9
9
  #
10
10
  # @safety
11
11
  # This cop is unsafe because it has known compatibility issues with `ActiveRecord` and other
12
- # frameworks. ActiveRecord's `count` ignores the block that is passed to it.
13
- # `ActiveRecord` will ignore the block that is passed to `count`.
12
+ # frameworks. Before Rails 5.1, `ActiveRecord` will ignore the block that is passed to `count`.
14
13
  # Other methods, such as `select`, will convert the association to an
15
14
  # array and then run the block on the array. A simple work around to
16
15
  # make `count` work with a block is to call `to_a.count {...}`.
@@ -19,14 +18,14 @@ module RuboCop
19
18
  #
20
19
  # [source,ruby]
21
20
  # ----
22
- # `Model.where(id: [1, 2, 3]).select { |m| m.method == true }.size`
21
+ # Model.where(id: [1, 2, 3]).select { |m| m.method == true }.size
23
22
  # ----
24
23
  #
25
24
  # becomes:
26
25
  #
27
26
  # [source,ruby]
28
27
  # ----
29
- # `Model.where(id: [1, 2, 3]).to_a.count { |m| m.method == true }`
28
+ # Model.where(id: [1, 2, 3]).to_a.count { |m| m.method == true }
30
29
  # ----
31
30
  #
32
31
  # @example
@@ -55,8 +54,8 @@ module RuboCop
55
54
 
56
55
  def_node_matcher :count_candidate?, <<~PATTERN
57
56
  {
58
- (send (block $(send _ ${:select :filter :find_all :reject}) ...) ${:count :length :size})
59
- (send $(send _ ${:select :filter :find_all :reject} (:block_pass _)) ${:count :length :size})
57
+ (call (block $(call _ ${:select :filter :find_all :reject}) ...) ${:count :length :size})
58
+ (call $(call _ ${:select :filter :find_all :reject} (:block_pass _)) ${:count :length :size})
60
59
  }
61
60
  PATTERN
62
61
 
@@ -73,6 +72,7 @@ module RuboCop
73
72
  end
74
73
  end
75
74
  end
75
+ alias on_csend on_send
76
76
 
77
77
  private
78
78
 
@@ -101,7 +101,7 @@ module RuboCop
101
101
  end
102
102
 
103
103
  def negate_reject(corrector, node)
104
- if node.receiver.send_type?
104
+ if node.receiver.call_type?
105
105
  negate_block_pass_reject(corrector, node)
106
106
  else
107
107
  negate_block_reject(corrector, node)
@@ -64,9 +64,10 @@ module RuboCop
64
64
  }.freeze
65
65
 
66
66
  def_node_matcher :delete_prefix_candidate?, <<~PATTERN
67
- (send $!nil? ${:gsub :gsub! :sub :sub!} (regexp (str $#literal_at_start?) (regopt)) (str $_))
67
+ (call $!nil? ${:gsub :gsub! :sub :sub!} (regexp (str $#literal_at_start?) (regopt)) (str $_))
68
68
  PATTERN
69
69
 
70
+ # rubocop:disable Metrics/AbcSize
70
71
  def on_send(node)
71
72
  return unless (receiver, bad_method, regexp_str, replace_string = delete_prefix_candidate?(node))
72
73
  return unless replace_string.empty?
@@ -80,11 +81,13 @@ module RuboCop
80
81
  regexp_str = interpret_string_escapes(regexp_str)
81
82
  string_literal = to_string_literal(regexp_str)
82
83
 
83
- new_code = "#{receiver.source}.#{good_method}(#{string_literal})"
84
+ new_code = "#{receiver.source}#{node.loc.dot.source}#{good_method}(#{string_literal})"
84
85
 
85
86
  corrector.replace(node, new_code)
86
87
  end
87
88
  end
89
+ # rubocop:enable Metrics/AbcSize
90
+ alias on_csend on_send
88
91
  end
89
92
  end
90
93
  end
@@ -64,9 +64,10 @@ module RuboCop
64
64
  }.freeze
65
65
 
66
66
  def_node_matcher :delete_suffix_candidate?, <<~PATTERN
67
- (send $!nil? ${:gsub :gsub! :sub :sub!} (regexp (str $#literal_at_end?) (regopt)) (str $_))
67
+ (call $!nil? ${:gsub :gsub! :sub :sub!} (regexp (str $#literal_at_end?) (regopt)) (str $_))
68
68
  PATTERN
69
69
 
70
+ # rubocop:disable Metrics/AbcSize
70
71
  def on_send(node)
71
72
  return unless (receiver, bad_method, regexp_str, replace_string = delete_suffix_candidate?(node))
72
73
  return unless replace_string.empty?
@@ -80,11 +81,13 @@ module RuboCop
80
81
  regexp_str = interpret_string_escapes(regexp_str)
81
82
  string_literal = to_string_literal(regexp_str)
82
83
 
83
- new_code = "#{receiver.source}.#{good_method}(#{string_literal})"
84
+ new_code = "#{receiver.source}#{node.loc.dot.source}#{good_method}(#{string_literal})"
84
85
 
85
86
  corrector.replace(node, new_code)
86
87
  end
87
88
  end
89
+ # rubocop:enable Metrics/AbcSize
90
+ alias on_csend on_send
88
91
  end
89
92
  end
90
93
  end
@@ -40,9 +40,9 @@ module RuboCop
40
40
 
41
41
  def_node_matcher :detect_candidate?, <<~PATTERN
42
42
  {
43
- (send $(block (send _ %CANDIDATE_METHODS) ...) ${:first :last} $...)
43
+ (send $(block (call _ %CANDIDATE_METHODS) ...) ${:first :last} $...)
44
44
  (send $(block (send _ %CANDIDATE_METHODS) ...) $:[] (int ${0 -1}))
45
- (send $(send _ %CANDIDATE_METHODS ...) ${:first :last} $...)
45
+ (send $(call _ %CANDIDATE_METHODS ...) ${:first :last} $...)
46
46
  (send $(send _ %CANDIDATE_METHODS ...) $:[] (int ${0 -1}))
47
47
  }
48
48
  PATTERN
@@ -63,6 +63,7 @@ module RuboCop
63
63
  register_offense(node, receiver, second_method, index)
64
64
  end
65
65
  end
66
+ alias on_csend on_send
66
67
 
67
68
  private
68
69
 
@@ -41,7 +41,7 @@ module RuboCop
41
41
  class DoubleStartEndWith < Base
42
42
  extend AutoCorrector
43
43
 
44
- MSG = 'Use `%<receiver>s.%<method>s(%<combined_args>s)` instead of `%<original_code>s`.'
44
+ MSG = 'Use `%<replacement>s` instead of `%<original_code>s`.'
45
45
 
46
46
  def on_or(node)
47
47
  receiver, method, first_call_args, second_call_args = process_source(node)
@@ -50,7 +50,7 @@ module RuboCop
50
50
 
51
51
  combined_args = combine_args(first_call_args, second_call_args)
52
52
 
53
- add_offense(node, message: message(node, receiver, method, combined_args)) do |corrector|
53
+ add_offense(node, message: message(node, receiver, first_call_args, method, combined_args)) do |corrector|
54
54
  autocorrect(corrector, first_call_args, second_call_args, combined_args)
55
55
  end
56
56
  end
@@ -73,10 +73,10 @@ module RuboCop
73
73
  end
74
74
  end
75
75
 
76
- def message(node, receiver, method, combined_args)
77
- format(
78
- MSG, receiver: receiver.source, method: method, combined_args: combined_args, original_code: node.source
79
- )
76
+ def message(node, receiver, first_call_args, method, combined_args)
77
+ dot = first_call_args.first.parent.send_type? ? '.' : '&.'
78
+ replacement = "#{receiver.source}#{dot}#{method}(#{combined_args})"
79
+ format(MSG, replacement: replacement, original_code: node.source)
80
80
  end
81
81
 
82
82
  def combine_args(first_call_args, second_call_args)
@@ -89,16 +89,16 @@ module RuboCop
89
89
 
90
90
  def_node_matcher :two_start_end_with_calls, <<~PATTERN
91
91
  (or
92
- (send $_recv [{:start_with? :end_with?} $_method] $...)
93
- (send _recv _method $...))
92
+ (call $_recv [{:start_with? :end_with?} $_method] $...)
93
+ (call _recv _method $...))
94
94
  PATTERN
95
95
 
96
96
  def_node_matcher :check_with_active_support_aliases, <<~PATTERN
97
97
  (or
98
- (send $_recv
98
+ (call $_recv
99
99
  [{:start_with? :starts_with? :end_with? :ends_with?} $_method]
100
100
  $...)
101
- (send _recv _method $...))
101
+ (call _recv _method $...))
102
102
  PATTERN
103
103
  end
104
104
  end
@@ -54,9 +54,10 @@ module RuboCop
54
54
  RESTRICT_ON_SEND = %i[match =~ match?].freeze
55
55
 
56
56
  def_node_matcher :redundant_regex?, <<~PATTERN
57
- {(send $!nil? {:match :=~ :match?} (regexp (str $#literal_at_end?) (regopt)))
57
+ {(call $!nil? {:match :=~ :match?} (regexp (str $#literal_at_end?) (regopt)))
58
58
  (send (regexp (str $#literal_at_end?) (regopt)) {:match :match?} $_)
59
- (match-with-lvasgn (regexp (str $#literal_at_end?) (regopt)) $_)}
59
+ ({send match-with-lvasgn} (regexp (str $#literal_at_end?) (regopt)) $_)
60
+ (send (regexp (str $#literal_at_end?) (regopt)) :=~ $_)}
60
61
  PATTERN
61
62
 
62
63
  def on_send(node)
@@ -66,12 +67,14 @@ module RuboCop
66
67
  receiver, regex_str = regex_str, receiver if receiver.is_a?(String)
67
68
  regex_str = drop_end_metacharacter(regex_str)
68
69
  regex_str = interpret_string_escapes(regex_str)
70
+ dot = node.loc.dot ? node.loc.dot.source : '.'
69
71
 
70
- new_source = "#{receiver.source}.end_with?(#{to_string_literal(regex_str)})"
72
+ new_source = "#{receiver.source}#{dot}end_with?(#{to_string_literal(regex_str)})"
71
73
 
72
74
  corrector.replace(node, new_source)
73
75
  end
74
76
  end
77
+ alias on_csend on_send
75
78
  alias on_match_with_lvasgn on_send
76
79
  end
77
80
  end