rubocop-performance 1.9.2 → 1.11.1
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 +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 +77 -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 +18 -2
- 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: 0fddb9566bda412ba7ab8f26966cca63207064468d2c2da5796a81cd84da1c69
|
4
|
+
data.tar.gz: f49da21e50225a0f35fe6959ad3a37a2c26ff73e64df03f5a94025811d380b02
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a70fd0b8cf0778f410098e889dc52c828202f0d197a10a9fb4edff36991204cad23cfba361039b9b80952c7f5f814679d7277e2f9c533fe29fd55e708c4d1bb9
|
7
|
+
data.tar.gz: a2ce3b7915ed3c3bc208ef020f031646e3350d46397759eb0c5a772e826dc30ad8d72d75c009b385358ef015be4b6af4c7cd1b6f337b6f34c583432bef608a65
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# RuboCop Performance
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/rubocop-performance.svg)](https://badge.fury.io/rb/rubocop-performance)
|
4
|
-
[![CircleCI](https://circleci.com/gh/rubocop
|
4
|
+
[![CircleCI](https://circleci.com/gh/rubocop/rubocop-performance.svg?style=svg)](https://circleci.com/gh/rubocop/rubocop-performance)
|
5
|
+
[![Discord](https://img.shields.io/badge/chat-on%20discord-7289da.svg?sanitize=true)](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,77 @@
|
|
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_method_range(node))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def compact_method_range(compact_node)
|
66
|
+
compact_method_range = compact_node.loc.selector
|
67
|
+
|
68
|
+
if compact_node.multiline?
|
69
|
+
range_by_whole_lines(compact_method_range, include_final_newline: true)
|
70
|
+
else
|
71
|
+
compact_method_range
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
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,12 +6,20 @@ 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
|
@@ -24,6 +32,8 @@ module RuboCop
|
|
24
32
|
MATCHER
|
25
33
|
|
26
34
|
def on_send(node)
|
35
|
+
return if use_return_value?(node)
|
36
|
+
|
27
37
|
reverse_each?(node) do
|
28
38
|
range = offense_range(node)
|
29
39
|
|
@@ -35,6 +45,12 @@ module RuboCop
|
|
35
45
|
|
36
46
|
private
|
37
47
|
|
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
|
+
|
38
54
|
def offense_range(node)
|
39
55
|
range_between(node.children.first.loc.selector.begin_pos, node.loc.selector.end_pos)
|
40
56
|
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.1
|
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: 2021-
|
13
|
+
date: 2021-05-01 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.
|