rubocop-performance 1.13.3 → 1.19.0

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +13 -2
  5. data/lib/rubocop/cop/mixin/regexp_metacharacter.rb +2 -2
  6. data/lib/rubocop/cop/mixin/sort_block.rb +7 -0
  7. data/lib/rubocop/cop/performance/ancestors_include.rb +1 -2
  8. data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +3 -2
  9. data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +2 -6
  10. data/lib/rubocop/cop/performance/bind_call.rb +1 -2
  11. data/lib/rubocop/cop/performance/block_given_with_explicit_block.rb +1 -1
  12. data/lib/rubocop/cop/performance/caller.rb +1 -2
  13. data/lib/rubocop/cop/performance/case_when_splat.rb +7 -13
  14. data/lib/rubocop/cop/performance/casecmp.rb +10 -12
  15. data/lib/rubocop/cop/performance/chain_array_allocation.rb +5 -5
  16. data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +4 -6
  17. data/lib/rubocop/cop/performance/compare_with_block.rb +20 -11
  18. data/lib/rubocop/cop/performance/concurrent_monotonic_time.rb +1 -1
  19. data/lib/rubocop/cop/performance/constant_regexp.rb +6 -4
  20. data/lib/rubocop/cop/performance/count.rb +39 -3
  21. data/lib/rubocop/cop/performance/delete_prefix.rb +8 -2
  22. data/lib/rubocop/cop/performance/delete_suffix.rb +8 -2
  23. data/lib/rubocop/cop/performance/detect.rb +7 -6
  24. data/lib/rubocop/cop/performance/double_start_end_with.rb +4 -5
  25. data/lib/rubocop/cop/performance/end_with.rb +7 -6
  26. data/lib/rubocop/cop/performance/fixed_size.rb +2 -2
  27. data/lib/rubocop/cop/performance/flat_map.rb +7 -5
  28. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +17 -15
  29. data/lib/rubocop/cop/performance/io_readlines.rb +2 -2
  30. data/lib/rubocop/cop/performance/map_compact.rb +9 -4
  31. data/lib/rubocop/cop/performance/map_method_chain.rb +87 -0
  32. data/lib/rubocop/cop/performance/method_object_as_block.rb +1 -1
  33. data/lib/rubocop/cop/performance/open_struct.rb +2 -3
  34. data/lib/rubocop/cop/performance/range_include.rb +2 -2
  35. data/lib/rubocop/cop/performance/redundant_block_call.rb +2 -2
  36. data/lib/rubocop/cop/performance/redundant_equality_comparison_block.rb +38 -3
  37. data/lib/rubocop/cop/performance/redundant_match.rb +10 -9
  38. data/lib/rubocop/cop/performance/redundant_merge.rb +9 -16
  39. data/lib/rubocop/cop/performance/redundant_sort_block.rb +17 -10
  40. data/lib/rubocop/cop/performance/redundant_split_regexp_argument.rb +3 -2
  41. data/lib/rubocop/cop/performance/redundant_string_chars.rb +10 -6
  42. data/lib/rubocop/cop/performance/regexp_match.rb +23 -24
  43. data/lib/rubocop/cop/performance/reverse_each.rb +3 -3
  44. data/lib/rubocop/cop/performance/reverse_first.rb +4 -3
  45. data/lib/rubocop/cop/performance/select_map.rb +2 -1
  46. data/lib/rubocop/cop/performance/size.rb +1 -2
  47. data/lib/rubocop/cop/performance/sort_reverse.rb +19 -10
  48. data/lib/rubocop/cop/performance/squeeze.rb +8 -8
  49. data/lib/rubocop/cop/performance/start_with.rb +7 -6
  50. data/lib/rubocop/cop/performance/string_identifier_argument.rb +16 -11
  51. data/lib/rubocop/cop/performance/string_include.rb +23 -17
  52. data/lib/rubocop/cop/performance/string_replacement.rb +7 -10
  53. data/lib/rubocop/cop/performance/sum.rb +7 -4
  54. data/lib/rubocop/cop/performance/times_map.rb +6 -7
  55. data/lib/rubocop/cop/performance/uri_default_parser.rb +4 -6
  56. data/lib/rubocop/cop/performance_cops.rb +1 -0
  57. data/lib/rubocop/performance/version.rb +1 -1
  58. metadata +6 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cc8de809f0aa167f08fd75ed2f7047fabc37b06b12a0c59e4647530b18fb6d99
