rubocop-performance 1.6.1 → 1.7.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.
- checksums.yaml +4 -4
- data/config/default.yml +50 -1
- data/lib/rubocop/cop/mixin/sort_block.rb +28 -0
- data/lib/rubocop/cop/performance/ancestors_include.rb +45 -0
- data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +43 -0
- data/lib/rubocop/cop/performance/io_readlines.rb +127 -0
- data/lib/rubocop/cop/performance/range_include.rb +9 -7
- data/lib/rubocop/cop/performance/redundant_sort_block.rb +53 -0
- data/lib/rubocop/cop/performance/redundant_string_chars.rb +137 -0
- data/lib/rubocop/cop/performance/reverse_first.rb +78 -0
- data/lib/rubocop/cop/performance/size.rb +35 -37
- data/lib/rubocop/cop/performance/sort_reverse.rb +54 -0
- data/lib/rubocop/cop/performance/squeeze.rb +70 -0
- data/lib/rubocop/cop/performance/string_include.rb +57 -0
- data/lib/rubocop/cop/performance_cops.rb +10 -0
- data/lib/rubocop/performance/version.rb +1 -1
- metadata +15 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5be372d62d20424e05201d4b78bb06bf19fc707d845d6b799599ba72958f72b7
|
4
|
+
data.tar.gz: 87c59d78e37de238add70195fa26c95d31b23eb3065695f443a1694d3acf9145
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d6caf119ca1a8b11d829ed555f3a0653a1087d3a3ee3be078460c9ecfb5a5fb236a7abf2c058f57647d4f74dffccc66aebcf4d330f5a6a5bd5deaa12a8fea480
|
7
|
+
data.tar.gz: d8a6e634d4453300d24e5869bffa690457d07048b1e2978cc00dbc930c40e2dc80789a6faa95823e63414e5c579f8784cbe4f743607e2fd9598cf478aa1801ed
|
data/config/default.yml
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# This is the default configuration file.
|
2
2
|
|
3
|
+
Performance/AncestorsInclude:
|
4
|
+
Description: 'Use `A <= B` instead of `A.ancestors.include?(B)`.'
|
5
|
+
Reference: 'https://github.com/JuanitoFatas/fast-ruby#ancestorsinclude-vs--code'
|
6
|
+
Enabled: 'pending'
|
7
|
+
VersionAdded: '1.7'
|
8
|
+
|
9
|
+
Performance/BigDecimalWithNumericArgument:
|
10
|
+
Description: 'Convert numeric argument to string before passing to BigDecimal.'
|
11
|
+
Enabled: 'pending'
|
12
|
+
VersionAdded: '1.7'
|
13
|
+
|
3
14
|
Performance/BindCall:
|
4
15
|
Description: 'Use `bind_call(obj, args, ...)` instead of `bind(obj).call(args, ...)`.'
|
5
16
|
Enabled: true
|
@@ -131,6 +142,12 @@ Performance/InefficientHashSearch:
|
|
131
142
|
VersionAdded: '0.56'
|
132
143
|
Safe: false
|
133
144
|
|
145
|
+
Performance/IoReadlines:
|
146
|
+
Description: 'Use `IO.each_line` (`IO#each_line`) instead of `IO.readlines` (`IO#readlines`).'
|
147
|
+
Reference: 'https://docs.gitlab.com/ee/development/performance.html#reading-from-files-and-other-data-sources'
|
148
|
+
Enabled: false
|
149
|
+
VersionAdded: '1.7'
|
150
|
+
|
134
151
|
Performance/OpenStruct:
|
135
152
|
Description: 'Use `Struct` instead of `OpenStruct`.'
|
136
153
|
Enabled: false
|
@@ -138,10 +155,11 @@ Performance/OpenStruct:
|
|
138
155
|
Safe: false
|
139
156
|
|
140
157
|
Performance/RangeInclude:
|
141
|
-
Description: 'Use `Range#cover?` instead of `Range#include
|
158
|
+
Description: 'Use `Range#cover?` instead of `Range#include?` (or `Range#member?`).'
|
142
159
|
Reference: 'https://github.com/JuanitoFatas/fast-ruby#cover-vs-include-code'
|
143
160
|
Enabled: true
|
144
161
|
VersionAdded: '0.36'
|
162
|
+
VersionChanged: '1.7'
|
145
163
|
Safe: false
|
146
164
|
|
147
165
|
Performance/RedundantBlockCall:
|
@@ -165,6 +183,16 @@ Performance/RedundantMerge:
|
|
165
183
|
# Max number of key-value pairs to consider an offense
|
166
184
|
MaxKeyValuePairs: 2
|
167
185
|
|
186
|
+
Performance/RedundantSortBlock:
|
187
|
+
Description: 'Use `sort` instead of `sort { |a, b| a <=> b }`.'
|
188
|
+
Enabled: 'pending'
|
189
|
+
VersionAdded: '1.7'
|
190
|
+
|
191
|
+
Performance/RedundantStringChars:
|
192
|
+
Description: 'Checks for redundant `String#chars`.'
|
193
|
+
Enabled: 'pending'
|
194
|
+
VersionAdded: '1.7'
|
195
|
+
|
168
196
|
Performance/RegexpMatch:
|
169
197
|
Description: >-
|
170
198
|
Use `match?` instead of `Regexp#match`, `String#match`, `Symbol#match`,
|
@@ -179,6 +207,11 @@ Performance/ReverseEach:
|
|
179
207
|
Enabled: true
|
180
208
|
VersionAdded: '0.30'
|
181
209
|
|
210
|
+
Performance/ReverseFirst:
|
211
|
+
Description: 'Use `last(n).reverse` instead of `reverse.first(n)`.'
|
212
|
+
Enabled: 'pending'
|
213
|
+
VersionAdded: '1.7'
|
214
|
+
|
182
215
|
Performance/Size:
|
183
216
|
Description: >-
|
184
217
|
Use `size` instead of `count` for counting
|
@@ -187,6 +220,17 @@ Performance/Size:
|
|
187
220
|
Enabled: true
|
188
221
|
VersionAdded: '0.30'
|
189
222
|
|
223
|
+
Performance/SortReverse:
|
224
|
+
Description: 'Use `sort.reverse` instead of `sort { |a, b| b <=> a }`.'
|
225
|
+
Enabled: 'pending'
|
226
|
+
VersionAdded: '1.7'
|
227
|
+
|
228
|
+
Performance/Squeeze:
|
229
|
+
Description: "Use `squeeze('a')` instead of `gsub(/a+/, 'a')`."
|
230
|
+
Reference: 'https://github.com/JuanitoFatas/fast-ruby#remove-extra-spaces-or-other-contiguous-characters-code'
|
231
|
+
Enabled: 'pending'
|
232
|
+
VersionAdded: '1.7'
|
233
|
+
|
190
234
|
Performance/StartWith:
|
191
235
|
Description: 'Use `start_with?` instead of a regex match anchored to the beginning of a string.'
|
192
236
|
Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end'
|
@@ -200,6 +244,11 @@ Performance/StartWith:
|
|
200
244
|
VersionAdded: '0.36'
|
201
245
|
VersionChanged: '1.6'
|
202
246
|
|
247
|
+
Performance/StringInclude:
|
248
|
+
Description: 'Use `String#include?` instead of a regex match with literal-only pattern.'
|
249
|
+
Enabled: 'pending'
|
250
|
+
VersionAdded: '1.7'
|
251
|
+
|
203
252
|
Performance/StringReplacement:
|
204
253
|
Description: >-
|
205
254
|
Use `tr` instead of `gsub` when you are replacing the same
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
# Common functionality for cops checking `Enumerable#sort` blocks.
|
6
|
+
module SortBlock
|
7
|
+
extend NodePattern::Macros
|
8
|
+
include RangeHelp
|
9
|
+
|
10
|
+
def_node_matcher :sort_with_block?, <<~PATTERN
|
11
|
+
(block
|
12
|
+
$(send _ :sort)
|
13
|
+
(args (arg $_a) (arg $_b))
|
14
|
+
$send)
|
15
|
+
PATTERN
|
16
|
+
|
17
|
+
def_node_matcher :replaceable_body?, <<~PATTERN
|
18
|
+
(send (lvar %1) :<=> (lvar %2))
|
19
|
+
PATTERN
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def sort_range(send, node)
|
24
|
+
range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop is used to identify usages of `ancestors.include?` and
|
7
|
+
# change them to use `<=` instead.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# A.ancestors.include?(B)
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# A <= B
|
15
|
+
#
|
16
|
+
class AncestorsInclude < Cop
|
17
|
+
include RangeHelp
|
18
|
+
|
19
|
+
MSG = 'Use `<=` instead of `ancestors.include?`.'
|
20
|
+
|
21
|
+
def_node_matcher :ancestors_include_candidate?, <<~PATTERN
|
22
|
+
(send (send $_subclass :ancestors) :include? $_superclass)
|
23
|
+
PATTERN
|
24
|
+
|
25
|
+
def on_send(node)
|
26
|
+
return unless ancestors_include_candidate?(node)
|
27
|
+
|
28
|
+
location_of_ancestors = node.children[0].loc.selector.begin_pos
|
29
|
+
end_location = node.loc.selector.end_pos
|
30
|
+
range = range_between(location_of_ancestors, end_location)
|
31
|
+
|
32
|
+
add_offense(node, location: range)
|
33
|
+
end
|
34
|
+
|
35
|
+
def autocorrect(node)
|
36
|
+
ancestors_include_candidate?(node) do |subclass, superclass|
|
37
|
+
lambda do |corrector|
|
38
|
+
corrector.replace(node, "#{subclass.source} <= #{superclass.source}")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop identifies places where numeric argument to BigDecimal should be
|
7
|
+
# converted to string. Initializing from String is faster
|
8
|
+
# than from Numeric for BigDecimal.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
#
|
12
|
+
# # bad
|
13
|
+
# BigDecimal(1, 2)
|
14
|
+
# BigDecimal(1.2, 3, exception: true)
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# BigDecimal('1', 2)
|
18
|
+
# BigDecimal('1.2', 3, exception: true)
|
19
|
+
#
|
20
|
+
class BigDecimalWithNumericArgument < Cop
|
21
|
+
MSG = 'Convert numeric argument to string before passing to `BigDecimal`.'
|
22
|
+
|
23
|
+
def_node_matcher :big_decimal_with_numeric_argument?, <<~PATTERN
|
24
|
+
(send nil? :BigDecimal $numeric_type? ...)
|
25
|
+
PATTERN
|
26
|
+
|
27
|
+
def on_send(node)
|
28
|
+
big_decimal_with_numeric_argument?(node) do |numeric|
|
29
|
+
add_offense(node, location: numeric.source_range)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def autocorrect(node)
|
34
|
+
big_decimal_with_numeric_argument?(node) do |numeric|
|
35
|
+
lambda do |corrector|
|
36
|
+
corrector.wrap(numeric, "'", "'")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop identifies places where inefficient `readlines` method
|
7
|
+
# can be replaced by `each_line` to avoid fully loading file content into memory.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# # bad
|
12
|
+
# File.readlines('testfile').each { |l| puts l }
|
13
|
+
# IO.readlines('testfile', chomp: true).each { |l| puts l }
|
14
|
+
#
|
15
|
+
# conn.readlines(10).map { |l| l.size }
|
16
|
+
# file.readlines.find { |l| l.start_with?('#') }
|
17
|
+
# file.readlines.each { |l| puts l }
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# File.open('testfile', 'r').each_line { |l| puts l }
|
21
|
+
# IO.open('testfile').each_line(chomp: true) { |l| puts l }
|
22
|
+
#
|
23
|
+
# conn.each_line(10).map { |l| l.size }
|
24
|
+
# file.each_line.find { |l| l.start_with?('#') }
|
25
|
+
# file.each_line { |l| puts l }
|
26
|
+
#
|
27
|
+
class IoReadlines < Cop
|
28
|
+
include RangeHelp
|
29
|
+
|
30
|
+
MSG = 'Use `%<good>s` instead of `%<bad>s`.'
|
31
|
+
ENUMERABLE_METHODS = (Enumerable.instance_methods + [:each]).freeze
|
32
|
+
|
33
|
+
def_node_matcher :readlines_on_class?, <<~PATTERN
|
34
|
+
$(send $(send (const nil? {:IO :File}) :readlines ...) #enumerable_method?)
|
35
|
+
PATTERN
|
36
|
+
|
37
|
+
def_node_matcher :readlines_on_instance?, <<~PATTERN
|
38
|
+
$(send $(send ${nil? !const_type?} :readlines ...) #enumerable_method? ...)
|
39
|
+
PATTERN
|
40
|
+
|
41
|
+
def on_send(node)
|
42
|
+
readlines_on_class?(node) do |enumerable_call, readlines_call|
|
43
|
+
offense(node, enumerable_call, readlines_call)
|
44
|
+
end
|
45
|
+
|
46
|
+
readlines_on_instance?(node) do |enumerable_call, readlines_call, _|
|
47
|
+
offense(node, enumerable_call, readlines_call)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def autocorrect(node)
|
52
|
+
readlines_on_instance?(node) do |enumerable_call, readlines_call, receiver|
|
53
|
+
# We cannot safely correct `.readlines` method called on IO/File classes
|
54
|
+
# due to its signature and we are not sure with implicit receiver
|
55
|
+
# if it is called in the context of some instance or mentioned class.
|
56
|
+
return if receiver.nil?
|
57
|
+
|
58
|
+
lambda do |corrector|
|
59
|
+
range = correction_range(enumerable_call, readlines_call)
|
60
|
+
|
61
|
+
if readlines_call.arguments?
|
62
|
+
call_args = build_call_args(readlines_call.arguments)
|
63
|
+
replacement = "each_line(#{call_args})"
|
64
|
+
else
|
65
|
+
replacement = 'each_line'
|
66
|
+
end
|
67
|
+
|
68
|
+
corrector.replace(range, replacement)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def enumerable_method?(node)
|
76
|
+
ENUMERABLE_METHODS.include?(node.to_sym)
|
77
|
+
end
|
78
|
+
|
79
|
+
def offense(node, enumerable_call, readlines_call)
|
80
|
+
range = offense_range(enumerable_call, readlines_call)
|
81
|
+
good_method = build_good_method(enumerable_call)
|
82
|
+
bad_method = build_bad_method(enumerable_call)
|
83
|
+
|
84
|
+
add_offense(
|
85
|
+
node,
|
86
|
+
location: range,
|
87
|
+
message: format(MSG, good: good_method, bad: bad_method)
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
def offense_range(enumerable_call, readlines_call)
|
92
|
+
readlines_pos = readlines_call.loc.selector.begin_pos
|
93
|
+
enumerable_pos = enumerable_call.loc.selector.end_pos
|
94
|
+
range_between(readlines_pos, enumerable_pos)
|
95
|
+
end
|
96
|
+
|
97
|
+
def build_good_method(enumerable_call)
|
98
|
+
if enumerable_call.method?(:each)
|
99
|
+
'each_line'
|
100
|
+
else
|
101
|
+
"each_line.#{enumerable_call.method_name}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def build_bad_method(enumerable_call)
|
106
|
+
"readlines.#{enumerable_call.method_name}"
|
107
|
+
end
|
108
|
+
|
109
|
+
def correction_range(enumerable_call, readlines_call)
|
110
|
+
begin_pos = readlines_call.loc.selector.begin_pos
|
111
|
+
|
112
|
+
end_pos = if enumerable_call.method?(:each)
|
113
|
+
enumerable_call.loc.expression.end_pos
|
114
|
+
else
|
115
|
+
enumerable_call.loc.dot.begin_pos
|
116
|
+
end
|
117
|
+
|
118
|
+
range_between(begin_pos, end_pos)
|
119
|
+
end
|
120
|
+
|
121
|
+
def build_call_args(call_args_node)
|
122
|
+
call_args_node.map(&:source).join(', ')
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -3,18 +3,19 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Performance
|
6
|
-
# This cop identifies uses of `Range#include?`, which iterates over each
|
6
|
+
# This cop identifies uses of `Range#include?` and `Range#member?`, which iterates over each
|
7
7
|
# item in a `Range` to see if a specified item is there. In contrast,
|
8
8
|
# `Range#cover?` simply compares the target item with the beginning and
|
9
9
|
# end points of the `Range`. In a great majority of cases, this is what
|
10
10
|
# is wanted.
|
11
11
|
#
|
12
|
-
# This cop is `Safe: false` by default because `Range#include?` and
|
12
|
+
# This cop is `Safe: false` by default because `Range#include?` (or `Range#member?`) and
|
13
13
|
# `Range#cover?` are not equivalent behaviour.
|
14
14
|
#
|
15
15
|
# @example
|
16
16
|
# # bad
|
17
17
|
# ('a'..'z').include?('b') # => true
|
18
|
+
# ('a'..'z').member?('b') # => true
|
18
19
|
#
|
19
20
|
# # good
|
20
21
|
# ('a'..'z').cover?('b') # => true
|
@@ -24,7 +25,7 @@ module RuboCop
|
|
24
25
|
#
|
25
26
|
# ('a'..'z').cover?('yellow') # => true
|
26
27
|
class RangeInclude < Cop
|
27
|
-
MSG = 'Use `Range#cover?` instead of `Range
|
28
|
+
MSG = 'Use `Range#cover?` instead of `Range#%<bad_method>s`.'
|
28
29
|
|
29
30
|
# TODO: If we traced out assignments of variables to their uses, we
|
30
31
|
# might pick up on a few more instances of this issue
|
@@ -32,13 +33,14 @@ module RuboCop
|
|
32
33
|
# (We don't even catch it if the Range is in double parens)
|
33
34
|
|
34
35
|
def_node_matcher :range_include, <<~PATTERN
|
35
|
-
(send {irange erange (begin {irange erange})} :include? ...)
|
36
|
+
(send {irange erange (begin {irange erange})} ${:include? :member?} ...)
|
36
37
|
PATTERN
|
37
38
|
|
38
39
|
def on_send(node)
|
39
|
-
|
40
|
-
|
41
|
-
|
40
|
+
range_include(node) do |bad_method|
|
41
|
+
message = format(MSG, bad_method: bad_method)
|
42
|
+
add_offense(node, location: :selector, message: message)
|
43
|
+
end
|
42
44
|
end
|
43
45
|
|
44
46
|
def autocorrect(node)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop identifies places where `sort { |a, b| a <=> b }`
|
7
|
+
# can be replaced with `sort`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# array.sort { |a, b| a <=> b }
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# array.sort
|
15
|
+
#
|
16
|
+
class RedundantSortBlock < Cop
|
17
|
+
include SortBlock
|
18
|
+
|
19
|
+
MSG = 'Use `sort` instead of `%<bad_method>s`.'
|
20
|
+
|
21
|
+
def on_block(node)
|
22
|
+
sort_with_block?(node) do |send, var_a, var_b, body|
|
23
|
+
replaceable_body?(body, var_a, var_b) do
|
24
|
+
range = sort_range(send, node)
|
25
|
+
|
26
|
+
add_offense(
|
27
|
+
node,
|
28
|
+
location: range,
|
29
|
+
message: message(var_a, var_b)
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def autocorrect(node)
|
36
|
+
sort_with_block?(node) do |send, _var_a, _var_b, _body|
|
37
|
+
lambda do |corrector|
|
38
|
+
range = sort_range(send, node)
|
39
|
+
corrector.replace(range, 'sort')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def message(var_a, var_b)
|
47
|
+
bad_method = "sort { |#{var_a}, #{var_b}| #{var_a} <=> #{var_b} }"
|
48
|
+
format(MSG, bad_method: bad_method)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop checks for redundant `String#chars`.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# str.chars[0..2]
|
11
|
+
# str.chars.slice(0..2)
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# str[0..2].chars
|
15
|
+
#
|
16
|
+
# # bad
|
17
|
+
# str.chars.first
|
18
|
+
# str.chars.first(2)
|
19
|
+
# str.chars.last
|
20
|
+
# str.chars.last(2)
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# str[0]
|
24
|
+
# str[0...2].chars
|
25
|
+
# str[-1]
|
26
|
+
# str[-2..-1].chars
|
27
|
+
#
|
28
|
+
# # bad
|
29
|
+
# str.chars.take(2)
|
30
|
+
# str.chars.drop(2)
|
31
|
+
# str.chars.length
|
32
|
+
# str.chars.size
|
33
|
+
# str.chars.empty?
|
34
|
+
#
|
35
|
+
# # good
|
36
|
+
# str[0...2].chars
|
37
|
+
# str[2..-1].chars
|
38
|
+
# str.length
|
39
|
+
# str.size
|
40
|
+
# str.empty?
|
41
|
+
#
|
42
|
+
class RedundantStringChars < Cop
|
43
|
+
include RangeHelp
|
44
|
+
|
45
|
+
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
46
|
+
REPLACEABLE_METHODS = %i[[] slice first last take drop length size empty?].freeze
|
47
|
+
|
48
|
+
def_node_matcher :redundant_chars_call?, <<~PATTERN
|
49
|
+
(send $(send _ :chars) $#replaceable_method? $...)
|
50
|
+
PATTERN
|
51
|
+
|
52
|
+
def on_send(node)
|
53
|
+
redundant_chars_call?(node) do |receiver, method, args|
|
54
|
+
range = offense_range(receiver, node)
|
55
|
+
message = build_message(method, args)
|
56
|
+
add_offense(node, location: range, message: message)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def autocorrect(node)
|
61
|
+
redundant_chars_call?(node) do |receiver, method, args|
|
62
|
+
range = correction_range(receiver, node)
|
63
|
+
replacement = build_good_method(method, args)
|
64
|
+
|
65
|
+
lambda do |corrector|
|
66
|
+
corrector.replace(range, replacement)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def replaceable_method?(method_name)
|
74
|
+
REPLACEABLE_METHODS.include?(method_name)
|
75
|
+
end
|
76
|
+
|
77
|
+
def offense_range(receiver, node)
|
78
|
+
range_between(receiver.loc.selector.begin_pos, node.loc.expression.end_pos)
|
79
|
+
end
|
80
|
+
|
81
|
+
def correction_range(receiver, node)
|
82
|
+
range_between(receiver.loc.dot.begin_pos, node.loc.expression.end_pos)
|
83
|
+
end
|
84
|
+
|
85
|
+
def build_message(method, args)
|
86
|
+
good_method = build_good_method(method, args)
|
87
|
+
bad_method = build_bad_method(method, args)
|
88
|
+
format(MSG, good_method: good_method, bad_method: bad_method)
|
89
|
+
end
|
90
|
+
|
91
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
92
|
+
def build_good_method(method, args)
|
93
|
+
case method
|
94
|
+
when :[], :slice
|
95
|
+
"[#{build_call_args(args)}].chars"
|
96
|
+
when :first
|
97
|
+
if args.any?
|
98
|
+
"[0...#{args.first.source}].chars"
|
99
|
+
else
|
100
|
+
'[0]'
|
101
|
+
end
|
102
|
+
when :last
|
103
|
+
if args.any?
|
104
|
+
"[-#{args.first.source}..-1].chars"
|
105
|
+
else
|
106
|
+
'[-1]'
|
107
|
+
end
|
108
|
+
when :take
|
109
|
+
"[0...#{args.first.source}].chars"
|
110
|
+
when :drop
|
111
|
+
"[#{args.first.source}..-1].chars"
|
112
|
+
else
|
113
|
+
".#{method}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
117
|
+
|
118
|
+
def build_bad_method(method, args)
|
119
|
+
case method
|
120
|
+
when :[]
|
121
|
+
"chars[#{build_call_args(args)}]"
|
122
|
+
else
|
123
|
+
if args.any?
|
124
|
+
"chars.#{method}(#{build_call_args(args)})"
|
125
|
+
else
|
126
|
+
"chars.#{method}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def build_call_args(call_args_node)
|
132
|
+
call_args_node.map(&:source).join(', ')
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop identifies places where `reverse.first(n)` and `reverse.first`
|
7
|
+
# can be replaced by `last(n).reverse` and `last`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# # bad
|
12
|
+
# array.reverse.first(5)
|
13
|
+
# array.reverse.first
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# array.last(5).reverse
|
17
|
+
# array.last
|
18
|
+
#
|
19
|
+
class ReverseFirst < Cop
|
20
|
+
include RangeHelp
|
21
|
+
|
22
|
+
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
23
|
+
|
24
|
+
def_node_matcher :reverse_first_candidate?, <<~PATTERN
|
25
|
+
(send $(send _ :reverse) :first (int _)?)
|
26
|
+
PATTERN
|
27
|
+
|
28
|
+
def on_send(node)
|
29
|
+
reverse_first_candidate?(node) do |receiver|
|
30
|
+
range = correction_range(receiver, node)
|
31
|
+
message = build_message(node)
|
32
|
+
|
33
|
+
add_offense(node, location: range, message: message)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def autocorrect(node)
|
38
|
+
reverse_first_candidate?(node) do |receiver|
|
39
|
+
range = correction_range(receiver, node)
|
40
|
+
replacement = build_good_method(node)
|
41
|
+
|
42
|
+
lambda do |corrector|
|
43
|
+
corrector.replace(range, replacement)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def correction_range(receiver, node)
|
51
|
+
range_between(receiver.loc.selector.begin_pos, node.loc.expression.end_pos)
|
52
|
+
end
|
53
|
+
|
54
|
+
def build_message(node)
|
55
|
+
good_method = build_good_method(node)
|
56
|
+
bad_method = build_bad_method(node)
|
57
|
+
format(MSG, good_method: good_method, bad_method: bad_method)
|
58
|
+
end
|
59
|
+
|
60
|
+
def build_good_method(node)
|
61
|
+
if node.arguments?
|
62
|
+
"last(#{node.arguments.first.source}).reverse"
|
63
|
+
else
|
64
|
+
'last'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def build_bad_method(node)
|
69
|
+
if node.arguments?
|
70
|
+
"reverse.first(#{node.arguments.first.source})"
|
71
|
+
else
|
72
|
+
'reverse.first'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -9,15 +9,27 @@ module RuboCop
|
|
9
9
|
# @example
|
10
10
|
# # bad
|
11
11
|
# [1, 2, 3].count
|
12
|
+
# (1..3).to_a.count
|
13
|
+
# Array[*1..3].count
|
14
|
+
# Array(1..3).count
|
12
15
|
#
|
13
16
|
# # bad
|
14
17
|
# {a: 1, b: 2, c: 3}.count
|
18
|
+
# [[:foo, :bar], [1, 2]].to_h.count
|
19
|
+
# Hash[*('a'..'z')].count
|
20
|
+
# Hash(key: :value).count
|
15
21
|
#
|
16
22
|
# # good
|
17
23
|
# [1, 2, 3].size
|
24
|
+
# (1..3).to_a.size
|
25
|
+
# Array[*1..3].size
|
26
|
+
# Array(1..3).size
|
18
27
|
#
|
19
28
|
# # good
|
20
29
|
# {a: 1, b: 2, c: 3}.size
|
30
|
+
# [[:foo, :bar], [1, 2]].to_h.size
|
31
|
+
# Hash[*('a'..'z')].size
|
32
|
+
# Hash(key: :value).size
|
21
33
|
#
|
22
34
|
# # good
|
23
35
|
# [1, 2, 3].count { |e| e > 2 }
|
@@ -26,8 +38,30 @@ module RuboCop
|
|
26
38
|
class Size < Cop
|
27
39
|
MSG = 'Use `size` instead of `count`.'
|
28
40
|
|
41
|
+
def_node_matcher :array?, <<~PATTERN
|
42
|
+
{
|
43
|
+
[!nil? array_type?]
|
44
|
+
(send _ :to_a)
|
45
|
+
(send (const nil? :Array) :[] _)
|
46
|
+
(send nil? :Array _)
|
47
|
+
}
|
48
|
+
PATTERN
|
49
|
+
|
50
|
+
def_node_matcher :hash?, <<~PATTERN
|
51
|
+
{
|
52
|
+
[!nil? hash_type?]
|
53
|
+
(send _ :to_h)
|
54
|
+
(send (const nil? :Hash) :[] _)
|
55
|
+
(send nil? :Hash _)
|
56
|
+
}
|
57
|
+
PATTERN
|
58
|
+
|
59
|
+
def_node_matcher :count?, <<~PATTERN
|
60
|
+
(send {#array? #hash?} :count)
|
61
|
+
PATTERN
|
62
|
+
|
29
63
|
def on_send(node)
|
30
|
-
return
|
64
|
+
return if node.parent&.block_type? || !count?(node)
|
31
65
|
|
32
66
|
add_offense(node, location: :selector)
|
33
67
|
end
|
@@ -35,42 +69,6 @@ module RuboCop
|
|
35
69
|
def autocorrect(node)
|
36
70
|
->(corrector) { corrector.replace(node.loc.selector, 'size') }
|
37
71
|
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def eligible_node?(node)
|
42
|
-
return false unless node.method?(:count) && !node.arguments?
|
43
|
-
|
44
|
-
eligible_receiver?(node.receiver) && !allowed_parent?(node.parent)
|
45
|
-
end
|
46
|
-
|
47
|
-
def eligible_receiver?(node)
|
48
|
-
return false unless node
|
49
|
-
|
50
|
-
array?(node) || hash?(node)
|
51
|
-
end
|
52
|
-
|
53
|
-
def allowed_parent?(node)
|
54
|
-
node&.block_type?
|
55
|
-
end
|
56
|
-
|
57
|
-
def array?(node)
|
58
|
-
return true if node.array_type?
|
59
|
-
return false unless node.send_type?
|
60
|
-
|
61
|
-
_, constant = *node.receiver
|
62
|
-
|
63
|
-
constant == :Array || node.method?(:to_a)
|
64
|
-
end
|
65
|
-
|
66
|
-
def hash?(node)
|
67
|
-
return true if node.hash_type?
|
68
|
-
return false unless node.send_type?
|
69
|
-
|
70
|
-
_, constant = *node.receiver
|
71
|
-
|
72
|
-
constant == :Hash || node.method?(:to_h)
|
73
|
-
end
|
74
72
|
end
|
75
73
|
end
|
76
74
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop identifies places where `sort { |a, b| b <=> a }`
|
7
|
+
# can be replaced by a faster `sort.reverse`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# array.sort { |a, b| b <=> a }
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# array.sort.reverse
|
15
|
+
#
|
16
|
+
class SortReverse < Cop
|
17
|
+
include SortBlock
|
18
|
+
|
19
|
+
MSG = 'Use `sort.reverse` instead of `%<bad_method>s`.'
|
20
|
+
|
21
|
+
def on_block(node)
|
22
|
+
sort_with_block?(node) do |send, var_a, var_b, body|
|
23
|
+
replaceable_body?(body, var_b, var_a) do
|
24
|
+
range = sort_range(send, node)
|
25
|
+
|
26
|
+
add_offense(
|
27
|
+
node,
|
28
|
+
location: range,
|
29
|
+
message: message(var_a, var_b)
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def autocorrect(node)
|
36
|
+
sort_with_block?(node) do |send, _var_a, _var_b, _body|
|
37
|
+
lambda do |corrector|
|
38
|
+
range = sort_range(send, node)
|
39
|
+
replacement = 'sort.reverse'
|
40
|
+
corrector.replace(range, replacement)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def message(var_a, var_b)
|
48
|
+
bad_method = "sort { |#{var_a}, #{var_b}| #{var_b} <=> #{var_a} }"
|
49
|
+
format(MSG, bad_method: bad_method)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop identifies places where `gsub(/a+/, 'a')` and `gsub!(/a+/, 'a')`
|
7
|
+
# can be replaced by `squeeze('a')` and `squeeze!('a')`.
|
8
|
+
#
|
9
|
+
# The `squeeze('a')` method is faster than `gsub(/a+/, 'a')`.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
#
|
13
|
+
# # bad
|
14
|
+
# str.gsub(/a+/, 'a')
|
15
|
+
# str.gsub!(/a+/, 'a')
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# str.squeeze('a')
|
19
|
+
# str.squeeze!('a')
|
20
|
+
#
|
21
|
+
class Squeeze < Cop
|
22
|
+
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
23
|
+
|
24
|
+
PREFERRED_METHODS = {
|
25
|
+
gsub: :squeeze,
|
26
|
+
gsub!: :squeeze!
|
27
|
+
}.freeze
|
28
|
+
|
29
|
+
def_node_matcher :squeeze_candidate?, <<~PATTERN
|
30
|
+
(send
|
31
|
+
$!nil? ${:gsub :gsub!}
|
32
|
+
(regexp
|
33
|
+
(str $#repeating_literal?)
|
34
|
+
(regopt))
|
35
|
+
(str $_))
|
36
|
+
PATTERN
|
37
|
+
|
38
|
+
def on_send(node)
|
39
|
+
squeeze_candidate?(node) do |_, bad_method, regexp_str, replace_str|
|
40
|
+
regexp_str = regexp_str[0..-2] # delete '+' from the end
|
41
|
+
regexp_str = interpret_string_escapes(regexp_str)
|
42
|
+
return unless replace_str == regexp_str
|
43
|
+
|
44
|
+
good_method = PREFERRED_METHODS[bad_method]
|
45
|
+
message = format(MSG, current: bad_method, prefer: good_method)
|
46
|
+
add_offense(node, location: :selector, message: message)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def autocorrect(node)
|
51
|
+
squeeze_candidate?(node) do |receiver, bad_method, _regexp_str, replace_str|
|
52
|
+
lambda do |corrector|
|
53
|
+
good_method = PREFERRED_METHODS[bad_method]
|
54
|
+
string_literal = to_string_literal(replace_str)
|
55
|
+
|
56
|
+
new_code = "#{receiver.source}.#{good_method}(#{string_literal})"
|
57
|
+
corrector.replace(node.source_range, new_code)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def repeating_literal?(regex_str)
|
65
|
+
regex_str.match?(/\A(?:#{Util::LITERAL_REGEX})\+\z/)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop identifies unnecessary use of a regex where
|
7
|
+
# `String#include?` would suffice.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# 'abc'.match?(/ab/)
|
12
|
+
# /ab/.match?('abc')
|
13
|
+
# 'abc' =~ /ab/
|
14
|
+
# /ab/ =~ 'abc'
|
15
|
+
# 'abc'.match(/ab/)
|
16
|
+
# /ab/.match('abc')
|
17
|
+
#
|
18
|
+
# # good
|
19
|
+
# 'abc'.include?('ab')
|
20
|
+
class StringInclude < Cop
|
21
|
+
MSG = 'Use `String#include?` instead of a regex match with literal-only pattern.'
|
22
|
+
|
23
|
+
def_node_matcher :redundant_regex?, <<~PATTERN
|
24
|
+
{(send $!nil? {:match :=~ :match?} (regexp (str $#literal?) (regopt)))
|
25
|
+
(send (regexp (str $#literal?) (regopt)) {:match :match?} $str)
|
26
|
+
(match-with-lvasgn (regexp (str $#literal?) (regopt)) $_)}
|
27
|
+
PATTERN
|
28
|
+
|
29
|
+
def on_send(node)
|
30
|
+
return unless redundant_regex?(node)
|
31
|
+
|
32
|
+
add_offense(node)
|
33
|
+
end
|
34
|
+
alias on_match_with_lvasgn on_send
|
35
|
+
|
36
|
+
def autocorrect(node)
|
37
|
+
redundant_regex?(node) do |receiver, regex_str|
|
38
|
+
receiver, regex_str = regex_str, receiver if receiver.is_a?(String)
|
39
|
+
regex_str = interpret_string_escapes(regex_str)
|
40
|
+
|
41
|
+
lambda do |corrector|
|
42
|
+
new_source = receiver.source + '.include?(' +
|
43
|
+
to_string_literal(regex_str) + ')'
|
44
|
+
corrector.replace(node.source_range, new_source)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def literal?(regex_str)
|
52
|
+
regex_str.match?(/\A#{Util::LITERAL_REGEX}+\z/)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -1,7 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'mixin/regexp_metacharacter'
|
4
|
+
require_relative 'mixin/sort_block'
|
4
5
|
|
6
|
+
require_relative 'performance/ancestors_include'
|
7
|
+
require_relative 'performance/big_decimal_with_numeric_argument'
|
5
8
|
require_relative 'performance/bind_call'
|
6
9
|
require_relative 'performance/caller'
|
7
10
|
require_relative 'performance/case_when_splat'
|
@@ -18,13 +21,20 @@ require_relative 'performance/flat_map'
|
|
18
21
|
require_relative 'performance/inefficient_hash_search'
|
19
22
|
require_relative 'performance/open_struct'
|
20
23
|
require_relative 'performance/range_include'
|
24
|
+
require_relative 'performance/io_readlines'
|
21
25
|
require_relative 'performance/redundant_block_call'
|
22
26
|
require_relative 'performance/redundant_match'
|
23
27
|
require_relative 'performance/redundant_merge'
|
28
|
+
require_relative 'performance/redundant_sort_block'
|
29
|
+
require_relative 'performance/redundant_string_chars'
|
24
30
|
require_relative 'performance/regexp_match'
|
25
31
|
require_relative 'performance/reverse_each'
|
32
|
+
require_relative 'performance/reverse_first'
|
26
33
|
require_relative 'performance/size'
|
34
|
+
require_relative 'performance/sort_reverse'
|
35
|
+
require_relative 'performance/squeeze'
|
27
36
|
require_relative 'performance/start_with'
|
37
|
+
require_relative 'performance/string_include'
|
28
38
|
require_relative 'performance/string_replacement'
|
29
39
|
require_relative 'performance/times_map'
|
30
40
|
require_relative 'performance/unfreeze_string'
|
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.
|
4
|
+
version: 1.7.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: 2020-
|
13
|
+
date: 2020-07-07 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rubocop
|
@@ -18,14 +18,14 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ">="
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.
|
21
|
+
version: 0.82.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
26
|
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
version: 0.
|
28
|
+
version: 0.82.0
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: simplecov
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
@@ -55,6 +55,9 @@ files:
|
|
55
55
|
- config/default.yml
|
56
56
|
- lib/rubocop-performance.rb
|
57
57
|
- lib/rubocop/cop/mixin/regexp_metacharacter.rb
|
58
|
+
- lib/rubocop/cop/mixin/sort_block.rb
|
59
|
+
- lib/rubocop/cop/performance/ancestors_include.rb
|
60
|
+
- lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb
|
58
61
|
- lib/rubocop/cop/performance/bind_call.rb
|
59
62
|
- lib/rubocop/cop/performance/caller.rb
|
60
63
|
- lib/rubocop/cop/performance/case_when_splat.rb
|
@@ -70,15 +73,22 @@ files:
|
|
70
73
|
- lib/rubocop/cop/performance/fixed_size.rb
|
71
74
|
- lib/rubocop/cop/performance/flat_map.rb
|
72
75
|
- lib/rubocop/cop/performance/inefficient_hash_search.rb
|
76
|
+
- lib/rubocop/cop/performance/io_readlines.rb
|
73
77
|
- lib/rubocop/cop/performance/open_struct.rb
|
74
78
|
- lib/rubocop/cop/performance/range_include.rb
|
75
79
|
- lib/rubocop/cop/performance/redundant_block_call.rb
|
76
80
|
- lib/rubocop/cop/performance/redundant_match.rb
|
77
81
|
- lib/rubocop/cop/performance/redundant_merge.rb
|
82
|
+
- lib/rubocop/cop/performance/redundant_sort_block.rb
|
83
|
+
- lib/rubocop/cop/performance/redundant_string_chars.rb
|
78
84
|
- lib/rubocop/cop/performance/regexp_match.rb
|
79
85
|
- lib/rubocop/cop/performance/reverse_each.rb
|
86
|
+
- lib/rubocop/cop/performance/reverse_first.rb
|
80
87
|
- lib/rubocop/cop/performance/size.rb
|
88
|
+
- lib/rubocop/cop/performance/sort_reverse.rb
|
89
|
+
- lib/rubocop/cop/performance/squeeze.rb
|
81
90
|
- lib/rubocop/cop/performance/start_with.rb
|
91
|
+
- lib/rubocop/cop/performance/string_include.rb
|
82
92
|
- lib/rubocop/cop/performance/string_replacement.rb
|
83
93
|
- lib/rubocop/cop/performance/times_map.rb
|
84
94
|
- lib/rubocop/cop/performance/unfreeze_string.rb
|
@@ -111,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
121
|
- !ruby/object:Gem::Version
|
112
122
|
version: '0'
|
113
123
|
requirements: []
|
114
|
-
rubygems_version: 3.1.
|
124
|
+
rubygems_version: 3.1.4
|
115
125
|
signing_key:
|
116
126
|
specification_version: 4
|
117
127
|
summary: Automatic performance checking tool for Ruby code.
|