rubocop-performance 1.19.1 → 1.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  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 +2 -2
  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 +1 -1
  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 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1fa48a119a4c26e989f4d73366c0cbac4bbe854453a702e49f0af9348b1cf798
4
- data.tar.gz: e63e391f5f6e2ab2500033cd83df591b9d48254e7f295ce69bbb352e5242c67a
3
+ metadata.gz: c00ee718f283199f3c4f53b54071998b356f7239edcd3c84015bac94ad1b05dc
4
+ data.tar.gz: a5e7d011cd090fd5d30eb0ef9e12acabb93a993cc3acb46129da46c37294e1ba
5
5
  SHA512:
6
- metadata.gz: 98939001b2b61212c3b4bab44c8b9f9ae8bd906eeb873e2a9516b075f896679fbd513c23c874555982a14d390d265fe01451c218860249ff18376d89f1f59709
7
- data.tar.gz: ce73767335d2fe19418afa72ca4a8c5c15632960a2d77282b1042c002c65ec1d744af929fc3b612ba5f7c7e7a5cbaa35aab978fcc8a57e7dde02b114c8fc873b
6
+ metadata.gz: 711efd2eae8c54dc1b567e198360038fb16eee00c2e6a61db7dbcfb063f219618e36809ac5679cc531e8fe84245ea1f66e6e8eb9fbed5cbb9dcc22f57c10dab6
7
+ data.tar.gz: 5618f678392ffb6f93b34388e3ef060c017f7e70b53d992ebe48d2bb49b7ec5570adc7b21f5f22f0511cddc4fb1dead044904d7bcf40949191ee6ed8b884565f
data/README.md CHANGED
@@ -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)
@@ -39,11 +39,11 @@ module RuboCop
39
39
 
40
40
  def_node_matcher :map_compact, <<~PATTERN
41
41
  {
42
- (send
42
+ (call
43
43
  $(call _ {:map :collect}
44
44
  (block_pass
45
45
  (sym _))) _)
46
- (send
46
+ (call
47
47
  (block
48
48
  $(call _ {:map :collect})
49
49
  (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,7 +63,7 @@ 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
66
+ block_argument = node.first_argument
67
67
  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)
@@ -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.0'
8
8
 
9
9
  def self.document_version
10
10
  STRING.match('\d+\.\d+').to_s
metadata CHANGED
@@ -1,7 +1,7 @@
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.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2023-09-17 00:00:00.000000000 Z
13
+ date: 2024-03-30 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rubocop
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ">="
20
20
  - !ruby/object:Gem::Version
21
- version: 1.7.0
21
+ version: 1.48.1
22
22
  - - "<"
23
23
  - !ruby/object:Gem::Version
24
24
  version: '2.0'
@@ -28,7 +28,7 @@ dependencies:
28
28
  requirements:
29
29
  - - ">="
30
30
  - !ruby/object:Gem::Version
31
- version: 1.7.0
31
+ version: 1.48.1
32
32
  - - "<"
33
33
  - !ruby/object:Gem::Version
34
34
  version: '2.0'
@@ -38,14 +38,20 @@ dependencies:
38
38
  requirements:
39
39
  - - ">="
40
40
  - !ruby/object:Gem::Version
41
- version: 0.4.0
41
+ version: 1.31.1
42
+ - - "<"
43
+ - !ruby/object:Gem::Version
44
+ version: '2.0'
42
45
  type: :runtime
43
46
  prerelease: false
44
47
  version_requirements: !ruby/object:Gem::Requirement
45
48
  requirements:
46
49
  - - ">="
47
50
  - !ruby/object:Gem::Version
48
- version: 0.4.0
51
+ version: 1.31.1
52
+ - - "<"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
49
55
  description: |
50
56
  A collection of RuboCop cops to check for performance optimizations
51
57
  in Ruby code.
@@ -124,7 +130,7 @@ metadata:
124
130
  homepage_uri: https://docs.rubocop.org/rubocop-performance/
125
131
  changelog_uri: https://github.com/rubocop/rubocop-performance/blob/master/CHANGELOG.md
126
132
  source_code_uri: https://github.com/rubocop/rubocop-performance/
127
- documentation_uri: https://docs.rubocop.org/rubocop-performance/1.19/
133
+ documentation_uri: https://docs.rubocop.org/rubocop-performance/1.21/
128
134
  bug_tracker_uri: https://github.com/rubocop/rubocop-performance/issues
129
135
  rubygems_mfa_required: 'true'
130
136
  post_install_message:
@@ -142,7 +148,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
142
148
  - !ruby/object:Gem::Version
143
149
  version: '0'
144
150
  requirements: []
145
- rubygems_version: 3.5.0.dev
151
+ rubygems_version: 3.5.3
146
152
  signing_key:
147
153
  specification_version: 4
148
154
  summary: Automatic performance checking tool for Ruby code.