4
- data.tar.gz: df849a7c117eb44d8851969031cda6d5927e2f271ad41bc3689212bad6b9e2e1
3
+ metadata.gz: 06f3e8346dc7f9be72a1f18c62a25dc02aeac41debcfe3c58df68e2a6904df43
4
+ data.tar.gz: f01facdb92a3056d05990e2ed2b6b1920de0501edf53a08e95e08620651c8557
5
5
  SHA512:
6
- metadata.gz: 24c72e01dfa6cfe35b3c93e1c83d63aaa0362dff7bc4ed234b8b6f8c1a434927a945b077273ec84c95be86d70e050f28e18dad27ffc9c72350f49c7ff3477b4a
7
- data.tar.gz: 3340644f87ea108d8f7022756ff5cdf442b0d2951cd9ea351bb0a4012c2d0721dc921b1402e8b5f952899015b51c402cdbcab5e785d764f4655aa068c71be6b3
6
+ metadata.gz: 07eb541698b6ccf2985ef62392bd78703bf6607e2877ef3865ab1762d62910c4c346fbb7e9bc48af7b7d98b0e1ad22e8c79ea2425555fbe75c2a5e614837eaa7
7
+ data.tar.gz: 60fc9f900906cca518d0da62170f71ae6e5f5ffd44f4c1cbe75af83f9a1fef252b6bab95d3af4f49f328d4a856cd17c7f47621ee62575ab282eacfd26dc09841
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-22 Bozhidar Batsov
1
+ Copyright (c) 2012-23 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
@@ -11,7 +11,7 @@ Performance optimization analysis for your projects, as an extension to [RuboCop
11
11
  Just install the `rubocop-performance` gem
12
12
 
13
13
  ```sh
14
- gem install rubocop-performance
14
+ $ gem install rubocop-performance
15
15
  ```
16
16
 
17
17
  or if you use bundler put this in your `Gemfile`
@@ -47,7 +47,7 @@ cops together with the standard cops.
47
47
  ### Command line
48
48
 
49
49
  ```sh
50
- rubocop --require rubocop-performance
50
+ $ rubocop --require rubocop-performance
51
51
  ```
52
52
 
53
53
  ### Rake task
data/config/default.yml CHANGED
@@ -1,5 +1,9 @@
1
1
  # This is the default configuration file.
2
2
 
3
+ Performance:
4
+ Enabled: true
5
+ DocumentationBaseURL: https://docs.rubocop.org/rubocop-performance
6
+
3
7
  Performance/AncestorsInclude:
4
8
  Description: 'Use `A <= B` instead of `A.ancestors.include?(B)`.'
5
9
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#ancestorsinclude-vs--code'
@@ -160,7 +164,7 @@ Performance/FlatMap:
160
164
  Description: >-
161
165
  Use `Enumerable#flat_map`
162
166
  instead of `Enumerable#map...Array#flatten(1)`
163
- or `Enumberable#collect..Array#flatten(1)`.
167
+ or `Enumerable#collect..Array#flatten(1)`.
164
168
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code'
165
169
  Enabled: true
166
170
  VersionAdded: '0.30'
@@ -189,6 +193,12 @@ Performance/MapCompact:
189
193
  SafeAutoCorrect: false
190
194
  VersionAdded: '1.11'
191
195
 
196
+ Performance/MapMethodChain:
197
+ Description: 'Checks if the `map` method is used in a chain.'
198
+ Enabled: pending
199
+ Safe: false
200
+ VersionAdded: '1.19'
201
+
192
202
  Performance/MethodObjectAsBlock:
193
203
  Description: 'Use block explicitly instead of block-passing a method object.'
194
204
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#normal-way-to-apply-method-vs-method-code'
@@ -222,6 +232,7 @@ Performance/RedundantEqualityComparisonBlock:
222
232
  Reference: 'https://github.com/rails/rails/pull/41363'
223
233
  Enabled: pending
224
234
  Safe: false
235
+ AllowRegexpMatch: true
225
236
  VersionAdded: '1.10'
226
237
 
227
238
  Performance/RedundantMatch:
@@ -247,7 +258,7 @@ Performance/RedundantSortBlock:
247
258
  VersionAdded: '1.7'
248
259
 
249
260
  Performance/RedundantSplitRegexpArgument:
250
- Description: 'This cop identifies places where `split` argument can be replaced from a deterministic regexp to a string.'
261
+ Description: 'Identifies places where `split` argument can be replaced from a deterministic regexp to a string.'
251
262
  Enabled: pending
252
263
  VersionAdded: '1.10'
253
264
 
@@ -54,9 +54,9 @@ module RuboCop
54
54
 
55
55
  def drop_start_metacharacter(regexp_string)
56
56
  if regexp_string.start_with?('\\A')
57
- regexp_string[2..-1] # drop `\A` anchor
57
+ regexp_string[2..] # drop `\A` anchor
58
58
  else
59
- regexp_string[1..-1] # drop `^` anchor
59
+ regexp_string[1..] # drop `^` anchor
60
60
  end
61
61
  end
62
62
 
@@ -14,6 +14,13 @@ module RuboCop
14
14
  $send)
