rubocop-performance 1.8.1 → 1.10.1
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 +10 -2
- data/config/default.yml +47 -6
- data/lib/rubocop/cop/mixin/regexp_metacharacter.rb +4 -4
- data/lib/rubocop/cop/performance/ancestors_include.rb +1 -0
- data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +77 -0
- data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +1 -0
- data/lib/rubocop/cop/performance/bind_call.rb +3 -2
- data/lib/rubocop/cop/performance/block_given_with_explicit_block.rb +52 -0
- data/lib/rubocop/cop/performance/caller.rb +13 -15
- data/lib/rubocop/cop/performance/casecmp.rb +1 -0
- data/lib/rubocop/cop/performance/chain_array_allocation.rb +20 -18
- data/lib/rubocop/cop/performance/constant_regexp.rb +73 -0
- data/lib/rubocop/cop/performance/count.rb +1 -0
- data/lib/rubocop/cop/performance/delete_prefix.rb +1 -0
- data/lib/rubocop/cop/performance/delete_suffix.rb +1 -0
- data/lib/rubocop/cop/performance/detect.rb +3 -2
- data/lib/rubocop/cop/performance/end_with.rb +1 -0
- data/lib/rubocop/cop/performance/fixed_size.rb +1 -0
- data/lib/rubocop/cop/performance/flat_map.rb +1 -0
- data/lib/rubocop/cop/performance/inefficient_hash_search.rb +2 -0
- data/lib/rubocop/cop/performance/io_readlines.rb +3 -7
- data/lib/rubocop/cop/performance/method_object_as_block.rb +32 -0
- data/lib/rubocop/cop/performance/open_struct.rb +1 -0
- data/lib/rubocop/cop/performance/range_include.rb +1 -0
- data/lib/rubocop/cop/performance/redundant_block_call.rb +4 -4
- data/lib/rubocop/cop/performance/redundant_equality_comparison_block.rb +80 -0
- data/lib/rubocop/cop/performance/redundant_match.rb +1 -0
- data/lib/rubocop/cop/performance/redundant_merge.rb +1 -0
- data/lib/rubocop/cop/performance/redundant_split_regexp_argument.rb +64 -0
- data/lib/rubocop/cop/performance/redundant_string_chars.rb +2 -6
- data/lib/rubocop/cop/performance/reverse_each.rb +7 -10
- data/lib/rubocop/cop/performance/reverse_first.rb +1 -0
- data/lib/rubocop/cop/performance/size.rb +1 -0
- data/lib/rubocop/cop/performance/squeeze.rb +2 -1
- data/lib/rubocop/cop/performance/start_with.rb +1 -0
- data/lib/rubocop/cop/performance/string_include.rb +2 -1
- data/lib/rubocop/cop/performance/string_replacement.rb +1 -0
- data/lib/rubocop/cop/performance/sum.rb +123 -15
- data/lib/rubocop/cop/performance/times_map.rb +1 -0
- data/lib/rubocop/cop/performance/unfreeze_string.rb +19 -1
- data/lib/rubocop/cop/performance/uri_default_parser.rb +1 -0
- data/lib/rubocop/cop/performance_cops.rb +6 -0
- data/lib/rubocop/performance/version.rb +6 -1
- metadata +25 -27
@@ -44,10 +44,10 @@ module RuboCop
|
|
44
44
|
extend AutoCorrector
|
45
45
|
|
46
46
|
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
47
|
-
|
47
|
+
RESTRICT_ON_SEND = %i[[] slice first last take drop length size empty?].freeze
|
48
48
|
|
49
49
|
def_node_matcher :redundant_chars_call?, <<~PATTERN
|
50
|
-
(send $(send _ :chars)
|
50
|
+
(send $(send _ :chars) $_ $...)
|
51
51
|
PATTERN
|
52
52
|
|
53
53
|
def on_send(node)
|
@@ -66,10 +66,6 @@ module RuboCop
|
|
66
66
|
|
67
67
|
private
|
68
68
|
|
69
|
-
def replaceable_method?(method_name)
|
70
|
-
REPLACEABLE_METHODS.include?(method_name)
|
71
|
-
end
|
72
|
-
|
73
69
|
def offense_range(receiver, node)
|
74
70
|
range_between(receiver.loc.selector.begin_pos, node.loc.expression.end_pos)
|
75
71
|
end
|
@@ -17,29 +17,26 @@ module RuboCop
|
|
17
17
|
extend AutoCorrector
|
18
18
|
|
19
19
|
MSG = 'Use `reverse_each` instead of `reverse.each`.'
|
20
|
-
|
20
|
+
RESTRICT_ON_SEND = %i[each].freeze
|
21
21
|
|
22
22
|
def_node_matcher :reverse_each?, <<~MATCHER
|
23
|
-
(send
|
23
|
+
(send (send _ :reverse) :each)
|
24
24
|
MATCHER
|
25
25
|
|
26
26
|
def on_send(node)
|
27
|
-
reverse_each?(node) do
|
28
|
-
|
29
|
-
end_location = node.loc.selector.end_pos
|
30
|
-
|
31
|
-
range = range_between(location_of_reverse, end_location)
|
27
|
+
reverse_each?(node) do
|
28
|
+
range = offense_range(node)
|
32
29
|
|
33
30
|
add_offense(range) do |corrector|
|
34
|
-
corrector.replace(
|
31
|
+
corrector.replace(range, 'reverse_each')
|
35
32
|
end
|
36
33
|
end
|
37
34
|
end
|
38
35
|
|
39
36
|
private
|
40
37
|
|
41
|
-
def
|
42
|
-
range_between(node.loc.
|
38
|
+
def offense_range(node)
|
39
|
+
range_between(node.children.first.loc.selector.begin_pos, node.loc.selector.end_pos)
|
43
40
|
end
|
44
41
|
end
|
45
42
|
end
|
@@ -22,6 +22,7 @@ module RuboCop
|
|
22
22
|
extend AutoCorrector
|
23
23
|
|
24
24
|
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
25
|
+
RESTRICT_ON_SEND = %i[gsub gsub!].freeze
|
25
26
|
|
26
27
|
PREFERRED_METHODS = {
|
27
28
|
gsub: :squeeze,
|
@@ -58,7 +59,7 @@ module RuboCop
|
|
58
59
|
private
|
59
60
|
|
60
61
|
def repeating_literal?(regex_str)
|
61
|
-
regex_str.match?(/\A(?:#{Util::LITERAL_REGEX})\+\z/)
|
62
|
+
regex_str.match?(/\A(?:#{Util::LITERAL_REGEX})\+\z/o)
|
62
63
|
end
|
63
64
|
end
|
64
65
|
end
|
@@ -47,6 +47,7 @@ module RuboCop
|
|
47
47
|
|
48
48
|
MSG = 'Use `String#start_with?` instead of a regex match anchored to ' \
|
49
49
|
'the beginning of the string.'
|
50
|
+
RESTRICT_ON_SEND = %i[match =~ match?].freeze
|
50
51
|
|
51
52
|
def_node_matcher :redundant_regex?, <<~PATTERN
|
52
53
|
{(send $!nil? {:match :=~ :match?} (regexp (str $#literal_at_start?) (regopt)))
|
@@ -23,6 +23,7 @@ module RuboCop
|
|
23
23
|
extend AutoCorrector
|
24
24
|
|
25
25
|
MSG = 'Use `String#include?` instead of a regex match with literal-only pattern.'
|
26
|
+
RESTRICT_ON_SEND = %i[match =~ match?].freeze
|
26
27
|
|
27
28
|
def_node_matcher :redundant_regex?, <<~PATTERN
|
28
29
|
{(send $!nil? {:match :=~ :match?} (regexp (str $#literal?) (regopt)))
|
@@ -47,7 +48,7 @@ module RuboCop
|
|
47
48
|
private
|
48
49
|
|
49
50
|
def literal?(regex_str)
|
50
|
-
regex_str.match?(/\A#{Util::LITERAL_REGEX}+\z/)
|
51
|
+
regex_str.match?(/\A#{Util::LITERAL_REGEX}+\z/o)
|
51
52
|
end
|
52
53
|
end
|
53
54
|
end
|
@@ -6,28 +6,67 @@ module RuboCop
|
|
6
6
|
# This cop identifies places where custom code finding the sum of elements
|
7
7
|
# in some Enumerable object can be replaced by `Enumerable#sum` method.
|
8
8
|
#
|
9
|
+
# This cop can change auto-correction scope depending on the value of
|
10
|
+
# `SafeAutoCorrect`.
|
11
|
+
# Its auto-correction is marked as safe by default (`SafeAutoCorrect: true`)
|
12
|
+
# to prevent `TypeError` in auto-correced code when initial value is not
|
13
|
+
# specified as shown below:
|
14
|
+
#
|
15
|
+
# [source,ruby]
|
16
|
+
# ----
|
17
|
+
# ['a', 'b'].sum # => (String can't be coerced into Integer)
|
18
|
+
# ----
|
19
|
+
#
|
20
|
+
# Therefore if initial value is not specified, unsafe auto-corrected will not occur.
|
21
|
+
#
|
22
|
+
# If you always want to enable auto-correction, you can set `SafeAutoCorrect: false`.
|
23
|
+
#
|
24
|
+
# [source,yaml]
|
25
|
+
# ----
|
26
|
+
# Performance/Sum:
|
27
|
+
# SafeAutoCorrect: false
|
28
|
+
# ----
|
29
|
+
#
|
30
|
+
# Please note that the auto-correction command line option will be changed from
|
31
|
+
# `rubocop -a` to `rubocop -A`, which includes unsafe auto-correction.
|
32
|
+
#
|
9
33
|
# @example
|
10
34
|
# # bad
|
11
|
-
# [1, 2, 3].inject(:+)
|
35
|
+
# [1, 2, 3].inject(:+) # These bad cases with no initial value are unsafe and
|
36
|
+
# [1, 2, 3].inject(&:+) # will not be auto-correced by default. If you want to
|
37
|
+
# [1, 2, 3].reduce { |acc, elem| acc + elem } # auto-corrected, you can set `SafeAutoCorrect: false`.
|
12
38
|
# [1, 2, 3].reduce(10, :+)
|
13
|
-
# [1, 2, 3].
|
14
|
-
# [1, 2, 3].
|
39
|
+
# [1, 2, 3].map { |elem| elem ** 2 }.sum
|
40
|
+
# [1, 2, 3].collect(&:count).sum(10)
|
15
41
|
#
|
16
42
|
# # good
|
17
43
|
# [1, 2, 3].sum
|
18
44
|
# [1, 2, 3].sum(10)
|
19
|
-
# [1, 2, 3].sum
|
45
|
+
# [1, 2, 3].sum { |elem| elem ** 2 }
|
46
|
+
# [1, 2, 3].sum(10, &:count)
|
20
47
|
#
|
21
48
|
class Sum < Base
|
22
49
|
include RangeHelp
|
23
50
|
extend AutoCorrector
|
24
51
|
|
25
52
|
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
53
|
+
MSG_IF_NO_INIT_VALUE =
|
54
|
+
'Use `%<good_method>s` instead of `%<bad_method>s`, unless calling `%<bad_method>s` on an empty array.'
|
55
|
+
RESTRICT_ON_SEND = %i[inject reduce sum].freeze
|
26
56
|
|
27
57
|
def_node_matcher :sum_candidate?, <<~PATTERN
|
28
58
|
(send _ ${:inject :reduce} $_init ? ${(sym :+) (block_pass (sym :+))})
|
29
59
|
PATTERN
|
30
60
|
|
61
|
+
def_node_matcher :sum_map_candidate?, <<~PATTERN
|
62
|
+
(send
|
63
|
+
{
|
64
|
+
(block $(send _ {:map :collect}) ...)
|
65
|
+
$(send _ {:map :collect} (block_pass _))
|
66
|
+
}
|
67
|
+
:sum $_init ?)
|
68
|
+
PATTERN
|
69
|
+
|
31
70
|
def_node_matcher :sum_with_block_candidate?, <<~PATTERN
|
32
71
|
(block
|
33
72
|
$(send _ {:inject :reduce} $_init ?)
|
@@ -41,14 +80,10 @@ module RuboCop
|
|
41
80
|
alias elem_plus_acc? acc_plus_elem?
|
42
81
|
|
43
82
|
def on_send(node)
|
44
|
-
|
45
|
-
range = sum_method_range(node)
|
46
|
-
message = build_method_message(method, init, operation)
|
83
|
+
return if empty_array_literal?(node)
|
47
84
|
|
48
|
-
|
49
|
-
|
50
|
-
end
|
51
|
-
end
|
85
|
+
handle_sum_candidate(node)
|
86
|
+
handle_sum_map_candidate(node)
|
52
87
|
end
|
53
88
|
|
54
89
|
def on_block(node)
|
@@ -66,25 +101,87 @@ module RuboCop
|
|
66
101
|
|
67
102
|
private
|
68
103
|
|
104
|
+
def handle_sum_candidate(node)
|
105
|
+
sum_candidate?(node) do |method, init, operation|
|
106
|
+
range = sum_method_range(node)
|
107
|
+
message = build_method_message(node, method, init, operation)
|
108
|
+
|
109
|
+
add_offense(range, message: message) do |corrector|
|
110
|
+
autocorrect(corrector, init, range)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def handle_sum_map_candidate(node)
|
116
|
+
sum_map_candidate?(node) do |map, init|
|
117
|
+
next if node.block_literal? || node.block_argument?
|
118
|
+
|
119
|
+
message = build_sum_map_message(map.method_name, init)
|
120
|
+
|
121
|
+
add_offense(sum_map_range(map, node), message: message) do |corrector|
|
122
|
+
autocorrect_sum_map(corrector, node, map, init)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def empty_array_literal?(node)
|
128
|
+
receiver = node.children.first
|
129
|
+
array_literal?(node) && receiver && receiver.children.empty?
|
130
|
+
end
|
131
|
+
|
132
|
+
def array_literal?(node)
|
133
|
+
receiver = node.children.first
|
134
|
+
receiver&.literal? && receiver&.array_type?
|
135
|
+
end
|
136
|
+
|
69
137
|
def autocorrect(corrector, init, range)
|
70
|
-
return if init.empty?
|
138
|
+
return if init.empty? && safe_autocorrect?
|
71
139
|
|
72
140
|
replacement = build_good_method(init)
|
73
141
|
|
74
142
|
corrector.replace(range, replacement)
|
75
143
|
end
|
76
144
|
|
145
|
+
def autocorrect_sum_map(corrector, sum, map, init)
|
146
|
+
sum_range = method_call_with_args_range(sum)
|
147
|
+
map_range = method_call_with_args_range(map)
|
148
|
+
|
149
|
+
block_pass = map.last_argument if map.last_argument&.block_pass_type?
|
150
|
+
replacement = build_good_method(init, block_pass)
|
151
|
+
|
152
|
+
corrector.remove(sum_range)
|
153
|
+
|
154
|
+
dot = '.' if map.receiver
|
155
|
+
corrector.replace(map_range, "#{dot}#{replacement}")
|
156
|
+
end
|
157
|
+
|
77
158
|
def sum_method_range(node)
|
78
159
|
range_between(node.loc.selector.begin_pos, node.loc.end.end_pos)
|
79
160
|
end
|
80
161
|
|
162
|
+
def sum_map_range(map, sum)
|
163
|
+
range_between(map.loc.selector.begin_pos, sum.source_range.end.end_pos)
|
164
|
+
end
|
165
|
+
|
81
166
|
def sum_block_range(send, node)
|
82
167
|
range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
|
83
168
|
end
|
84
169
|
|
85
|
-
def build_method_message(method, init, operation)
|
170
|
+
def build_method_message(node, method, init, operation)
|
86
171
|
good_method = build_good_method(init)
|
87
172
|
bad_method = build_method_bad_method(init, method, operation)
|
173
|
+
msg = if init.empty? && !array_literal?(node)
|
174
|
+
MSG_IF_NO_INIT_VALUE
|
175
|
+
else
|
176
|
+
MSG
|
177
|
+
end
|
178
|
+
format(msg, good_method: good_method, bad_method: bad_method)
|
179
|
+
end
|
180
|
+
|
181
|
+
def build_sum_map_message(method, init)
|
182
|
+
sum_method = build_good_method(init)
|
183
|
+
good_method = "#{sum_method} { ... }"
|
184
|
+
bad_method = "#{method} { ... }.#{sum_method}"
|
88
185
|
format(MSG, good_method: good_method, bad_method: bad_method)
|
89
186
|
end
|
90
187
|
|
@@ -94,13 +191,16 @@ module RuboCop
|
|
94
191
|
format(MSG, good_method: good_method, bad_method: bad_method)
|
95
192
|
end
|
96
193
|
|
97
|
-
def build_good_method(init)
|
194
|
+
def build_good_method(init, block_pass = nil)
|
98
195
|
good_method = 'sum'
|
99
196
|
|
197
|
+
args = []
|
100
198
|
unless init.empty?
|
101
199
|
init = init.first
|
102
|
-
|
200
|
+
args << init.source unless init.int_type? && init.value.zero?
|
103
201
|
end
|
202
|
+
args << block_pass.source if block_pass
|
203
|
+
good_method += "(#{args.join(', ')})" unless args.empty?
|
104
204
|
good_method
|
105
205
|
end
|
106
206
|
|
@@ -128,6 +228,14 @@ module RuboCop
|
|
128
228
|
bad_method += " { |#{var_acc}, #{var_elem}| #{body.source} }"
|
129
229
|
bad_method
|
130
230
|
end
|
231
|
+
|
232
|
+
def method_call_with_args_range(node)
|
233
|
+
if (receiver = node.receiver)
|
234
|
+
receiver.source_range.end.join(node.source_range.end)
|
235
|
+
else
|
236
|
+
node.source_range
|
237
|
+
end
|
238
|
+
end
|
131
239
|
end
|
132
240
|
end
|
133
241
|
end
|
@@ -23,6 +23,7 @@ module RuboCop
|
|
23
23
|
MESSAGE = 'Use `Array.new(%<count>s)` with a block ' \
|
24
24
|
'instead of `.times.%<map_or_collect>s`'
|
25
25
|
MESSAGE_ONLY_IF = 'only if `%<count>s` is always 0 or more'
|
26
|
+
RESTRICT_ON_SEND = %i[map collect].freeze
|
26
27
|
|
27
28
|
def on_send(node)
|
28
29
|
check(node)
|
@@ -10,6 +10,7 @@ module RuboCop
|
|
10
10
|
# NOTE: `String.new` (without operator) is not exactly the same as `+''`.
|
11
11
|
# These differ in encoding. `String.new.encoding` is always `ASCII-8BIT`.
|
12
12
|
# However, `(+'').encoding` is the same as script encoding(e.g. `UTF-8`).
|
13
|
+
# Therefore, auto-correction is unsafe.
|
13
14
|
# So, if you expect `ASCII-8BIT` encoding, disable this cop.
|
14
15
|
#
|
15
16
|
# @example
|
@@ -24,7 +25,10 @@ module RuboCop
|
|
24
25
|
# +'something'
|
25
26
|
# +''
|
26
27
|
class UnfreezeString < Base
|
28
|
+
extend AutoCorrector
|
29
|
+
|
27
30
|
MSG = 'Use unary plus to get an unfrozen string literal.'
|
31
|
+
RESTRICT_ON_SEND = %i[dup new].freeze
|
28
32
|
|
29
33
|
def_node_matcher :dup_string?, <<~PATTERN
|
30
34
|
(send {str dstr} :dup)
|
@@ -38,7 +42,21 @@ module RuboCop
|
|
38
42
|
PATTERN
|
39
43
|
|
40
44
|
def on_send(node)
|
41
|
-
|
45
|
+
return unless dup_string?(node) || string_new?(node)
|
46
|
+
|
47
|
+
add_offense(node) do |corrector|
|
48
|
+
corrector.replace(node, "+#{string_value(node)}")
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def string_value(node)
|
55
|
+
if node.receiver.source == 'String' && node.method?(:new)
|
56
|
+
node.arguments.empty? ? "''" : node.first_argument.source
|
57
|
+
else
|
58
|
+
node.receiver.source
|
59
|
+
end
|
42
60
|
end
|
43
61
|
end
|
44
62
|
end
|
@@ -4,13 +4,16 @@ require_relative 'mixin/regexp_metacharacter'
|
|
4
4
|
require_relative 'mixin/sort_block'
|
5
5
|
|
6
6
|
require_relative 'performance/ancestors_include'
|
7
|
+
require_relative 'performance/array_semi_infinite_range_slice'
|
7
8
|
require_relative 'performance/big_decimal_with_numeric_argument'
|
8
9
|
require_relative 'performance/bind_call'
|
10
|
+
require_relative 'performance/block_given_with_explicit_block'
|
9
11
|
require_relative 'performance/caller'
|
10
12
|
require_relative 'performance/case_when_splat'
|
11
13
|
require_relative 'performance/casecmp'
|
12
14
|
require_relative 'performance/collection_literal_in_loop'
|
13
15
|
require_relative 'performance/compare_with_block'
|
16
|
+
require_relative 'performance/constant_regexp'
|
14
17
|
require_relative 'performance/count'
|
15
18
|
require_relative 'performance/delete_prefix'
|
16
19
|
require_relative 'performance/delete_suffix'
|
@@ -20,13 +23,16 @@ require_relative 'performance/end_with'
|
|
20
23
|
require_relative 'performance/fixed_size'
|
21
24
|
require_relative 'performance/flat_map'
|
22
25
|
require_relative 'performance/inefficient_hash_search'
|
26
|
+
require_relative 'performance/method_object_as_block'
|
23
27
|
require_relative 'performance/open_struct'
|
24
28
|
require_relative 'performance/range_include'
|
25
29
|
require_relative 'performance/io_readlines'
|
26
30
|
require_relative 'performance/redundant_block_call'
|
31
|
+
require_relative 'performance/redundant_equality_comparison_block'
|
27
32
|
require_relative 'performance/redundant_match'
|
28
33
|
require_relative 'performance/redundant_merge'
|
29
34
|
require_relative 'performance/redundant_sort_block'
|
35
|
+
require_relative 'performance/redundant_split_regexp_argument'
|
30
36
|
require_relative 'performance/redundant_string_chars'
|
31
37
|
require_relative 'performance/regexp_match'
|
32
38
|
require_relative 'performance/reverse_each'
|