rubocop-performance 1.19.1 → 1.21.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 (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/config/default.yml +18 -17
  4. data/lib/rubocop/cop/mixin/sort_block.rb +2 -2
  5. data/lib/rubocop/cop/performance/casecmp.rb +6 -0
  6. data/lib/rubocop/cop/performance/chain_array_allocation.rb +11 -4
  7. data/lib/rubocop/cop/performance/count.rb +5 -5
  8. data/lib/rubocop/cop/performance/end_with.rb +2 -1
  9. data/lib/rubocop/cop/performance/fixed_size.rb +2 -1
  10. data/lib/rubocop/cop/performance/flat_map.rb +4 -3
  11. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +2 -2
  12. data/lib/rubocop/cop/performance/map_compact.rb +5 -4
  13. data/lib/rubocop/cop/performance/map_method_chain.rb +3 -1
  14. data/lib/rubocop/cop/performance/range_include.rb +2 -1
  15. data/lib/rubocop/cop/performance/redundant_block_call.rb +2 -0
  16. data/lib/rubocop/cop/performance/redundant_equality_comparison_block.rb +2 -2
  17. data/lib/rubocop/cop/performance/redundant_merge.rb +3 -1
  18. data/lib/rubocop/cop/performance/regexp_match.rb +1 -8
  19. data/lib/rubocop/cop/performance/reverse_each.rb +1 -1
  20. data/lib/rubocop/cop/performance/reverse_first.rb +5 -13
  21. data/lib/rubocop/cop/performance/select_map.rb +2 -6
  22. data/lib/rubocop/cop/performance/size.rb +4 -3
  23. data/lib/rubocop/cop/performance/sort_reverse.rb +5 -4
  24. data/lib/rubocop/cop/performance/start_with.rb +2 -1
  25. data/lib/rubocop/cop/performance/string_identifier_argument.rb +58 -10
  26. data/lib/rubocop/cop/performance/string_include.rb +5 -3
  27. data/lib/rubocop/cop/performance/sum.rb +11 -9
  28. data/lib/rubocop/cop/performance/times_map.rb +14 -2
  29. data/lib/rubocop/cop/performance/unfreeze_string.rb +3 -3
  30. data/lib/rubocop/performance/version.rb +1 -1
  31. metadata +14 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1fa48a119a4c26e989f4d73366c0cbac4bbe854453a702e49f0af9348b1cf798
4
- data.tar.gz: e63e391f5f6e2ab2500033cd83df591b9d48254e7f295ce69bbb352e5242c67a
3
+ metadata.gz: dd286ebd5cb0d982f759a355a86281898577db559004bef9bfb53b8493a32934
4
+ data.tar.gz: f3e101a14918c3efa6dc657841471b22afec47d54ce70442567d54ffc3ee8ed5
5
5
  SHA512:
6
- metadata.gz: 98939001b2b61212c3b4bab44c8b9f9ae8bd906eeb873e2a9516b075f896679fbd513c23c874555982a14d390d265fe01451c218860249ff18376d89f1f59709
7
- data.tar.gz: ce73767335d2fe19418afa72ca4a8c5c15632960a2d77282b1042c002c65ec1d744af929fc3b612ba5f7c7e7a5cbaa35aab978fcc8a57e7dde02b114c8fc873b
6
+ metadata.gz: 1d6df8e36cbb224d892f14e7fb1eeccf34c887c9fd6e9ad302889ab71b64e48db3c3167291f210ad7e69a23f5e4720ae23ee767c4a569701d09e8fa771ede28c
7
+ data.tar.gz: 428da3a79195bcf8c715a1962c5bea38a08ee2973c0450b78fc24ceeb376a4f984b3d4f91efba93c799d28986dda22f711e90937738007abbd41b470a484a887
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'
@@ -54,10 +54,11 @@ Performance/CaseWhenSplat:
54
54
  Performance/Casecmp:
55
55
  Description: >-
56
56
  Use `casecmp` rather than `downcase ==`, `upcase ==`, `== downcase`, or `== upcase`..
57
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringcasecmp-vs-stringdowncase---code'
58
- Enabled: true
57
+ Reference: 'https://github.com/fastruby/fast-ruby#stringcasecmp-vs--stringcasecmp-vs-stringdowncase---code'
58
+ Enabled: false
59
59
  Safe: false
60
60
  VersionAdded: '0.36'
61
+ VersionChanged: '1.21'
61
62
 
62
63
  Performance/ChainArrayAllocation:
63
64
  Description: >-
@@ -122,7 +123,7 @@ Performance/Detect:
122
123
  Description: >-
123
124
  Use `detect` instead of `select.first`, `find_all.first`, `filter.first`,
124
125
  `select.last`, `find_all.last`, and `filter.last`.
125
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code'
126
+ Reference: 'https://github.com/fastruby/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code'
126
127
  # This cop has known compatibility issues with `ActiveRecord` and other
127
128
  # frameworks. `ActiveRecord` does not implement a `detect` method and `find`
128
129
  # has its own meaning. Correcting `ActiveRecord` methods with this cop
@@ -145,7 +146,7 @@ Performance/DoubleStartEndWith:
145
146
 
146
147
  Performance/EndWith:
147
148
  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'
149
+ Reference: 'https://github.com/fastruby/fast-ruby#stringmatch-vs-stringmatch-vs-stringstart_withstringend_with-code-start-code-end'
149
150
  # This will change to a new method call which isn't guaranteed to be on the
150
151
  # object. Switching these methods has to be done with knowledge of the types
151
152
  # of the variables which rubocop doesn't have.
@@ -165,7 +166,7 @@ Performance/FlatMap:
165
166
  Use `Enumerable#flat_map`
166
167
  instead of `Enumerable#map...Array#flatten(1)`
167
168
  or `Enumerable#collect..Array#flatten(1)`.
168
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code'
169
+ Reference: 'https://github.com/fastruby/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code'
169
170
  Enabled: true
170
171
  VersionAdded: '0.30'
171
172
  EnabledForFlattenWithoutParams: false
@@ -176,7 +177,7 @@ Performance/FlatMap:
176
177
 
177
178
  Performance/InefficientHashSearch:
178
179
  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'
180
+ Reference: 'https://github.com/fastruby/fast-ruby#hashkey-instead-of-hashkeysinclude-code'
180
181
  Enabled: true
181
182
  VersionAdded: '0.56'
182
183
  Safe: false
@@ -201,7 +202,7 @@ Performance/MapMethodChain:
201
202
 
202
203
  Performance/MethodObjectAsBlock:
203
204
  Description: 'Use block explicitly instead of block-passing a method object.'
204
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#normal-way-to-apply-method-vs-method-code'
205
+ Reference: 'https://github.com/fastruby/fast-ruby#normal-way-to-apply-method-vs-method-code'
205
206
  Enabled: pending
206
207
  VersionAdded: '1.9'
207
208
 
@@ -213,7 +214,7 @@ Performance/OpenStruct:
213
214
 
214
215
  Performance/RangeInclude:
215
216
  Description: 'Use `Range#cover?` instead of `Range#include?` (or `Range#member?`).'
216
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#cover-vs-include-code'
217
+ Reference: 'https://github.com/fastruby/fast-ruby#cover-vs-include-code'
217
218
  Enabled: true
218
219
  VersionAdded: '0.36'
219
220
  VersionChanged: '1.7'
@@ -221,7 +222,7 @@ Performance/RangeInclude:
221
222
 
222
223
  Performance/RedundantBlockCall:
223
224
  Description: 'Use `yield` instead of `block.call`.'
224
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#proccall-and-block-arguments-vs-yieldcode'
225
+ Reference: 'https://github.com/fastruby/fast-ruby#proccall-and-block-arguments-vs-yieldcode'
225
226
  Enabled: true
226
227
  VersionAdded: '0.36'
227
228
 
@@ -244,7 +245,7 @@ Performance/RedundantMatch:
244
245
 
245
246
  Performance/RedundantMerge:
246
247
  Description: 'Use Hash#[]=, rather than Hash#merge! with a single key-value pair.'
247
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#hashmerge-vs-hash-code'
248
+ Reference: 'https://github.com/fastruby/fast-ruby#hashmerge-vs-hash-code'
248
249
  Enabled: true
249
250
  Safe: false
250
251
  VersionAdded: '0.36'
@@ -271,13 +272,13 @@ Performance/RegexpMatch:
271
272
  Description: >-
272
273
  Use `match?` instead of `Regexp#match`, `String#match`, `Symbol#match`,
273
274
  `Regexp#===`, or `=~` when `MatchData` is not used.
274
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#regexp-vs-stringmatch-vs-string-vs-stringmatch-code-'
275
+ Reference: 'https://github.com/fastruby/fast-ruby#regexp-vs-regexpmatch-vs-regexpmatch-vs-stringmatch-vs-string-vs-stringmatch-code-'
275
276
  Enabled: true
276
277
  VersionAdded: '0.47'
277
278
 
278
279
  Performance/ReverseEach:
279
280
  Description: 'Use `reverse_each` instead of `reverse.each`.'
280
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code'
281
+ Reference: 'https://github.com/fastruby/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code'
281
282
  Enabled: true
282
283
  VersionAdded: '0.30'
283
284
 
@@ -295,7 +296,7 @@ Performance/Size:
295
296
  Description: >-
296
297
  Use `size` instead of `count` for counting
297
298
  the number of elements in `Array` and `Hash`.
298
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#arraylength-vs-arraysize-vs-arraycount-code'
299
+ Reference: 'https://github.com/fastruby/fast-ruby#arraylength-vs-arraysize-vs-arraycount-code'
299
300
  Enabled: true
300
301
  VersionAdded: '0.30'
301
302
 
@@ -306,13 +307,13 @@ Performance/SortReverse:
306
307
 
307
308
  Performance/Squeeze:
308
309
  Description: "Use `squeeze('a')` instead of `gsub(/a+/, 'a')`."
309
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#remove-extra-spaces-or-other-contiguous-characters-code'
310
+ Reference: 'https://github.com/fastruby/fast-ruby#remove-extra-spaces-or-other-contiguous-characters-code'
310
311
  Enabled: 'pending'
311
312
  VersionAdded: '1.7'
312
313
 
313
314
  Performance/StartWith:
314
315
  Description: 'Use `start_with?` instead of a regex match anchored to the beginning of a string.'
315
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end'
316
+ Reference: 'https://github.com/fastruby/fast-ruby#stringmatch-vs-stringmatch-vs-stringstart_withstringend_with-code-start-code-end'
316
317
  # This will change to a new method call which isn't guaranteed to be on the
317
318
  # object. Switching these methods has to be done with knowledge of the types
318
319
  # of the variables which rubocop doesn't have.
@@ -339,7 +340,7 @@ Performance/StringReplacement:
339
340
  Use `tr` instead of `gsub` when you are replacing the same
340
341
  number of characters. Use `delete` instead of `gsub` when
341
342
  you are deleting characters.
342
- Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringgsub-vs-stringtr-code'
343
+ Reference: 'https://github.com/fastruby/fast-ruby#stringgsub-vs-stringtr-code'
343
344
  Enabled: true
344
345
  VersionAdded: '0.33'
345
346
 
@@ -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
@@ -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 {...}`.
@@ -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)
@@ -56,7 +56,8 @@ module RuboCop
56
56
  def_node_matcher :redundant_regex?, <<~PATTERN
57
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)
@@ -50,7 +50,7 @@ module RuboCop
50
50
  RESTRICT_ON_SEND = %i[count length size].freeze
51
51
 
52
52
  def_node_matcher :counter, <<~MATCHER
53
- (send ${array hash str sym} {:count :length :size} $...)
53
+ (call ${array hash str sym} {:count :length :size} $...)
54
54
  MATCHER
55
55
 
56
56
  def on_send(node)
@@ -62,6 +62,7 @@ module RuboCop
62
62
  add_offense(node)
63
63
  end
64
64
  end
65
+ alias on_csend on_send
65
66
 
66
67
  private
67
68
 
@@ -26,10 +26,10 @@ module RuboCop
26
26
  'multiple levels.'
27
27
 
28
28
  def_node_matcher :flat_map_candidate?, <<~PATTERN
29
- (send
29
+ (call
30
30
  {
31
- $(block (send _ ${:collect :map}) ...)
32
- $(send _ ${:collect :map} (block_pass _))
31
+ $(block (call _ ${:collect :map}) ...)
32
+ $(call _ ${:collect :map} (block_pass _))
33
33
  }
34
34
  ${:flatten :flatten!}
35
35
  $...
@@ -46,6 +46,7 @@ module RuboCop
46
46
  end
47
47
  end
48
48
  end
49
+ alias on_csend on_send
49
50
 
50
51
  private
51
52
 
@@ -45,7 +45,7 @@ module RuboCop
45
45
  RESTRICT_ON_SEND = %i[include?].freeze
46
46
 
47
47
  def_node_matcher :inefficient_include?, <<~PATTERN
48
- (send (call $_ {:keys :values}) :include? _)
48
+ (call (call $_ {:keys :values}) :include? _)
49
49
  PATTERN
50
50
 
51
51
  def on_send(node)
@@ -89,7 +89,7 @@ module RuboCop
89
89
  end
90
90
 
91
91
  def correct_argument(node)
92
- node.arguments.first.source
92
+ node.first_argument.source
93
93
  end
94
94
 
95
95
  def correct_hash_expression(node)
@@ -8,8 +8,9 @@ module RuboCop
8
8
  # This cop identifies places where `map { ... }.compact` can be replaced by `filter_map`.
9
9
  #
10
10
  # @safety
11
- # This cop's autocorrection is unsafe because `map { ... }.compact` that is not
12
- # compatible with `filter_map`.
11
+ # This cop's autocorrection is unsafe because `map { ... }.compact` might yield
12
+ # different results than `filter_map`. As illustrated in the example, `filter_map`
13
+ # also filters out falsy values, while `compact` only gets rid of `nil`.
13
14
  #
14
15
  # [source,ruby]
15
16
  # ----
@@ -39,11 +40,11 @@ module RuboCop
39
40
 
40
41
  def_node_matcher :map_compact, <<~PATTERN
41
42
  {
42
- (send
43
+ (call
43
44
  $(call _ {:map :collect}
44
45
  (block_pass
45
46
  (sym _))) _)
46
- (send
47
+ (call
47
48
  (block
48
49
  $(call _ {:map :collect})
49
50
  (args ...) _) _)
@@ -68,6 +68,7 @@ module RuboCop
68
68
 
69
69
  private
70
70
 
71
+ # rubocop:disable Metrics/CyclomaticComplexity
71
72
  def find_begin_of_chained_map_method(node, map_args)
72
73
  return unless (chained_map_method = node.receiver)
73
74
  return if !chained_map_method.call_type? || !RESTRICT_ON_SEND.include?(chained_map_method.method_name)
@@ -77,10 +78,11 @@ module RuboCop
77
78
 
78
79
  receiver = chained_map_method.receiver
79
80
 
80
- return chained_map_method unless receiver.call_type? && block_pass_with_symbol_arg?(receiver.first_argument)
81
+ return chained_map_method unless receiver&.call_type? && block_pass_with_symbol_arg?(receiver.first_argument)
81
82
 
82
83
  find_begin_of_chained_map_method(chained_map_method, map_args)
83
84
  end
85
+ # rubocop:enable Metrics/CyclomaticComplexity
84
86
  end
85
87
  end
86
88
  end
@@ -38,7 +38,7 @@ module RuboCop
38
38
  # (We don't even catch it if the Range is in double parens)
39
39
 
40
40
  def_node_matcher :range_include, <<~PATTERN
41
- (send {irange erange (begin {irange erange})} ${:include? :member?} ...)
41
+ (call {irange erange (begin {irange erange})} ${:include? :member?} ...)
42
42
  PATTERN
43
43
 
44
44
  def on_send(node)
@@ -50,6 +50,7 @@ module RuboCop
50
50
  end
51
51
  end
52
52
  end
53
+ alias on_csend on_send
53
54
  end
54
55
  end
55
56
  end
@@ -49,6 +49,8 @@ module RuboCop
49
49
  next unless body
50
50
 
51
51
  calls_to_report(argname, body).each do |blockcall|
52
+ next if blockcall.block_literal?
53
+
52
54
  add_offense(blockcall, message: format(MSG, argname: argname)) do |corrector|
53
55
  autocorrect(corrector, blockcall)
54
56
  end
@@ -63,8 +63,8 @@ module RuboCop
63
63
  return unless TARGET_METHODS.include?(node.method_name)
64
64
  return unless one_block_argument?(node.arguments)
65
65
 
66
- block_argument = node.arguments.first
67
- block_body = node.body
66
+ block_argument = node.first_argument
67
+ return unless (block_body = node.body)
68
68
  return unless use_equality_comparison_block?(block_body)
69
69
  return if same_block_argument_and_is_a_argument?(block_body, block_argument)
70
70
  return unless (new_argument = new_argument(block_argument, block_body))
@@ -130,7 +130,9 @@ module RuboCop
130
130
  end
131
131
 
132
132
  def rewrite_with_modifier(node, parent, new_source)
133
- indent = ' ' * configured_indentation_width
133
+ # FIXME: `|| 2` can be removed when support is limited to RuboCop 1.44 or higher.
134
+ # https://github.com/rubocop/rubocop/commit/02d1e5b
135
+ indent = ' ' * (configured_indentation_width || 2)
134
136
  padding = "\n#{indent + leading_spaces(node)}"
135
137
  new_source.gsub!("\n", padding)
136
138
 
@@ -248,7 +248,7 @@ module RuboCop
248
248
  end
249
249
 
250
250
  def correct_operator(corrector, recv, arg, oper = nil)
251
- op_range = correction_range(recv, arg)
251
+ op_range = recv.source_range.end.join(arg.source_range.begin)
252
252
 
253
253
  replace_with_match_predicate_method(corrector, recv, arg, op_range)
254
254
 
@@ -271,13 +271,6 @@ module RuboCop
271
271
  corrector.replace(recv, arg.source)
272
272
  corrector.replace(arg, recv.source)
273
273
  end
274
-
275
- def correction_range(recv, arg)
276
- buffer = processed_source.buffer
277
- op_begin_pos = recv.source_range.end_pos
278
- op_end_pos = arg.source_range.begin_pos
279
- Parser::Source::Range.new(buffer, op_begin_pos, op_end_pos)
280
- end
281
274
  end
282
275
  end
283
276
  end
@@ -27,7 +27,7 @@ module RuboCop
27
27
  RESTRICT_ON_SEND = %i[each].freeze
28
28
 
29
29
  def_node_matcher :reverse_each?, <<~MATCHER
30
- (send (call _ :reverse) :each)
30
+ (call (call _ :reverse) :each)
31
31
  MATCHER
32
32
 
33
33
  def on_send(node)
@@ -24,13 +24,13 @@ module RuboCop
24
24
  RESTRICT_ON_SEND = %i[first].freeze
25
25
 
26
26
  def_node_matcher :reverse_first_candidate?, <<~PATTERN
27
- (send $(call _ :reverse) :first (int _)?)
27
+ (call $(call _ :reverse) :first (int _)?)
28
28
  PATTERN
29
29
 
30
30
  def on_send(node)
31
31
  reverse_first_candidate?(node) do |receiver|
32
32
  range = correction_range(receiver, node)
33
- message = build_message(node)
33
+ message = build_message(node, range)
34
34
 
35
35
  add_offense(range, message: message) do |corrector|
36
36
  replacement = build_good_method(node)
@@ -47,27 +47,19 @@ module RuboCop
47
47
  range_between(receiver.loc.selector.begin_pos, node.source_range.end_pos)
48
48
  end
49
49
 
50
- def build_message(node)
50
+ def build_message(node, range)
51
51
  good_method = build_good_method(node)
52
- bad_method = build_bad_method(node)
52
+ bad_method = range.source
53
53
  format(MSG, good_method: good_method, bad_method: bad_method)
54
54
  end
55
55
 
56
56
  def build_good_method(node)
57
57
  if node.arguments?
58
- "last(#{node.arguments.first.source}).reverse"
58
+ "last(#{node.first_argument.source})#{node.loc.dot.source}reverse"
59
59
  else
60
60
  'last'
61
61
  end
62
62
  end
63
-
64
- def build_bad_method(node)
65
- if node.arguments?
66
- "reverse.first(#{node.arguments.first.source})"
67
- else
68
- 'reverse.first'
69
- end
70
- end
71
63
  end
72
64
  end
73
65
  end
@@ -24,10 +24,6 @@ module RuboCop
24
24
  MSG = 'Use `filter_map` instead of `%<method_name>s.map`.'
25
25
  RESTRICT_ON_SEND = %i[select filter].freeze
26
26
 
27
- def_node_matcher :bad_method?, <<~PATTERN
28
- (send nil? :bad_method ...)
29
- PATTERN
30
-
31
27
  def on_send(node)
32
28
  return if (first_argument = node.first_argument) && !first_argument.block_pass_type?
33
29
  return unless (send_node = map_method_candidate(node))
@@ -45,9 +41,9 @@ module RuboCop
45
41
  def map_method_candidate(node)
46
42
  return unless (parent = node.parent)
47
43
 
48
- if parent.block_type? && parent.parent&.send_type?
44
+ if parent.block_type? && parent.parent&.call_type?
49
45
  parent.parent
50
- elsif parent.send_type?
46
+ elsif parent.call_type?
51
47
  parent
52
48
  end
53
49
  end
@@ -43,7 +43,7 @@ module RuboCop
43
43
  def_node_matcher :array?, <<~PATTERN
44
44
  {
45
45
  [!nil? array_type?]
46
- (send _ :to_a)
46
+ (call _ :to_a)
47
47
  (send (const nil? :Array) :[] _)
48
48
  (send nil? :Array _)
49
49
  }
@@ -52,14 +52,14 @@ module RuboCop
52
52
  def_node_matcher :hash?, <<~PATTERN
53
53
  {
54
54
  [!nil? hash_type?]
55
- (send _ :to_h)
55
+ (call _ :to_h)
56
56
  (send (const nil? :Hash) :[] _)
57
57
  (send nil? :Hash _)
58
58
  }
59
59
  PATTERN
60
60
 
61
61
  def_node_matcher :count?, <<~PATTERN
62
- (send {#array? #hash?} :count)
62
+ (call {#array? #hash?} :count)
63
63
  PATTERN
64
64
 
65
65
  def on_send(node)
@@ -69,6 +69,7 @@ module RuboCop
69
69
  corrector.replace(node.loc.selector, 'size')
70
70
  end
71
71
  end
72
+ alias on_csend on_send
72
73
  end
73
74
  end
74
75
  end
@@ -17,7 +17,7 @@ module RuboCop
17
17
  include SortBlock
18
18
  extend AutoCorrector
19
19
 
20
- MSG = 'Use `sort.reverse` instead.'
20
+ MSG = 'Use `%<prefer>s` instead.'
21
21
 
22
22
  def on_block(node)
23
23
  sort_with_block?(node) do |send, var_a, var_b, body|
@@ -41,11 +41,12 @@ module RuboCop
41
41
 
42
42
  def register_offense(send, node)
43
43
  range = sort_range(send, node)
44
+ prefer = "sort#{send.loc.dot.source}reverse"
44
45
 
45
- add_offense(range) do |corrector|
46
- replacement = 'sort.reverse'
46
+ message = format(MSG, prefer: prefer)
47
47
 
48
- corrector.replace(range, replacement)
48
+ add_offense(range, message: message) do |corrector|
49
+ corrector.replace(range, prefer)
49
50
  end
50
51
  end
51
52
  end
@@ -56,7 +56,8 @@ module RuboCop
56
56
  def_node_matcher :redundant_regex?, <<~PATTERN
57
57
  {(call $!nil? {:match :=~ :match?} (regexp (str $#literal_at_start?) (regopt)))
58
58
  (send (regexp (str $#literal_at_start?) (regopt)) {:match :match?} $_)
59
- (match-with-lvasgn (regexp (str $#literal_at_start?) (regopt)) $_)}
59
+ (match-with-lvasgn (regexp (str $#literal_at_start?) (regopt)) $_)
60
+ (send (regexp (str $#literal_at_start?) (regopt)) :=~ $_)}
60
61
  PATTERN
61
62
 
62
63
  def on_send(node)
@@ -16,11 +16,19 @@ module RuboCop
16
16
  # send('do_something')
17
17
  # attr_accessor 'do_something'
18
18
  # instance_variable_get('@ivar')
19
+ # respond_to?("string_#{interpolation}")
19
20
  #
20
21
  # # good
21
22
  # send(:do_something)
22
23
  # attr_accessor :do_something
23
24
  # instance_variable_get(:@ivar)
25
+ # respond_to?(:"string_#{interpolation}")
26
+ #
27
+ # # good - these methods don't support namespaced symbols
28
+ # const_get("#{module_path}::Base")
29
+ # const_source_location("#{module_path}::Base")
30
+ # const_defined?("#{module_path}::Base")
31
+ #
24
32
  #
25
33
  class StringIdentifierArgument < Base
26
34
  extend AutoCorrector
@@ -32,33 +40,73 @@ module RuboCop
32
40
  protected public public_constant module_function
33
41
  ].freeze
34
42
 
43
+ INTERPOLATION_IGNORE_METHODS = %i[const_get const_source_location const_defined?].freeze
44
+
45
+ TWO_ARGUMENTS_METHOD = :alias_method
46
+ MULTIPLE_ARGUMENTS_METHODS = %i[
47
+ attr_accessor attr_reader attr_writer private private_constant
48
+ protected public public_constant module_function
49
+ ].freeze
50
+
35
51
  # NOTE: `attr` method is not included in this list as it can cause false positives in Nokogiri API.
36
52
  # And `attr` may not be used because `Style/Attr` registers an offense.
37
53
  # https://github.com/rubocop/rubocop-performance/issues/278
38
54
  RESTRICT_ON_SEND = (%i[
39
- class_variable_defined? const_defined? const_get const_set const_source_location
55
+ class_variable_defined? const_set
40
56
  define_method instance_method method_defined? private_class_method? private_method_defined?
41
57
  protected_method_defined? public_class_method public_instance_method public_method_defined?
42
58
  remove_class_variable remove_method undef_method class_variable_get class_variable_set
43
59
  deprecate_constant remove_const ruby2_keywords define_singleton_method instance_variable_defined?
44
60
  instance_variable_get instance_variable_set method public_method public_send remove_instance_variable
45
61
  respond_to? send singleton_method __send__
46
- ] + COMMAND_METHODS).freeze
62
+ ] + COMMAND_METHODS + INTERPOLATION_IGNORE_METHODS).freeze
47
63
 
48
64
  def on_send(node)
49
65
  return if COMMAND_METHODS.include?(node.method_name) && node.receiver
50
- return unless (first_argument = node.first_argument)
51
- return unless first_argument.str_type?
52
66
 
53
- first_argument_value = first_argument.value
54
- return if first_argument_value.include?(' ') || first_argument_value.include?('::')
67
+ string_arguments(node).each do |string_argument|
68
+ string_argument_value = string_argument.value
69
+ next if string_argument_value.include?(' ') || string_argument_value.include?('::')
70
+
71
+ register_offense(string_argument, string_argument_value)
72
+ end
73
+ end
74
+
75
+ private
55
76
 
56
- replacement = first_argument_value.to_sym.inspect
77
+ def string_arguments(node)
78
+ arguments = if node.method?(TWO_ARGUMENTS_METHOD)
79
+ [node.first_argument, node.arguments[1]]
80
+ elsif MULTIPLE_ARGUMENTS_METHODS.include?(node.method_name)
81
+ node.arguments
82
+ else
83
+ [node.first_argument]
84
+ end
57
85
 
58
- message = format(MSG, symbol_arg: replacement, string_arg: first_argument.source)
86
+ arguments.compact.filter { |argument| string_argument_compatible?(argument, node) }
87
+ end
88
+
89
+ def string_argument_compatible?(argument, node)
90
+ return true if argument.str_type?
91
+
92
+ argument.dstr_type? && INTERPOLATION_IGNORE_METHODS.none? { |method| node.method?(method) }
93
+ end
94
+
95
+ def register_offense(argument, argument_value)
96
+ replacement = argument_replacement(argument, argument_value)
97
+
98
+ message = format(MSG, symbol_arg: replacement, string_arg: argument.source)
99
+
100
+ add_offense(argument, message: message) do |corrector|
101
+ corrector.replace(argument, replacement)
102
+ end
103
+ end
59
104
 
60
- add_offense(first_argument, message: message) do |corrector|
61
- corrector.replace(first_argument, replacement)
105
+ def argument_replacement(node, value)
106
+ if node.str_type?
107
+ value.to_sym.inspect
108
+ else
109
+ ":\"#{value.to_sym}\""
62
110
  end
63
111
  end
64
112
  end
@@ -16,6 +16,7 @@ module RuboCop
16
16
  # /ab/ =~ str
17
17
  # str.match(/ab/)
18
18
  # /ab/.match(str)
19
+ # /ab/ === str
19
20
  #
20
21
  # # good
21
22
  # str.include?('ab')
@@ -23,12 +24,13 @@ module RuboCop
23
24
  extend AutoCorrector
24
25
 
25
26
  MSG = 'Use `%<negation>sString#include?` instead of a regex match with literal-only pattern.'
26
- RESTRICT_ON_SEND = %i[match =~ !~ match?].freeze
27
+ RESTRICT_ON_SEND = %i[match =~ !~ match? ===].freeze
27
28
 
28
29
  def_node_matcher :redundant_regex?, <<~PATTERN
29
30
  {(call $!nil? {:match :=~ :!~ :match?} (regexp (str $#literal?) (regopt)))
30
- (send (regexp (str $#literal?) (regopt)) {:match :match?} $_)
31
- (match-with-lvasgn (regexp (str $#literal?) (regopt)) $_)}
31
+ (send (regexp (str $#literal?) (regopt)) {:match :match? :===} $_)
32
+ (match-with-lvasgn (regexp (str $#literal?) (regopt)) $_)
33
+ (send (regexp (str $#literal?) (regopt)) :=~ $_)}
32
34
  PATTERN
33
35
 
34
36
  # rubocop:disable Metrics/AbcSize
@@ -80,21 +80,21 @@ module RuboCop
80
80
  RESTRICT_ON_SEND = %i[inject reduce sum].freeze
81
81
 
82
82
  def_node_matcher :sum_candidate?, <<~PATTERN
83
- (send _ ${:inject :reduce} $_init ? ${(sym :+) (block_pass (sym :+))})
83
+ (call _ ${:inject :reduce} $_init ? ${(sym :+) (block_pass (sym :+))})
84
84
  PATTERN
85
85
 
86
86
  def_node_matcher :sum_map_candidate?, <<~PATTERN
87
- (send
87
+ (call
88
88
  {
89
- (block $(send _ {:map :collect}) ...)
90
- $(send _ {:map :collect} (block_pass _))
89
+ (block $(call _ {:map :collect}) ...)
90
+ $(call _ {:map :collect} (block_pass _))
91
91
  }
92
92
  :sum $_init ?)
93
93
  PATTERN
94
94
 
95
95
  def_node_matcher :sum_with_block_candidate?, <<~PATTERN
96
96
  (block
97
- $(send _ {:inject :reduce} $_init ?)
97
+ $(call _ {:inject :reduce} $_init ?)
98
98
  (args (arg $_acc) (arg $_elem))
99
99
  $send)
100
100
  PATTERN
@@ -110,6 +110,7 @@ module RuboCop
110
110
  handle_sum_candidate(node)
111
111
  handle_sum_map_candidate(node)
112
112
  end
113
+ alias on_csend on_send
113
114
 
114
115
  def on_block(node)
115
116
  sum_with_block_candidate?(node) do |send, init, var_acc, var_elem, body|
@@ -143,7 +144,7 @@ module RuboCop
143
144
  sum_map_candidate?(node) do |map, init|
144
145
  next if node.block_literal? || node.block_argument?
145
146
 
146
- message = build_sum_map_message(map.method_name, init)
147
+ message = build_sum_map_message(map, init)
147
148
 
148
149
  add_offense(sum_map_range(map, node), message: message) do |corrector|
149
150
  autocorrect_sum_map(corrector, node, map, init)
@@ -178,7 +179,7 @@ module RuboCop
178
179
 
179
180
  corrector.remove(sum_range)
180
181
 
181
- dot = '.' if map.receiver
182
+ dot = map.loc.dot&.source || ''
182
183
  corrector.replace(map_range, "#{dot}#{replacement}")
183
184
  end
184
185
 
@@ -205,10 +206,11 @@ module RuboCop
205
206
  format(msg, good_method: good_method, bad_method: bad_method)
206
207
  end
207
208
 
208
- def build_sum_map_message(method, init)
209
+ def build_sum_map_message(send_node, init)
209
210
  sum_method = build_good_method(init)
210
211
  good_method = "#{sum_method} { ... }"
211
- bad_method = "#{method} { ... }.#{sum_method}"
212
+ dot = send_node.loc.dot&.source || '.'
213
+ bad_method = "#{send_node.method_name} { ... }#{dot}#{sum_method}"
212
214
  format(MSG, good_method: good_method, bad_method: bad_method)
213
215
  end
214
216
 
@@ -39,6 +39,7 @@ module RuboCop
39
39
  def on_send(node)
40
40
  check(node)
41
41
  end
42
+ alias on_csend on_send
42
43
 
43
44
  def on_block(node)
44
45
  check(node)
@@ -49,6 +50,8 @@ module RuboCop
49
50
 
50
51
  def check(node)
51
52
  times_map_call(node) do |map_or_collect, count|
53
+ next unless handleable_receiver?(node)
54
+
52
55
  add_offense(node, message: message(map_or_collect, count)) do |corrector|
53
56
  replacement = "Array.new(#{count.source}#{map_or_collect.arguments.map { |arg| ", #{arg.source}" }.join})"
54
57
 
@@ -57,6 +60,13 @@ module RuboCop
57
60
  end
58
61
  end
59
62
 
63
+ def handleable_receiver?(node)
64
+ receiver = node.receiver.receiver
65
+ return true if receiver.literal? && (receiver.int_type? || receiver.float_type?)
66
+
67
+ node.receiver.dot?
68
+ end
69
+
60
70
  def message(map_or_collect, count)
61
71
  template = if count.literal?
62
72
  "#{MESSAGE}."
@@ -67,8 +77,10 @@ module RuboCop
67
77
  end
68
78
 
69
79
  def_node_matcher :times_map_call, <<~PATTERN
70
- {({block numblock} $(send (send $!nil? :times) {:map :collect}) ...)
71
- $(send (send $!nil? :times) {:map :collect} (block_pass ...))}
80
+ {
81
+ ({block numblock} $(call (call $!nil? :times) {:map :collect}) ...)
82
+ $(call (call $!nil? :times) {:map :collect} (block_pass ...))
83
+ }
72
84
  PATTERN
73
85
  end
74
86
  end
@@ -15,8 +15,8 @@ module RuboCop
15
15
  #
16
16
  # @example
17
17
  # # bad
18
- # ''.dup
19
- # "something".dup
18
+ # ''.dup # when Ruby 3.2 or lower
19
+ # "something".dup # when Ruby 3.2 or lower
20
20
  # String.new
21
21
  # String.new('')
22
22
  # String.new('something')
@@ -45,7 +45,7 @@ module RuboCop
45
45
  PATTERN
46
46
 
47
47
  def on_send(node)
48
- return unless dup_string?(node) || string_new?(node)
48
+ return unless (dup_string?(node) && target_ruby_version <= 3.2) || string_new?(node)
49
49
 
50
50
  add_offense(node) do |corrector|
51
51
  string_value = "+#{string_value(node)}"
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Performance
5
5
  # This module holds the RuboCop Performance version information.
6
6
  module Version
7
- STRING = '1.19.1'
7
+ STRING = '1.21.1'
8
8
 
9
9
  def self.document_version
10
10
  STRING.match('\d+\.\d+').to_s
metadata CHANGED
@@ -1,16 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-performance
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.19.1
4
+ version: 1.21.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
8
8
  - Jonas Arvidsson
9
9
  - Yuji Nakayama
10
- autorequire:
11
10
  bindir: bin
12
11
  cert_chain: []
13
- date: 2023-09-17 00:00:00.000000000 Z
12
+ date: 2024-06-16 00:00:00.000000000 Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: rubocop
@@ -18,7 +17,7 @@ dependencies:
18
17
  requirements:
19
18
  - - ">="
20
19
  - !ruby/object:Gem::Version
21
- version: 1.7.0
20
+ version: 1.48.1
22
21
  - - "<"
23
22
  - !ruby/object:Gem::Version
24
23
  version: '2.0'
@@ -28,7 +27,7 @@ dependencies:
28
27
  requirements:
29
28
  - - ">="
30
29
  - !ruby/object:Gem::Version
31
- version: 1.7.0
30
+ version: 1.48.1
32
31
  - - "<"
33
32
  - !ruby/object:Gem::Version
34
33
  version: '2.0'
@@ -38,14 +37,20 @@ dependencies:
38
37
  requirements:
39
38
  - - ">="
40
39
  - !ruby/object:Gem::Version
41
- version: 0.4.0
40
+ version: 1.31.1
41
+ - - "<"
42
+ - !ruby/object:Gem::Version
43
+ version: '2.0'
42
44
  type: :runtime
43
45
  prerelease: false
44
46
  version_requirements: !ruby/object:Gem::Requirement
45
47
  requirements:
46
48
  - - ">="
47
49
  - !ruby/object:Gem::Version
48
- version: 0.4.0
50
+ version: 1.31.1
51
+ - - "<"
52
+ - !ruby/object:Gem::Version
53
+ version: '2.0'
49
54
  description: |
50
55
  A collection of RuboCop cops to check for performance optimizations
51
56
  in Ruby code.
@@ -124,10 +129,9 @@ metadata:
124
129
  homepage_uri: https://docs.rubocop.org/rubocop-performance/
125
130
  changelog_uri: https://github.com/rubocop/rubocop-performance/blob/master/CHANGELOG.md
126
131
  source_code_uri: https://github.com/rubocop/rubocop-performance/
127
- documentation_uri: https://docs.rubocop.org/rubocop-performance/1.19/
132
+ documentation_uri: https://docs.rubocop.org/rubocop-performance/1.21/
128
133
  bug_tracker_uri: https://github.com/rubocop/rubocop-performance/issues
129
134
  rubygems_mfa_required: 'true'
130
- post_install_message:
131
135
  rdoc_options: []
132
136
  require_paths:
133
137
  - lib
@@ -142,8 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
142
146
  - !ruby/object:Gem::Version
143
147
  version: '0'
144
148
  requirements: []
145
- rubygems_version: 3.5.0.dev
146
- signing_key:
149
+ rubygems_version: 3.6.0.dev
147
150
  specification_version: 4
148
151
  summary: Automatic performance checking tool for Ruby code.
149
152
  test_files: []