15
15
  PATTERN
16
16
 
17
+ def_node_matcher :sort_with_numblock?, <<~PATTERN
18
+ (numblock
19
+ $(send _ :sort)
20
+ $_arg_count
21
+ $send)
22
+ PATTERN
23
+
17
24
  def_node_matcher :replaceable_body?, <<~PATTERN
18
25
  (send (lvar %1) :<=> (lvar %2))
19
26
  PATTERN
@@ -3,8 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # This cop is used to identify usages of `ancestors.include?` and
7
- # change them to use `<=` instead.
6
+ # Identifies usages of `ancestors.include?` and change them to use `<=` instead.
8
7
  #
9
8
  # @safety
10
9
  # This cop is unsafe because it can't tell whether the receiver is a class or an object.
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # This cop identifies places where slicing arrays with semi-infinite ranges
6
+ # Identifies places where slicing arrays with semi-infinite ranges
7
7
  # can be replaced by `Array#take` and `Array#drop`.
8
8
  # This cop was created due to a mistake in microbenchmark and hence is disabled by default.
9
9
  # Refer https://github.com/rubocop/rubocop-performance/pull/175#issuecomment-731892717
@@ -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 $_ $%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,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # This cop identifies places where numeric argument to BigDecimal should be
6
+ # Identifies places where numeric argument to BigDecimal should be
7
7
  # converted to string. Initializing from String is faster
8
8
  # than from Numeric for BigDecimal.
9
9
  #
@@ -41,11 +41,7 @@ module RuboCop
41
41
  end
42
42
  elsif (numeric_to_d = to_d?(node))
43
43
  add_offense(numeric_to_d.source_range) do |corrector|
44
- big_decimal_args = node
45
- .arguments
46
- .map(&:source)
47
- .unshift("'#{numeric_to_d.source}'")
48
- .join(', ')
44
+ big_decimal_args = node.arguments.map(&:source).unshift("'#{numeric_to_d.source}'").join(', ')
49
45
 
50
46
  corrector.replace(node, "BigDecimal(#{big_decimal_args})")
51
47
  end
@@ -26,8 +26,7 @@ module RuboCop
26
26
 
27
27
  minimum_target_ruby_version 2.7
28
28
 
29
- MSG = 'Use `bind_call(%<bind_arg>s%<comma>s%<call_args>s)` ' \
30
- 'instead of `bind(%<bind_arg>s).call(%<call_args>s)`.'
29
+ MSG = 'Use `bind_call(%<bind_arg>s%<comma>s%<call_args>s)` instead of `bind(%<bind_arg>s).call(%<call_args>s)`.'
31
30
  RESTRICT_ON_SEND = %i[call].freeze
