rubocop-performance 1.13.3 → 1.19.0

Sign up to get free protection for your applications and to get access to all the features.
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