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.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +2 -2
- data/config/default.yml +13 -2
- data/lib/rubocop/cop/mixin/regexp_metacharacter.rb +2 -2
- data/lib/rubocop/cop/mixin/sort_block.rb +7 -0
- data/lib/rubocop/cop/performance/ancestors_include.rb +1 -2
- data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +3 -2
- data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +2 -6
- data/lib/rubocop/cop/performance/bind_call.rb +1 -2
- data/lib/rubocop/cop/performance/block_given_with_explicit_block.rb +1 -1
- data/lib/rubocop/cop/performance/caller.rb +1 -2
- data/lib/rubocop/cop/performance/case_when_splat.rb +7 -13
- data/lib/rubocop/cop/performance/casecmp.rb +10 -12
- data/lib/rubocop/cop/performance/chain_array_allocation.rb +5 -5
- data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +4 -6
- data/lib/rubocop/cop/performance/compare_with_block.rb +20 -11
- data/lib/rubocop/cop/performance/concurrent_monotonic_time.rb +1 -1
- data/lib/rubocop/cop/performance/constant_regexp.rb +6 -4
- data/lib/rubocop/cop/performance/count.rb +39 -3
- data/lib/rubocop/cop/performance/delete_prefix.rb +8 -2
- data/lib/rubocop/cop/performance/delete_suffix.rb +8 -2
- data/lib/rubocop/cop/performance/detect.rb +7 -6
- data/lib/rubocop/cop/performance/double_start_end_with.rb +4 -5
- data/lib/rubocop/cop/performance/end_with.rb +7 -6
- data/lib/rubocop/cop/performance/fixed_size.rb +2 -2
- data/lib/rubocop/cop/performance/flat_map.rb +7 -5
- data/lib/rubocop/cop/performance/inefficient_hash_search.rb +17 -15
- data/lib/rubocop/cop/performance/io_readlines.rb +2 -2
- data/lib/rubocop/cop/performance/map_compact.rb +9 -4
- data/lib/rubocop/cop/performance/map_method_chain.rb +87 -0
- data/lib/rubocop/cop/performance/method_object_as_block.rb +1 -1
- data/lib/rubocop/cop/performance/open_struct.rb +2 -3
- data/lib/rubocop/cop/performance/range_include.rb +2 -2
- data/lib/rubocop/cop/performance/redundant_block_call.rb +2 -2
- data/lib/rubocop/cop/performance/redundant_equality_comparison_block.rb +38 -3
- data/lib/rubocop/cop/performance/redundant_match.rb +10 -9
- data/lib/rubocop/cop/performance/redundant_merge.rb +9 -16
- data/lib/rubocop/cop/performance/redundant_sort_block.rb +17 -10
- data/lib/rubocop/cop/performance/redundant_split_regexp_argument.rb +3 -2
- data/lib/rubocop/cop/performance/redundant_string_chars.rb +10 -6
- data/lib/rubocop/cop/performance/regexp_match.rb +23 -24
- data/lib/rubocop/cop/performance/reverse_each.rb +3 -3
- data/lib/rubocop/cop/performance/reverse_first.rb +4 -3
- data/lib/rubocop/cop/performance/select_map.rb +2 -1
- data/lib/rubocop/cop/performance/size.rb +1 -2
- data/lib/rubocop/cop/performance/sort_reverse.rb +19 -10
- data/lib/rubocop/cop/performance/squeeze.rb +8 -8
- data/lib/rubocop/cop/performance/start_with.rb +7 -6
- data/lib/rubocop/cop/performance/string_identifier_argument.rb +16 -11
- data/lib/rubocop/cop/performance/string_include.rb +23 -17
- data/lib/rubocop/cop/performance/string_replacement.rb +7 -10
- data/lib/rubocop/cop/performance/sum.rb +7 -4
- data/lib/rubocop/cop/performance/times_map.rb +6 -7
- data/lib/rubocop/cop/performance/uri_default_parser.rb +4 -6
- data/lib/rubocop/cop/performance_cops.rb +1 -0
- data/lib/rubocop/performance/version.rb +1 -1
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 06f3e8346dc7f9be72a1f18c62a25dc02aeac41debcfe3c58df68e2a6904df43
|
4
|
+
data.tar.gz: f01facdb92a3056d05990e2ed2b6b1920de0501edf53a08e95e08620651c8557
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07eb541698b6ccf2985ef62392bd78703bf6607e2877ef3865ab1762d62910c4c346fbb7e9bc48af7b7d98b0e1ad22e8c79ea2425555fbe75c2a5e614837eaa7
|
7
|
+
data.tar.gz: 60fc9f900906cca518d0da62170f71ae6e5f5ffd44f4c1cbe75af83f9a1fef252b6bab95d3af4f49f328d4a856cd17c7f47621ee62575ab282eacfd26dc09841
|
data/LICENSE.txt
CHANGED
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 `
|
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: '
|
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
|
57
|
+
regexp_string[2..] # drop `\A` anchor
|
58
58
|
else
|
59
|
-
regexp_string[1
|
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
|
-
#
|
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
|
-
#
|
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
|
-
(
|
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
|
-
#
|
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
|
@@ -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
|
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].
|
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
|
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
|
-
#
|
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
|
-
|
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
|
85
|
-
corrector.
|
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
|
-
#
|
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
|
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
|
-
#
|
9
|
-
#
|
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
|
-
#
|
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 `
|
8
|
+
# This cop also checks `sort!`, `min`, `max` and `minmax` methods.
|
9
9
|
#
|
10
10
|
# @example
|
11
11
|
# # bad
|
12
|
-
# array.sort
|
13
|
-
# array.
|
14
|
-
# array.
|
15
|
-
# array.
|
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 `%<
|
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}
|
64
|
+
"#{REPLACEMENT[send.method_name]} { |a| a[#{args_a.first.source}] }"
|
58
65
|
else
|
59
|
-
"#{send.method_name}
|
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
|
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
|
-
#
|
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
|
-
#
|
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
|
-
(
|
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}
|
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
|
-
(
|
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}
|
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
|