32
31
 
33
32
  def_node_matcher :bind_with_call_method?, <<~PATTERN
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # This cop identifies unnecessary use of a `block_given?` where explicit check
6
+ # Identifies unnecessary use of a `block_given?` where explicit check
7
7
  # of block argument would suffice.
8
8
  #
9
9
  # @example
@@ -3,8 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # This cop identifies places where `caller[n]`
7
- # can be replaced by `caller(n..n).first`.
6
+ # Identifies places where `caller[n]` can be replaced by `caller(n..n).first`.
8
7
  #
9
8
  # @example
10
9
  # # bad
@@ -18,7 +18,7 @@ module RuboCop
18
18
  # that is inside of the splat expansion.
19
19
  #
20
20
  # @safety
21
- # This cop is not unsafe auto-correction because it is not a guaranteed
21
+ # This cop is not unsafe autocorrection because it is not a guaranteed
22
22
  # performance improvement. If the data being processed by the `case` condition is
23
23
  # normalized in a manner that favors hitting a condition in the splat expansion,
24
24
  # it is possible that moving the splat condition to the end will use more memory,
@@ -99,8 +99,7 @@ module RuboCop
99
99
 
100
100
  def inline_fix_branch(corrector, when_node)
101
101
  conditions = when_node.conditions
102
- range = range_between(conditions[0].loc.expression.begin_pos,
103
- conditions[-1].loc.expression.end_pos)
102
+ range = range_between(conditions[0].source_range.begin_pos, conditions[-1].source_range.end_pos)
104
103
 
105
104
  corrector.replace(range, replacement(conditions))
106
105
  end
@@ -111,8 +110,7 @@ module RuboCop
111
110
  return if when_branches.one?
112
111
 
113
112
  corrector.remove(when_branch_range(when_node))
114
- corrector.insert_after(when_branches.last.source_range,
115
- reordering_correction(when_node))
113
+ corrector.insert_after(when_branches.last, reordering_correction(when_node))
116
114
  end
117
115
 
118
116
  def reordering_correction(when_node)
@@ -126,11 +124,9 @@ module RuboCop
126
124
  end
127
125
 
128
126
  def when_branch_range(when_node)
129
- next_branch =
130
- when_node.parent.when_branches[when_node.branch_index + 1]
127
+ next_branch = when_node.parent.when_branches[when_node.branch_index + 1]
131
128
 
132
- range_between(when_node.source_range.begin_pos,
133
- next_branch.source_range.begin_pos)
129
+ range_between(when_node.source_range.begin_pos, next_branch.source_range.begin_pos)
134
130
  end
135
131
 
136
132
  def new_condition_with_then(node, new_condition)
@@ -162,13 +158,11 @@ module RuboCop
162
158
  def non_splat?(condition)
163
159
  variable, = *condition
164
160
 
165
- (condition.splat_type? && variable.array_type?) ||
166
- !condition.splat_type?
161
+ (condition.splat_type? && variable.array_type?) || !condition.splat_type?
167
162
  end
168
163
 
169
164
  def needs_reorder?(when_node)
170
- following_branches =
171
- when_node.parent.when_branches[(when_node.branch_index + 1)..-1]
165
+ following_branches = when_node.parent.when_branches[(when_node.branch_index + 1)..]
172
166
 
173
167
  following_branches.any? do |when_branch|
174
168
  when_branch.conditions.any? do |condition|
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # This cop identifies places where a case-insensitive string comparison
6
+ # Identifies places where a case-insensitive string comparison
7
7
  # can better be implemented using `casecmp`.
8
8
  #
9
9
  # @safety
@@ -54,11 +54,11 @@ module RuboCop
54
54
  return unless (parts = take_method_apart(node))
55
55
 
56
56
  _receiver, method, arg, variable = parts
57
- good_method = build_good_method(arg, variable)
57
+ good_method = build_good_method(method, arg, variable)
58
58
 
