rubocop-performance 1.9.1 → 1.11.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/LICENSE.txt +1 -1
- data/README.md +3 -2
- data/config/default.yml +32 -6
- data/config/obsoletion.yml +7 -0
- data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +1 -1
- data/lib/rubocop/cop/performance/bind_call.rb +2 -2
- data/lib/rubocop/cop/performance/chain_array_allocation.rb +2 -0
- data/lib/rubocop/cop/performance/constant_regexp.rb +12 -7
- data/lib/rubocop/cop/performance/flat_map.rb +2 -1
- data/lib/rubocop/cop/performance/map_compact.rb +65 -0
- data/lib/rubocop/cop/performance/redundant_block_call.rb +7 -1
- data/lib/rubocop/cop/performance/redundant_equality_comparison_block.rb +89 -0
- data/lib/rubocop/cop/performance/redundant_merge.rb +3 -0
- data/lib/rubocop/cop/performance/redundant_split_regexp_argument.rb +64 -0
- data/lib/rubocop/cop/performance/reverse_each.rb +23 -11
- data/lib/rubocop/cop/performance/select_map.rb +60 -0
- data/lib/rubocop/cop/performance/sum.rb +8 -2
- data/lib/rubocop/cop/performance_cops.rb +4 -0
- data/lib/rubocop/performance.rb +2 -0
- data/lib/rubocop/performance/version.rb +1 -1
- metadata +16 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7398f6e66937ee06d17e56c52ebe2858d26fcaf0a91d00d0c90caccbd1722b6
|
4
|
+
data.tar.gz: ab180bb4e81913bdabd470b04264e91b9687edde72f6f6d126e9918a11f361ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ae6caa06f44418aab93a55378a7727e48dde52eaf49c0e70e6e84ed01c62210048dc9c5cbe826bffbc0b7b85a779084517828350f50f76f88759f291385bdef
|
7
|
+
data.tar.gz: e9a63b027c140d453648376871ab24f5192004971239aa8b06ad5f73b3da917a7b70923d8ac161fc3a10d352a167ac55577136c05f8dfd12a78e4c0c08acfbc3
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# RuboCop Performance
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/rubocop-performance)
|
4
|
-
[](https://circleci.com/gh/rubocop/rubocop-performance)
|
5
|
+
[](https://discord.gg/wJjWvGRDmm)
|
5
6
|
|
6
|
-
Performance optimization analysis for your projects, as an extension to [RuboCop](https://github.com/rubocop
|
7
|
+
Performance optimization analysis for your projects, as an extension to [RuboCop](https://github.com/rubocop/rubocop).
|
7
8
|
|
8
9
|
## Installation
|
9
10
|
|
data/config/default.yml
CHANGED
@@ -10,7 +10,7 @@ Performance/AncestorsInclude:
|
|
10
10
|
Performance/ArraySemiInfiniteRangeSlice:
|
11
11
|
Description: 'Identifies places where slicing arrays with semi-infinite ranges can be replaced by `Array#take` and `Array#drop`.'
|
12
12
|
# This cop was created due to a mistake in microbenchmark.
|
13
|
-
# Refer https://github.com/rubocop
|
13
|
+
# Refer https://github.com/rubocop/rubocop-performance/pull/175#issuecomment-731892717
|
14
14
|
Enabled: false
|
15
15
|
# Unsafe for string slices because strings do not have `#take` and `#drop` methods.
|
16
16
|
Safe: false
|
@@ -80,6 +80,7 @@ Performance/ConstantRegexp:
|
|
80
80
|
Description: 'Finds regular expressions with dynamic components that are all constants.'
|
81
81
|
Enabled: pending
|
82
82
|
VersionAdded: '1.9'
|
83
|
+
VersionChanged: '1.10'
|
83
84
|
|
84
85
|
Performance/Count:
|
85
86
|
Description: >-
|
@@ -136,11 +137,10 @@ Performance/EndWith:
|
|
136
137
|
# object. Switching these methods has to be done with knowledge of the types
|
137
138
|
# of the variables which rubocop doesn't have.
|
138
139
|
SafeAutoCorrect: false
|
139
|
-
AutoCorrect: false
|
140
140
|
Enabled: true
|
141
141
|
SafeMultiline: true
|
142
142
|
VersionAdded: '0.36'
|
143
|
-
VersionChanged: '1.
|
143
|
+
VersionChanged: '1.10'
|
144
144
|
|
145
145
|
Performance/FixedSize:
|
146
146
|
Description: 'Do not compute the size of statically sized objects except in constants.'
|
@@ -174,6 +174,12 @@ Performance/IoReadlines:
|
|
174
174
|
Enabled: false
|
175
175
|
VersionAdded: '1.7'
|
176
176
|
|
177
|
+
Performance/MapCompact:
|
178
|
+
Description: 'Use `filter_map` instead of `collection.map(&:do_something).compact`.'
|
179
|
+
Enabled: pending
|
180
|
+
SafeAutoCorrect: false
|
181
|
+
VersionAdded: '1.11'
|
182
|
+
|
177
183
|
Performance/MethodObjectAsBlock:
|
178
184
|
Description: 'Use block explicitly instead of block-passing a method object.'
|
179
185
|
Reference: 'https://github.com/JuanitoFatas/fast-ruby#normal-way-to-apply-method-vs-method-code'
|
@@ -200,6 +206,15 @@ Performance/RedundantBlockCall:
|
|
200
206
|
Enabled: true
|
201
207
|
VersionAdded: '0.36'
|
202
208
|
|
209
|
+
Performance/RedundantEqualityComparisonBlock:
|
210
|
+
Description: >-
|
211
|
+
Checks for uses `Enumerable#all?`, `Enumerable#any?`, `Enumerable#one?`,
|
212
|
+
or `Enumerable#none?` are compared with `===` or similar methods in block.
|
213
|
+
Reference: 'https://github.com/rails/rails/pull/41363'
|
214
|
+
Enabled: pending
|
215
|
+
Safe: false
|
216
|
+
VersionAdded: '1.10'
|
217
|
+
|
203
218
|
Performance/RedundantMatch:
|
204
219
|
Description: >-
|
205
220
|
Use `=~` instead of `String#match` or `Regexp#match` in a context where the
|
@@ -211,7 +226,9 @@ Performance/RedundantMerge:
|
|
211
226
|
Description: 'Use Hash#[]=, rather than Hash#merge! with a single key-value pair.'
|
212
227
|
Reference: 'https://github.com/JuanitoFatas/fast-ruby#hashmerge-vs-hash-code'
|
213
228
|
Enabled: true
|
229
|
+
Safe: false
|
214
230
|
VersionAdded: '0.36'
|
231
|
+
VersionChanged: '1.11'
|
215
232
|
# Max number of key-value pairs to consider an offense
|
216
233
|
MaxKeyValuePairs: 2
|
217
234
|
|
@@ -220,6 +237,11 @@ Performance/RedundantSortBlock:
|
|
220
237
|
Enabled: 'pending'
|
221
238
|
VersionAdded: '1.7'
|
222
239
|
|
240
|
+
Performance/RedundantSplitRegexpArgument:
|
241
|
+
Description: 'This cop identifies places where `split` argument can be replaced from a deterministic regexp to a string.'
|
242
|
+
Enabled: pending
|
243
|
+
VersionAdded: '1.10'
|
244
|
+
|
223
245
|
Performance/RedundantStringChars:
|
224
246
|
Description: 'Checks for redundant `String#chars`.'
|
225
247
|
Enabled: 'pending'
|
@@ -244,6 +266,11 @@ Performance/ReverseFirst:
|
|
244
266
|
Enabled: 'pending'
|
245
267
|
VersionAdded: '1.7'
|
246
268
|
|
269
|
+
Performance/SelectMap:
|
270
|
+
Description: 'Use `filter_map` instead of `ary.select(&:foo).map(&:bar)`.'
|
271
|
+
Enabled: false
|
272
|
+
VersionAdded: '1.11'
|
273
|
+
|
247
274
|
Performance/Size:
|
248
275
|
Description: >-
|
249
276
|
Use `size` instead of `count` for counting
|
@@ -270,11 +297,10 @@ Performance/StartWith:
|
|
270
297
|
# object. Switching these methods has to be done with knowledge of the types
|
271
298
|
# of the variables which rubocop doesn't have.
|
272
299
|
SafeAutoCorrect: false
|
273
|
-
AutoCorrect: false
|
274
300
|
Enabled: true
|
275
301
|
SafeMultiline: true
|
276
302
|
VersionAdded: '0.36'
|
277
|
-
VersionChanged: '1.
|
303
|
+
VersionChanged: '1.10'
|
278
304
|
|
279
305
|
Performance/StringInclude:
|
280
306
|
Description: 'Use `String#include?` instead of a regex match with literal-only pattern.'
|
@@ -304,7 +330,7 @@ Performance/TimesMap:
|
|
304
330
|
Enabled: true
|
305
331
|
VersionAdded: '0.36'
|
306
332
|
VersionChanged: '0.50'
|
307
|
-
SafeAutoCorrect: false # see https://github.com/rubocop
|
333
|
+
SafeAutoCorrect: false # see https://github.com/rubocop/rubocop/issues/4658
|
308
334
|
|
309
335
|
Performance/UnfreezeString:
|
310
336
|
Description: 'Use unary plus to get an unfrozen string literal.'
|
@@ -6,7 +6,7 @@ module RuboCop
|
|
6
6
|
# This cop 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
|
-
# Refer https://github.com/rubocop
|
9
|
+
# Refer https://github.com/rubocop/rubocop-performance/pull/175#issuecomment-731892717
|
10
10
|
# This cop is also unsafe for string slices because strings do not have `#take` and `#drop` methods.
|
11
11
|
#
|
12
12
|
# @example
|
@@ -33,7 +33,7 @@ module RuboCop
|
|
33
33
|
def_node_matcher :bind_with_call_method?, <<~PATTERN
|
34
34
|
(send
|
35
35
|
$(send
|
36
|
-
|
36
|
+
_ :bind
|
37
37
|
$(...)) :call
|
38
38
|
$...)
|
39
39
|
PATTERN
|
@@ -64,7 +64,7 @@ module RuboCop
|
|
64
64
|
|
65
65
|
def correction_range(receiver, node)
|
66
66
|
location_of_bind = receiver.loc.selector.begin_pos
|
67
|
-
location_of_call = node.
|
67
|
+
location_of_call = node.source_range.end.end_pos
|
68
68
|
|
69
69
|
range_between(location_of_bind, location_of_call)
|
70
70
|
end
|
@@ -63,6 +63,8 @@ module RuboCop
|
|
63
63
|
|
64
64
|
def on_send(node)
|
65
65
|
chain_array_allocation?(node) do |fm, sm|
|
66
|
+
return if node.each_descendant(:send).any? { |descendant| descendant.method?(:lazy) }
|
67
|
+
|
66
68
|
range = range_between(node.loc.dot.begin_pos, node.source_range.end_pos)
|
67
69
|
|
68
70
|
add_offense(range, message: format(MSG, method: fm, second_method: sm))
|
@@ -6,9 +6,9 @@ module RuboCop
|
|
6
6
|
# This cop 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
|
-
# a regular expression. It is more efficient to extract it into a constant
|
10
|
-
# or add an `/o` option to perform `#{}` interpolation only once and
|
11
|
-
# Regexp object.
|
9
|
+
# a regular expression. It is more efficient to extract it into a constant,
|
10
|
+
# memoize it, or add an `/o` option to perform `#{}` interpolation only once and
|
11
|
+
# reuse that Regexp object.
|
12
12
|
#
|
13
13
|
# @example
|
14
14
|
#
|
@@ -28,13 +28,18 @@ module RuboCop
|
|
28
28
|
# pattern.scan(TOKEN).reject { |token| token.match?(/\A#{SEPARATORS}\Z/o) }
|
29
29
|
# end
|
30
30
|
#
|
31
|
+
# # good
|
32
|
+
# def separators
|
33
|
+
# @separators ||= /\A#{SEPARATORS}\Z/
|
34
|
+
# end
|
35
|
+
#
|
31
36
|
class ConstantRegexp < Base
|
32
37
|
extend AutoCorrector
|
33
38
|
|
34
|
-
MSG = 'Extract this regexp into a constant or append an `/o` option to its options.'
|
39
|
+
MSG = 'Extract this regexp into a constant, memoize it, or append an `/o` option to its options.'
|
35
40
|
|
36
41
|
def on_regexp(node)
|
37
|
-
return if
|
42
|
+
return if within_allowed_assignment?(node) ||
|
38
43
|
!include_interpolated_const?(node) ||
|
39
44
|
node.single_interpolation?
|
40
45
|
|
@@ -45,8 +50,8 @@ module RuboCop
|
|
45
50
|
|
46
51
|
private
|
47
52
|
|
48
|
-
def
|
49
|
-
node.each_ancestor(:casgn).any?
|
53
|
+
def within_allowed_assignment?(node)
|
54
|
+
node.each_ancestor(:casgn, :or_asgn).any?
|
50
55
|
end
|
51
56
|
|
52
57
|
def_node_matcher :regexp_escape?, <<~PATTERN
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# In Ruby 2.7, `Enumerable#filter_map` has been added.
|
7
|
+
#
|
8
|
+
# This cop identifies places where `map { ... }.compact` can be replaced by `filter_map`.
|
9
|
+
# It is marked as unsafe auto-correction by default because `map { ... }.compact`
|
10
|
+
# that is not compatible with `filter_map`.
|
11
|
+
#
|
12
|
+
# [source,ruby]
|
13
|
+
# ----
|
14
|
+
# [true, false, nil].compact #=> [true, false]
|
15
|
+
# [true, false, nil].filter_map(&:itself) #=> [true]
|
16
|
+
# ----
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# # bad
|
20
|
+
# ary.map(&:foo).compact
|
21
|
+
# ary.collect(&:foo).compact
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# ary.filter_map(&:foo)
|
25
|
+
# ary.map(&:foo).compact!
|
26
|
+
#
|
27
|
+
class MapCompact < Base
|
28
|
+
include RangeHelp
|
29
|
+
extend AutoCorrector
|
30
|
+
extend TargetRubyVersion
|
31
|
+
|
32
|
+
MSG = 'Use `filter_map` instead.'
|
33
|
+
RESTRICT_ON_SEND = %i[compact].freeze
|
34
|
+
|
35
|
+
minimum_target_ruby_version 2.7
|
36
|
+
|
37
|
+
def_node_matcher :map_compact, <<~PATTERN
|
38
|
+
{
|
39
|
+
(send
|
40
|
+
$(send _ {:map :collect}
|
41
|
+
(block_pass
|
42
|
+
(sym _))) _)
|
43
|
+
(send
|
44
|
+
(block
|
45
|
+
$(send _ {:map :collect})
|
46
|
+
(args ...) _) _)
|
47
|
+
}
|
48
|
+
PATTERN
|
49
|
+
|
50
|
+
def on_send(node)
|
51
|
+
return unless (map_node = map_compact(node))
|
52
|
+
|
53
|
+
compact_loc = node.loc
|
54
|
+
range = range_between(map_node.loc.selector.begin_pos, compact_loc.selector.end_pos)
|
55
|
+
|
56
|
+
add_offense(range) do |corrector|
|
57
|
+
corrector.replace(map_node.loc.selector, 'filter_map')
|
58
|
+
corrector.remove(compact_loc.dot)
|
59
|
+
corrector.remove(compact_loc.selector)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -78,7 +78,7 @@ module RuboCop
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def calls_to_report(argname, body)
|
81
|
-
return [] if blockarg_assigned?(body, argname)
|
81
|
+
return [] if blockarg_assigned?(body, argname) || shadowed_block_argument?(body, argname)
|
82
82
|
|
83
83
|
blockarg_calls(body, argname).map do |call|
|
84
84
|
return [] if args_include_block_pass?(call)
|
@@ -87,6 +87,12 @@ module RuboCop
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
+
def shadowed_block_argument?(body, block_argument_of_method_signature)
|
91
|
+
return false unless body.block_type?
|
92
|
+
|
93
|
+
body.arguments.map(&:source).include?(block_argument_of_method_signature.to_s)
|
94
|
+
end
|
95
|
+
|
90
96
|
def args_include_block_pass?(blockcall)
|
91
97
|
_receiver, _call, *args = *blockcall
|
92
98
|
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop checks for uses `Enumerable#all?`, `Enumerable#any?`, `Enumerable#one?`,
|
7
|
+
# and `Enumerable#none?` are compared with `===` or similar methods in block.
|
8
|
+
#
|
9
|
+
# By default, `Object#===` behaves the same as `Object#==`, but this
|
10
|
+
# behavior is appropriately overridden in subclass. For example,
|
11
|
+
# `Range#===` returns `true` when argument is within the range.
|
12
|
+
# Therefore, It is marked as unsafe by default because `===` and `==`
|
13
|
+
# do not always behave the same.
|
14
|
+
#
|
15
|
+
# @example
|
16
|
+
# # bad
|
17
|
+
# items.all? { |item| pattern === item }
|
18
|
+
# items.all? { |item| item == other }
|
19
|
+
# items.all? { |item| item.is_a?(Klass) }
|
20
|
+
# items.all? { |item| item.kind_of?(Klass) }
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# items.all?(pattern)
|
24
|
+
#
|
25
|
+
class RedundantEqualityComparisonBlock < Base
|
26
|
+
extend AutoCorrector
|
27
|
+
extend TargetRubyVersion
|
28
|
+
|
29
|
+
minimum_target_ruby_version 2.5
|
30
|
+
|
31
|
+
MSG = 'Use `%<prefer>s` instead of block.'
|
32
|
+
|
33
|
+
TARGET_METHODS = %i[all? any? one? none?].freeze
|
34
|
+
COMPARISON_METHODS = %i[== === is_a? kind_of?].freeze
|
35
|
+
IS_A_METHODS = %i[is_a? kind_of?].freeze
|
36
|
+
|
37
|
+
def on_block(node)
|
38
|
+
return unless TARGET_METHODS.include?(node.method_name)
|
39
|
+
return unless one_block_argument?(node.arguments)
|
40
|
+
|
41
|
+
block_argument = node.arguments.first
|
42
|
+
block_body = node.body
|
43
|
+
return unless use_equality_comparison_block?(block_body)
|
44
|
+
return if same_block_argument_and_is_a_argument?(block_body, block_argument)
|
45
|
+
return unless (new_argument = new_argument(block_argument, block_body))
|
46
|
+
|
47
|
+
range = offense_range(node)
|
48
|
+
prefer = "#{node.method_name}(#{new_argument})"
|
49
|
+
|
50
|
+
add_offense(range, message: format(MSG, prefer: prefer)) do |corrector|
|
51
|
+
corrector.replace(range, prefer)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def one_block_argument?(block_arguments)
|
58
|
+
block_arguments.one? && !block_arguments.source.include?(',')
|
59
|
+
end
|
60
|
+
|
61
|
+
def use_equality_comparison_block?(block_body)
|
62
|
+
block_body.send_type? && COMPARISON_METHODS.include?(block_body.method_name)
|
63
|
+
end
|
64
|
+
|
65
|
+
def same_block_argument_and_is_a_argument?(block_body, block_argument)
|
66
|
+
if block_body.method?(:===)
|
67
|
+
block_argument.source != block_body.children[2].source
|
68
|
+
elsif IS_A_METHODS.include?(block_body.method_name)
|
69
|
+
block_argument.source == block_body.first_argument.source
|
70
|
+
else
|
71
|
+
false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def new_argument(block_argument, block_body)
|
76
|
+
if block_argument.source == block_body.receiver.source
|
77
|
+
block_body.first_argument.source
|
78
|
+
elsif block_argument.source == block_body.first_argument.source
|
79
|
+
block_body.receiver.source
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def offense_range(node)
|
84
|
+
node.send_node.loc.selector.join(node.source_range.end)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -8,6 +8,9 @@ module RuboCop
|
|
8
8
|
# You can set the maximum number of key-value pairs to consider
|
9
9
|
# an offense with `MaxKeyValuePairs`.
|
10
10
|
#
|
11
|
+
# This cop is marked as unsafe because RuboCop cannot determine if the
|
12
|
+
# receiver of `merge!` is actually a hash or not.
|
13
|
+
#
|
11
14
|
# @example
|
12
15
|
# # bad
|
13
16
|
# hash.merge!(a: 1)
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop identifies places where `split` argument can be replaced from
|
7
|
+
# a deterministic regexp to a string.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# 'a,b,c'.split(/,/)
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# 'a,b,c'.split(',')
|
15
|
+
class RedundantSplitRegexpArgument < Base
|
16
|
+
extend AutoCorrector
|
17
|
+
|
18
|
+
MSG = 'Use string as argument instead of regexp.'
|
19
|
+
RESTRICT_ON_SEND = %i[split].freeze
|
20
|
+
DETERMINISTIC_REGEX = /\A(?:#{LITERAL_REGEX})+\Z/.freeze
|
21
|
+
STR_SPECIAL_CHARS = %w[\n \" \' \\\\ \t \b \f \r].freeze
|
22
|
+
|
23
|
+
def_node_matcher :split_call_with_regexp?, <<~PATTERN
|
24
|
+
{(send !nil? :split $regexp)}
|
25
|
+
PATTERN
|
26
|
+
|
27
|
+
def on_send(node)
|
28
|
+
return unless (regexp_node = split_call_with_regexp?(node))
|
29
|
+
return if regexp_node.ignore_case? || regexp_node.content == ' '
|
30
|
+
return unless determinist_regexp?(regexp_node)
|
31
|
+
|
32
|
+
add_offense(regexp_node) do |corrector|
|
33
|
+
new_argument = replacement(regexp_node)
|
34
|
+
|
35
|
+
corrector.replace(regexp_node, "\"#{new_argument}\"")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def determinist_regexp?(regexp_node)
|
42
|
+
DETERMINISTIC_REGEX.match?(regexp_node.source)
|
43
|
+
end
|
44
|
+
|
45
|
+
def replacement(regexp_node)
|
46
|
+
regexp_content = regexp_node.content
|
47
|
+
stack = []
|
48
|
+
chars = regexp_content.chars.each_with_object([]) do |char, strings|
|
49
|
+
if stack.empty? && char == '\\'
|
50
|
+
stack.push(char)
|
51
|
+
else
|
52
|
+
strings << "#{stack.pop}#{char}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
chars.map do |char|
|
56
|
+
char = char.dup
|
57
|
+
char.delete!('\\') unless STR_SPECIAL_CHARS.include?(char)
|
58
|
+
char
|
59
|
+
end.join
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -6,41 +6,53 @@ module RuboCop
|
|
6
6
|
# This cop is used to identify usages of `reverse.each` and
|
7
7
|
# change them to use `reverse_each` instead.
|
8
8
|
#
|
9
|
+
# If the return value is used, it will not be detected because the result will be different.
|
10
|
+
#
|
11
|
+
# [source,ruby]
|
12
|
+
# ----
|
13
|
+
# [1, 2, 3].reverse.each {} #=> [3, 2, 1]
|
14
|
+
# [1, 2, 3].reverse_each {} #=> [1, 2, 3]
|
15
|
+
# ----
|
16
|
+
#
|
9
17
|
# @example
|
10
18
|
# # bad
|
11
|
-
#
|
19
|
+
# items.reverse.each
|
12
20
|
#
|
13
21
|
# # good
|
14
|
-
#
|
22
|
+
# items.reverse_each
|
15
23
|
class ReverseEach < Base
|
16
24
|
include RangeHelp
|
17
25
|
extend AutoCorrector
|
18
26
|
|
19
27
|
MSG = 'Use `reverse_each` instead of `reverse.each`.'
|
20
28
|
RESTRICT_ON_SEND = %i[each].freeze
|
21
|
-
UNDERSCORE = '_'
|
22
29
|
|
23
30
|
def_node_matcher :reverse_each?, <<~MATCHER
|
24
|
-
(send
|
31
|
+
(send (send _ :reverse) :each)
|
25
32
|
MATCHER
|
26
33
|
|
27
34
|
def on_send(node)
|
28
|
-
|
29
|
-
location_of_reverse = receiver.loc.selector.begin_pos
|
30
|
-
end_location = node.loc.selector.end_pos
|
35
|
+
return if use_return_value?(node)
|
31
36
|
|
32
|
-
|
37
|
+
reverse_each?(node) do
|
38
|
+
range = offense_range(node)
|
33
39
|
|
34
40
|
add_offense(range) do |corrector|
|
35
|
-
corrector.replace(
|
41
|
+
corrector.replace(range, 'reverse_each')
|
36
42
|
end
|
37
43
|
end
|
38
44
|
end
|
39
45
|
|
40
46
|
private
|
41
47
|
|
42
|
-
def
|
43
|
-
|
48
|
+
def use_return_value?(node)
|
49
|
+
!!node.ancestors.detect do |ancestor|
|
50
|
+
ancestor.assignment? || ancestor.send_type? || ancestor.return_type?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def offense_range(node)
|
55
|
+
range_between(node.children.first.loc.selector.begin_pos, node.loc.selector.end_pos)
|
44
56
|
end
|
45
57
|
end
|
46
58
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# In Ruby 2.7, `Enumerable#filter_map` has been added.
|
7
|
+
#
|
8
|
+
# This cop identifies places where `select.map` can be replaced by `filter_map`.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# # bad
|
12
|
+
# ary.select(&:foo).map(&:bar)
|
13
|
+
# ary.filter(&:foo).map(&:bar)
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# ary.filter_map { |o| o.bar if o.foo }
|
17
|
+
#
|
18
|
+
class SelectMap < Base
|
19
|
+
include RangeHelp
|
20
|
+
extend TargetRubyVersion
|
21
|
+
|
22
|
+
minimum_target_ruby_version 2.7
|
23
|
+
|
24
|
+
MSG = 'Use `filter_map` instead of `%<method_name>s.map`.'
|
25
|
+
RESTRICT_ON_SEND = %i[select filter].freeze
|
26
|
+
|
27
|
+
def_node_matcher :bad_method?, <<~PATTERN
|
28
|
+
(send nil? :bad_method ...)
|
29
|
+
PATTERN
|
30
|
+
|
31
|
+
def on_send(node)
|
32
|
+
return if (first_argument = node.first_argument) && !first_argument.block_pass_type?
|
33
|
+
return unless (send_node = map_method_candidate(node))
|
34
|
+
return unless send_node.method?(:map)
|
35
|
+
|
36
|
+
map_method = send_node.parent&.block_type? ? send_node.parent : send_node
|
37
|
+
|
38
|
+
range = offense_range(node, map_method)
|
39
|
+
add_offense(range, message: format(MSG, method_name: node.method_name))
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def map_method_candidate(node)
|
45
|
+
return unless (parent = node.parent)
|
46
|
+
|
47
|
+
if parent.block_type? && parent.parent&.send_type?
|
48
|
+
parent.parent
|
49
|
+
elsif parent.send_type?
|
50
|
+
parent
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def offense_range(node, map_method)
|
55
|
+
range_between(node.loc.selector.begin_pos, map_method.loc.expression.end_pos)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -150,7 +150,9 @@ module RuboCop
|
|
150
150
|
replacement = build_good_method(init, block_pass)
|
151
151
|
|
152
152
|
corrector.remove(sum_range)
|
153
|
-
|
153
|
+
|
154
|
+
dot = '.' if map.receiver
|
155
|
+
corrector.replace(map_range, "#{dot}#{replacement}")
|
154
156
|
end
|
155
157
|
|
156
158
|
def sum_method_range(node)
|
@@ -228,7 +230,11 @@ module RuboCop
|
|
228
230
|
end
|
229
231
|
|
230
232
|
def method_call_with_args_range(node)
|
231
|
-
node.receiver
|
233
|
+
if (receiver = node.receiver)
|
234
|
+
receiver.source_range.end.join(node.source_range.end)
|
235
|
+
else
|
236
|
+
node.source_range
|
237
|
+
end
|
232
238
|
end
|
233
239
|
end
|
234
240
|
end
|
@@ -23,18 +23,22 @@ require_relative 'performance/end_with'
|
|
23
23
|
require_relative 'performance/fixed_size'
|
24
24
|
require_relative 'performance/flat_map'
|
25
25
|
require_relative 'performance/inefficient_hash_search'
|
26
|
+
require_relative 'performance/map_compact'
|
26
27
|
require_relative 'performance/method_object_as_block'
|
27
28
|
require_relative 'performance/open_struct'
|
28
29
|
require_relative 'performance/range_include'
|
29
30
|
require_relative 'performance/io_readlines'
|
30
31
|
require_relative 'performance/redundant_block_call'
|
32
|
+
require_relative 'performance/redundant_equality_comparison_block'
|
31
33
|
require_relative 'performance/redundant_match'
|
32
34
|
require_relative 'performance/redundant_merge'
|
33
35
|
require_relative 'performance/redundant_sort_block'
|
36
|
+
require_relative 'performance/redundant_split_regexp_argument'
|
34
37
|
require_relative 'performance/redundant_string_chars'
|
35
38
|
require_relative 'performance/regexp_match'
|
36
39
|
require_relative 'performance/reverse_each'
|
37
40
|
require_relative 'performance/reverse_first'
|
41
|
+
require_relative 'performance/select_map'
|
38
42
|
require_relative 'performance/size'
|
39
43
|
require_relative 'performance/sort_reverse'
|
40
44
|
require_relative 'performance/squeeze'
|
data/lib/rubocop/performance.rb
CHANGED
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.11.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:
|
13
|
+
date: 2021-04-21 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rubocop
|
@@ -18,7 +18,7 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ">="
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
21
|
+
version: 1.7.0
|
22
22
|
- - "<"
|
23
23
|
- !ruby/object:Gem::Version
|
24
24
|
version: '2.0'
|
@@ -28,7 +28,7 @@ dependencies:
|
|
28
28
|
requirements:
|
29
29
|
- - ">="
|
30
30
|
- !ruby/object:Gem::Version
|
31
|
-
version:
|
31
|
+
version: 1.7.0
|
32
32
|
- - "<"
|
33
33
|
- !ruby/object:Gem::Version
|
34
34
|
version: '2.0'
|
@@ -59,6 +59,7 @@ files:
|
|
59
59
|
- LICENSE.txt
|
60
60
|
- README.md
|
61
61
|
- config/default.yml
|
62
|
+
- config/obsoletion.yml
|
62
63
|
- lib/rubocop-performance.rb
|
63
64
|
- lib/rubocop/cop/mixin/regexp_metacharacter.rb
|
64
65
|
- lib/rubocop/cop/mixin/sort_block.rb
|
@@ -84,17 +85,21 @@ files:
|
|
84
85
|
- lib/rubocop/cop/performance/flat_map.rb
|
85
86
|
- lib/rubocop/cop/performance/inefficient_hash_search.rb
|
86
87
|
- lib/rubocop/cop/performance/io_readlines.rb
|
88
|
+
- lib/rubocop/cop/performance/map_compact.rb
|
87
89
|
- lib/rubocop/cop/performance/method_object_as_block.rb
|
88
90
|
- lib/rubocop/cop/performance/open_struct.rb
|
89
91
|
- lib/rubocop/cop/performance/range_include.rb
|
90
92
|
- lib/rubocop/cop/performance/redundant_block_call.rb
|
93
|
+
- lib/rubocop/cop/performance/redundant_equality_comparison_block.rb
|
91
94
|
- lib/rubocop/cop/performance/redundant_match.rb
|
92
95
|
- lib/rubocop/cop/performance/redundant_merge.rb
|
93
96
|
- lib/rubocop/cop/performance/redundant_sort_block.rb
|
97
|
+
- lib/rubocop/cop/performance/redundant_split_regexp_argument.rb
|
94
98
|
- lib/rubocop/cop/performance/redundant_string_chars.rb
|
95
99
|
- lib/rubocop/cop/performance/regexp_match.rb
|
96
100
|
- lib/rubocop/cop/performance/reverse_each.rb
|
97
101
|
- lib/rubocop/cop/performance/reverse_first.rb
|
102
|
+
- lib/rubocop/cop/performance/select_map.rb
|
98
103
|
- lib/rubocop/cop/performance/size.rb
|
99
104
|
- lib/rubocop/cop/performance/sort_reverse.rb
|
100
105
|
- lib/rubocop/cop/performance/squeeze.rb
|
@@ -109,15 +114,15 @@ files:
|
|
109
114
|
- lib/rubocop/performance.rb
|
110
115
|
- lib/rubocop/performance/inject.rb
|
111
116
|
- lib/rubocop/performance/version.rb
|
112
|
-
homepage: https://github.com/rubocop
|
117
|
+
homepage: https://github.com/rubocop/rubocop-performance
|
113
118
|
licenses:
|
114
119
|
- MIT
|
115
120
|
metadata:
|
116
121
|
homepage_uri: https://docs.rubocop.org/rubocop-performance/
|
117
|
-
changelog_uri: https://github.com/rubocop
|
118
|
-
source_code_uri: https://github.com/rubocop
|
119
|
-
documentation_uri: https://docs.rubocop.org/rubocop-performance/1.
|
120
|
-
bug_tracker_uri: https://github.com/rubocop
|
122
|
+
changelog_uri: https://github.com/rubocop/rubocop-performance/blob/master/CHANGELOG.md
|
123
|
+
source_code_uri: https://github.com/rubocop/rubocop-performance/
|
124
|
+
documentation_uri: https://docs.rubocop.org/rubocop-performance/1.11/
|
125
|
+
bug_tracker_uri: https://github.com/rubocop/rubocop-performance/issues
|
121
126
|
post_install_message:
|
122
127
|
rdoc_options: []
|
123
128
|
require_paths:
|
@@ -126,14 +131,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
126
131
|
requirements:
|
127
132
|
- - ">="
|
128
133
|
- !ruby/object:Gem::Version
|
129
|
-
version: 2.
|
134
|
+
version: 2.5.0
|
130
135
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
131
136
|
requirements:
|
132
137
|
- - ">="
|
133
138
|
- !ruby/object:Gem::Version
|
134
139
|
version: '0'
|
135
140
|
requirements: []
|
136
|
-
rubygems_version: 3.2.
|
141
|
+
rubygems_version: 3.2.12
|
137
142
|
signing_key:
|
138
143
|
specification_version: 4
|
139
144
|
summary: Automatic performance checking tool for Ruby code.
|