59
59
  message = format(MSG, good: good_method, bad: node.source)
60
60
  add_offense(node, message: message) do |corrector|
61
- correction(corrector, node, method, arg, variable)
61
+ autocorrect(corrector, node, good_method)
62
62
  end
63
63
  end
64
64
 
@@ -81,22 +81,20 @@ module RuboCop
81
81
  [receiver, method, arg, variable]
82
82
  end
83
83
 
84
- def correction(corrector, node, method, arg, variable)
85
- corrector.insert_before(node.loc.expression, '!') if method == :!=
86
-
87
- replacement = build_good_method(arg, variable)
88
-
89
- corrector.replace(node.loc.expression, replacement)
84
+ def autocorrect(corrector, node, replacement)
85
+ corrector.replace(node, replacement)
90
86
  end
91
87
 
92
- def build_good_method(arg, variable)
88
+ def build_good_method(method, arg, variable)
89
+ bang = method == :!= ? '!' : ''
90
+
93
91
  # We want resulting call to be parenthesized
94
92
  # if arg already includes one or more sets of parens, don't add more
95
93
  # or if method call already used parens, again, don't add more
96
94
  if arg.send_type? || !parentheses?(arg)
97
- "#{variable.source}.casecmp(#{arg.source}).zero?"
95
+ "#{bang}#{variable.source}.casecmp(#{arg.source}).zero?"
98
96
  else
99
- "#{variable.source}.casecmp#{arg.source}.zero?"
97
+ "#{bang}#{variable.source}.casecmp#{arg.source}.zero?"
100
98
  end
101
99
  end
102
100
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # This cop is used to identify usages of `array.compact.flatten.map { |x| x.downcase }`.
6
+ # Identifies usages of `array.compact.flatten.map { |x| x.downcase }`.
7
7
  # Each of these methods (`compact`, `flatten`, `map`) will generate a new intermediate array
8
8
  # that is promptly thrown away. Instead it is faster to mutate when we know it's safe.
9
9
  #
@@ -33,7 +33,7 @@ module RuboCop
33
33
  RETURNS_NEW_ARRAY_WHEN_NO_BLOCK = %i[zip product].to_set.freeze
34
34
 
35
35
  # These methods ALWAYS return a new array
36
- # after they're called it's safe to mutate the the resulting array
36
+ # after they're called it's safe to mutate the resulting array
37
37
  ALWAYS_RETURNS_NEW_ARRAY = %i[* + - collect compact drop
38
38
  drop_while flatten map reject
39
39
  reverse rotate select shuffle sort
@@ -47,13 +47,13 @@ module RuboCop
47
47
 
48
48
  RETURNS_NEW_ARRAY = (ALWAYS_RETURNS_NEW_ARRAY + RETURNS_NEW_ARRAY_WHEN_NO_BLOCK).freeze
49
49
 
50
- MSG = 'Use unchained `%<method>s` and `%<second_method>s!` '\
51
- '(followed by `return array` if required) instead of chaining '\
50
+ MSG = 'Use unchained `%<method>s` and `%<second_method>s!` ' \
51
+ '(followed by `return array` if required) instead of chaining ' \
52
52
  '`%<method>s...%<second_method>s`.'
53
53
 
54
54
  def_node_matcher :chain_array_allocation?, <<~PATTERN
55
55
  (send {
56
- (send _ $%RETURN_NEW_ARRAY_WHEN_ARGS {int lvar ivar cvar gvar})
56
+ (send _ $%RETURN_NEW_ARRAY_WHEN_ARGS {int lvar ivar cvar gvar send})
57
57
  (block (send _ $%ALWAYS_RETURNS_NEW_ARRAY) ...)
58
58
  (send _ $%RETURNS_NEW_ARRAY ...)
59
59
  } $%HAS_MUTATION_ALTERNATIVE ...)
@@ -5,8 +5,8 @@ require 'set'
5
5
  module RuboCop
6
6
  module Cop
7
7
  module Performance
8
- # This cop identifies places where Array and Hash literals are used
9
- # within loops. It is better to extract them into a local variable or constant
8
+ # Identifies places where Array and Hash literals are used within loops.
9
+ # It is better to extract them into a local variable or constant
10
10
  # to avoid unnecessary allocations on each iteration.
11
11
  #
12
12
  # You can set the minimum number of elements to consider
@@ -32,7 +32,7 @@ module RuboCop
32
32
  # end
33
33
  #
34
34
  class CollectionLiteralInLoop < Base
35
- MSG = 'Avoid immutable %<literal_class>s literals in loops. '\
35
+ MSG = 'Avoid immutable %<literal_class>s literals in loops. ' \
36
36
  'It is better to extract it into a local variable or a constant.'
37
37
 
38
38
  POST_CONDITION_LOOP_TYPES = %i[while_post until_post].freeze
@@ -104,9 +104,7 @@ module RuboCop
104
104
  end
105
105
 
106
106
  def loop?(ancestor, node)
107
- keyword_loop?(ancestor.type) ||
108
- kernel_loop?(ancestor) ||
109
- node_within_enumerable_loop?(node, ancestor)
107
+ keyword_loop?(ancestor.type) || kernel_loop?(ancestor) || node_within_enumerable_loop?(node, ancestor)
110
108
  end
111
109
 
112
110
  def keyword_loop?(type)
@@ -3,37 +3,44 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # This cop identifies places where `sort { |a, b| a.foo <=> b.foo }`
6
+ # Identifies places where `sort { |a, b| a.foo <=> b.foo }`
7
7
  # can be replaced by `sort_by(&:foo)`.
8
- # This cop also checks `max` and `min` methods.
8
+ # This cop also checks `sort!`, `min`, `max` and `minmax` methods.
9
9
  #
10
10
  # @example
11
11
  # # bad
12
- # array.sort { |a, b| a.foo <=> b.foo }
13
- # array.max { |a, b| a.foo <=> b.foo }
14
- # array.min { |a, b| a.foo <=> b.foo }
15
- # array.sort { |a, b| a[:foo] <=> b[:foo] }
12
+ # array.sort { |a, b| a.foo <=> b.foo }
13
+ # array.sort! { |a, b| a.foo <=> b.foo }
14
+ # array.max { |a, b| a.foo <=> b.foo }
15
+ # array.min { |a, b| a.foo <=> b.foo }
16
+ # array.minmax { |a, b| a.foo <=> b.foo }
17
+ # array.sort { |a, b| a[:foo] <=> b[:foo] }
16
18
  #
17
19
  # # good
18
20
  # array.sort_by(&:foo)
21
+ # array.sort_by!(&:foo)
19
22
  # array.sort_by { |v| v.foo }
20
23
  # array.sort_by do |var|
21
24
  # var.foo
22
25
  # end
23
26
  # array.max_by(&:foo)
24
27
  # array.min_by(&:foo)
28
+ # array.minmax_by(&:foo)
25
29
  # array.sort_by { |a| a[:foo] }
26
30
  class CompareWithBlock < Base
27
31
  include RangeHelp
28
32
  extend AutoCorrector
29
33
 
30
- MSG = 'Use `%<compare_method>s_by%<instead>s` instead of ' \
34
+ MSG = 'Use `%<replacement_method>s%<instead>s` instead of ' \
31
35
  '`%<compare_method>s { |%<var_a>s, %<var_b>s| %<str_a>s ' \
32
36
  '<=> %<str_b>s }`.'
33
37
 
38
+ REPLACEMENT = { sort: :sort_by, sort!: :sort_by!, min: :min_by, max: :max_by, minmax: :minmax_by }.freeze
39
+ private_constant :REPLACEMENT
40
+
34
41
  def_node_matcher :compare?, <<~PATTERN
35
42
  (block
36
- $(send _ {:sort :min :max})
43
+ $(send _ {:sort :sort! :min :max :minmax})
37
44
  (args (arg $_a) (arg $_b))
38
45
  $send)
39
46
  PATTERN
@@ -54,9 +61,9 @@ module RuboCop
54
61
 
55
62
  add_offense(range, message: message(send, method, var_a, var_b, args_a)) do |corrector|
56
63
  replacement = if method == :[]
57
- "#{send.method_name}_by { |a| a[#{args_a.first.source}] }"
64
+ "#{REPLACEMENT[send.method_name]} { |a| a[#{args_a.first.source}] }"
58
65
  else
59
- "#{send.method_name}_by(&:#{method})"
66
+ "#{REPLACEMENT[send.method_name]}(&:#{method})"
60
67
  end
61
68
  corrector.replace(range, replacement)
62
69
  end
@@ -82,7 +89,8 @@ module RuboCop
82
89
 
83
90
  # rubocop:disable Metrics/MethodLength
84
91
  def message(send, method, var_a, var_b, args)
85
- compare_method = send.method_name
92
+ compare_method = send.method_name
93
+ replacement_method = REPLACEMENT[compare_method]
86
94
  if method == :[]
87
95
  key = args.first
88
96
  instead = " { |a| a[#{key.source}] }"
@@ -94,6 +102,7 @@ module RuboCop
94
102
  str_b = "#{var_b}.#{method}"
95
103
  end
96
104
  format(MSG, compare_method: compare_method,
105
+ replacement_method: replacement_method,
97
106
  instead: instead,
98
107
  var_a: var_a,
99
108
  var_b: var_b,
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # This cop identifies places where `Concurrent.monotonic_time`
6
+ # Identifies places where `Concurrent.monotonic_time`
7
7
  # can be replaced by `Process.clock_gettime(Process::CLOCK_MONOTONIC)`.
8
8
  #
9
9
  # @example
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # This cop finds regular expressions with dynamic components that are all constants.
6
+ # Finds regular expressions with dynamic components that are all constants.
7
7
  #
8
8
  # Ruby allocates a new Regexp object every time it executes a code containing such
9
9
  # a regular expression. It is more efficient to extract it into a constant,
@@ -38,10 +38,12 @@ module RuboCop
38
38
 
39
39
  MSG = 'Extract this regexp into a constant, memoize it, or append an `/o` option to its options.'
40
40
 
41
+ def self.autocorrect_incompatible_with
42
+ [RegexpMatch]
43
+ end
44
+
41
45
  def on_regexp(node)
42
- return if within_allowed_assignment?(node) ||
43
- !include_interpolated_const?(node) ||
44
- node.single_interpolation?
46
+ return if within_allowed_assignment?(node) || !include_interpolated_const?(node) || node.single_interpolation?
45
47
 
46
48
  add_offense(node) do |corrector|
47
49
  corrector.insert_after(node, 'o')
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # This cop is used to identify usages of `count` on an `Enumerable` that
6
+ # Identifies usages of `count` on an `Enumerable` that
7
7
  # follow calls to `select`, `find_all`, `filter` or `reject`. Querying logic can instead be
8
8
  # passed to the `count` call.
9
9
  #
@@ -79,12 +79,11 @@ module RuboCop
79
79
  def autocorrect(corrector, node, selector_node, selector)
80
80
  selector_loc = selector_node.loc.selector
81
81
 
82
- return if selector == :reject
83
-
84
82
  range = source_starting_at(node) { |n| n.loc.dot.begin_pos }
85
83
 
86
84
  corrector.remove(range)
87
85
  corrector.replace(selector_loc, 'count')
86
+ negate_reject(corrector, node) if selector == :reject
88
87
  end
89
88
 
90
89
  def eligible_node?(node)
@@ -100,6 +99,43 @@ module RuboCop
100
99
 
101
100
  range_between(begin_pos, node.source_range.end_pos)
102
101
  end
102
+
103
+ def negate_reject(corrector, node)
104
+ if node.receiver.send_type?
105
+ negate_block_pass_reject(corrector, node)
106
+ else
107
+ negate_block_reject(corrector, node)
108
+ end
109
+ end
110
+
111
+ def negate_block_pass_reject(corrector, node)
112
+ corrector.replace(
113
+ node.receiver.source_range.with(begin_pos: node.receiver.loc.begin.begin_pos),
114
+ negate_block_pass_as_inline_block(node.receiver)
115
+ )
116
+ end
117
+
118
+ def negate_block_reject(corrector, node)
119
+ target =
120
+ if node.receiver.body.begin_type?
121
+ node.receiver.body.children.last
122
+ else
123
+ node.receiver.body
124
+ end
125
+ corrector.replace(target, negate_expression(target))
126
+ end
127
+
128
+ def negate_expression(node)
129
+ "!(#{node.source})"
130
+ end
131
+
132
+ def negate_block_pass_as_inline_block(node)
133
+ if node.last_argument.children.first.sym_type?
134
+ " { |element| !element.#{node.last_argument.children.first.value} }"
135
+ else
136
+ " { !#{node.last_argument.children.first.source}.call }"
137
+ end
138
+ end
103
139
  end
104
140
  end
105
141
  end
@@ -49,6 +49,9 @@ module RuboCop
49
49
  class DeletePrefix < Base
50
50
  include RegexpMetacharacter
51
51
  extend AutoCorrector
52
+ extend TargetRubyVersion
53
+
54
+ minimum_target_ruby_version 2.5
52
55
 
53
56
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
54
57
  RESTRICT_ON_SEND = %i[gsub gsub! sub sub!].freeze
@@ -61,9 +64,10 @@ module RuboCop
61
64
  }.freeze
62
65
 
63
66
  def_node_matcher :delete_prefix_candidate?, <<~PATTERN
64
- (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 $_))
65
68
  PATTERN
66
69
 
70
+ # rubocop:disable Metrics/AbcSize
67
71
  def on_send(node)
68
72
  return unless (receiver, bad_method, regexp_str, replace_string = delete_prefix_candidate?(node))
69
73
  return unless replace_string.empty?
@@ -77,11 +81,13 @@ module RuboCop
77
81
  regexp_str = interpret_string_escapes(regexp_str)
78
82
  string_literal = to_string_literal(regexp_str)
79
83
 
80
- new_code = "#{receiver.source}.#{good_method}(#{string_literal})"
84
+ new_code = "#{receiver.source}#{node.loc.dot.source}#{good_method}(#{string_literal})"
81
85
 
82
86
  corrector.replace(node, new_code)
83
87
  end
84
88
  end
89
+ # rubocop:enable Metrics/AbcSize
90
+ alias on_csend on_send
85
91
  end
86
92
  end
87
93
  end
@@ -49,6 +49,9 @@ module RuboCop
49
49
  class DeleteSuffix < Base
50
50
  include RegexpMetacharacter
51
51
  extend AutoCorrector
52
+ extend TargetRubyVersion
53
+
54
+ minimum_target_ruby_version 2.5
52
55
 
53
56
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
54
57
  RESTRICT_ON_SEND = %i[gsub gsub! sub sub!].freeze
@@ -61,9 +64,10 @@ module RuboCop
61
64
  }.freeze
62
65
 
63
66
  def_node_matcher :delete_suffix_candidate?, <<~PATTERN
64
- (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 $_))
65
68
  PATTERN
66
69
 
70
+ # rubocop:disable Metrics/AbcSize
67
71
  def on_send(node)
68
72
  return unless (receiver, bad_method, regexp_str, replace_string = delete_suffix_candidate?(node))
69
73
  return unless replace_string.empty?
@@ -77,11 +81,13 @@ module RuboCop
77
81
  regexp_str = interpret_string_escapes(regexp_str)
78
82
  string_literal = to_string_literal(regexp_str)
79
83
 
80
- new_code = "#{receiver.source}.#{good_method}(#{string_literal})"
84
+ new_code = "#{receiver.source}#{node.loc.dot.source}#{good_method}(#{string_literal})"
81
85
 
82
86
  corrector.replace(node, new_code)
83
87
  end
84
88
  end
89
+ # rubocop:enable Metrics/AbcSize
90
+ alias on_csend on_send
85
91
  end
86
92
  end
87
93
  end