rubocop-performance 1.7.1 → 1.8.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 +18 -7
- data/lib/rubocop/cop/performance/ancestors_include.rb +14 -13
- data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +7 -12
- data/lib/rubocop/cop/performance/bind_call.rb +8 -18
- data/lib/rubocop/cop/performance/caller.rb +3 -2
- data/lib/rubocop/cop/performance/case_when_splat.rb +18 -11
- data/lib/rubocop/cop/performance/casecmp.rb +12 -20
- data/lib/rubocop/cop/performance/chain_array_allocation.rb +4 -10
- data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +140 -0
- data/lib/rubocop/cop/performance/compare_with_block.rb +10 -21
- data/lib/rubocop/cop/performance/count.rb +13 -16
- data/lib/rubocop/cop/performance/delete_prefix.rb +13 -22
- data/lib/rubocop/cop/performance/delete_suffix.rb +13 -22
- data/lib/rubocop/cop/performance/detect.rb +29 -26
- data/lib/rubocop/cop/performance/double_start_end_with.rb +16 -24
- data/lib/rubocop/cop/performance/end_with.rb +8 -13
- data/lib/rubocop/cop/performance/fixed_size.rb +1 -1
- data/lib/rubocop/cop/performance/flat_map.rb +20 -22
- data/lib/rubocop/cop/performance/inefficient_hash_search.rb +13 -14
- data/lib/rubocop/cop/performance/io_readlines.rb +25 -36
- data/lib/rubocop/cop/performance/open_struct.rb +2 -2
- data/lib/rubocop/cop/performance/range_include.rb +7 -6
- data/lib/rubocop/cop/performance/redundant_block_call.rb +11 -6
- data/lib/rubocop/cop/performance/redundant_match.rb +11 -6
- data/lib/rubocop/cop/performance/redundant_merge.rb +18 -17
- data/lib/rubocop/cop/performance/redundant_sort_block.rb +6 -16
- data/lib/rubocop/cop/performance/redundant_string_chars.rb +8 -12
- data/lib/rubocop/cop/performance/regexp_match.rb +20 -20
- data/lib/rubocop/cop/performance/reverse_each.rb +9 -5
- data/lib/rubocop/cop/performance/reverse_first.rb +4 -10
- data/lib/rubocop/cop/performance/size.rb +6 -6
- data/lib/rubocop/cop/performance/sort_reverse.rb +6 -15
- data/lib/rubocop/cop/performance/squeeze.rb +6 -10
- data/lib/rubocop/cop/performance/start_with.rb +8 -13
- data/lib/rubocop/cop/performance/string_include.rb +9 -13
- data/lib/rubocop/cop/performance/string_replacement.rb +23 -27
- data/lib/rubocop/cop/performance/sum.rb +129 -0
- data/lib/rubocop/cop/performance/times_map.rb +11 -18
- data/lib/rubocop/cop/performance/unfreeze_string.rb +1 -1
- data/lib/rubocop/cop/performance/uri_default_parser.rb +6 -12
- data/lib/rubocop/cop/performance_cops.rb +2 -0
- data/lib/rubocop/performance/version.rb +1 -1
- metadata +6 -4
@@ -12,8 +12,9 @@ module RuboCop
|
|
12
12
|
#
|
13
13
|
# # good
|
14
14
|
# [].reverse_each
|
15
|
-
class ReverseEach <
|
15
|
+
class ReverseEach < Base
|
16
16
|
include RangeHelp
|
17
|
+
extend AutoCorrector
|
17
18
|
|
18
19
|
MSG = 'Use `reverse_each` instead of `reverse.each`.'
|
19
20
|
UNDERSCORE = '_'
|
@@ -29,13 +30,16 @@ module RuboCop
|
|
29
30
|
|
30
31
|
range = range_between(location_of_reverse, end_location)
|
31
32
|
|
32
|
-
add_offense(
|
33
|
+
add_offense(range) do |corrector|
|
34
|
+
corrector.replace(replacement_range(node), UNDERSCORE)
|
35
|
+
end
|
33
36
|
end
|
34
37
|
end
|
35
38
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
+
private
|
40
|
+
|
41
|
+
def replacement_range(node)
|
42
|
+
range_between(node.loc.dot.begin_pos, node.loc.selector.begin_pos)
|
39
43
|
end
|
40
44
|
end
|
41
45
|
end
|
@@ -16,8 +16,9 @@ module RuboCop
|
|
16
16
|
# array.last(5).reverse
|
17
17
|
# array.last
|
18
18
|
#
|
19
|
-
class ReverseFirst <
|
19
|
+
class ReverseFirst < Base
|
20
20
|
include RangeHelp
|
21
|
+
extend AutoCorrector
|
21
22
|
|
22
23
|
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
23
24
|
|
@@ -30,16 +31,9 @@ module RuboCop
|
|
30
31
|
range = correction_range(receiver, node)
|
31
32
|
message = build_message(node)
|
32
33
|
|
33
|
-
add_offense(
|
34
|
-
|
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)
|
34
|
+
add_offense(range, message: message) do |corrector|
|
35
|
+
replacement = build_good_method(node)
|
41
36
|
|
42
|
-
lambda do |corrector|
|
43
37
|
corrector.replace(range, replacement)
|
44
38
|
end
|
45
39
|
end
|
@@ -35,7 +35,9 @@ module RuboCop
|
|
35
35
|
# [1, 2, 3].count { |e| e > 2 }
|
36
36
|
# TODO: Add advanced detection of variables that could
|
37
37
|
# have been assigned to an array or a hash.
|
38
|
-
class Size <
|
38
|
+
class Size < Base
|
39
|
+
extend AutoCorrector
|
40
|
+
|
39
41
|
MSG = 'Use `size` instead of `count`.'
|
40
42
|
|
41
43
|
def_node_matcher :array?, <<~PATTERN
|
@@ -63,11 +65,9 @@ module RuboCop
|
|
63
65
|
def on_send(node)
|
64
66
|
return if node.parent&.block_type? || !count?(node)
|
65
67
|
|
66
|
-
add_offense(node
|
67
|
-
|
68
|
-
|
69
|
-
def autocorrect(node)
|
70
|
-
->(corrector) { corrector.replace(node.loc.selector, 'size') }
|
68
|
+
add_offense(node.loc.selector) do |corrector|
|
69
|
+
corrector.replace(node.loc.selector, 'size')
|
70
|
+
end
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end
|
@@ -13,8 +13,9 @@ module RuboCop
|
|
13
13
|
# # good
|
14
14
|
# array.sort.reverse
|
15
15
|
#
|
16
|
-
class SortReverse <
|
16
|
+
class SortReverse < Base
|
17
17
|
include SortBlock
|
18
|
+
extend AutoCorrector
|
18
19
|
|
19
20
|
MSG = 'Use `sort.reverse` instead of `%<bad_method>s`.'
|
20
21
|
|
@@ -23,21 +24,11 @@ module RuboCop
|
|
23
24
|
replaceable_body?(body, var_b, var_a) do
|
24
25
|
range = sort_range(send, node)
|
25
26
|
|
26
|
-
add_offense(
|
27
|
-
|
28
|
-
location: range,
|
29
|
-
message: message(var_a, var_b)
|
30
|
-
)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
27
|
+
add_offense(range, message: message(var_a, var_b)) do |corrector|
|
28
|
+
replacement = 'sort.reverse'
|
34
29
|
|
35
|
-
|
36
|
-
|
37
|
-
lambda do |corrector|
|
38
|
-
range = sort_range(send, node)
|
39
|
-
replacement = 'sort.reverse'
|
40
|
-
corrector.replace(range, replacement)
|
30
|
+
corrector.replace(range, replacement)
|
31
|
+
end
|
41
32
|
end
|
42
33
|
end
|
43
34
|
end
|
@@ -18,7 +18,9 @@ module RuboCop
|
|
18
18
|
# str.squeeze('a')
|
19
19
|
# str.squeeze!('a')
|
20
20
|
#
|
21
|
-
class Squeeze <
|
21
|
+
class Squeeze < Base
|
22
|
+
extend AutoCorrector
|
23
|
+
|
22
24
|
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
23
25
|
|
24
26
|
PREFERRED_METHODS = {
|
@@ -36,24 +38,18 @@ module RuboCop
|
|
36
38
|
PATTERN
|
37
39
|
|
38
40
|
def on_send(node)
|
39
|
-
squeeze_candidate?(node) do |
|
41
|
+
squeeze_candidate?(node) do |receiver, bad_method, regexp_str, replace_str|
|
40
42
|
regexp_str = regexp_str[0..-2] # delete '+' from the end
|
41
43
|
regexp_str = interpret_string_escapes(regexp_str)
|
42
44
|
return unless replace_str == regexp_str
|
43
45
|
|
44
46
|
good_method = PREFERRED_METHODS[bad_method]
|
45
47
|
message = format(MSG, current: bad_method, prefer: good_method)
|
46
|
-
add_offense(node, location: :selector, message: message)
|
47
|
-
end
|
48
|
-
end
|
49
48
|
|
50
|
-
|
51
|
-
squeeze_candidate?(node) do |receiver, bad_method, _regexp_str, replace_str|
|
52
|
-
lambda do |corrector|
|
53
|
-
good_method = PREFERRED_METHODS[bad_method]
|
49
|
+
add_offense(node.loc.selector, message: message) do |corrector|
|
54
50
|
string_literal = to_string_literal(replace_str)
|
55
|
-
|
56
51
|
new_code = "#{receiver.source}.#{good_method}(#{string_literal})"
|
52
|
+
|
57
53
|
corrector.replace(node.source_range, new_code)
|
58
54
|
end
|
59
55
|
end
|
@@ -41,8 +41,9 @@ module RuboCop
|
|
41
41
|
# 'abc'.match(/^ab/)
|
42
42
|
# /^ab/.match('abc')
|
43
43
|
#
|
44
|
-
class StartWith <
|
44
|
+
class StartWith < Base
|
45
45
|
include RegexpMetacharacter
|
46
|
+
extend AutoCorrector
|
46
47
|
|
47
48
|
MSG = 'Use `String#start_with?` instead of a regex match anchored to ' \
|
48
49
|
'the beginning of the string.'
|
@@ -54,25 +55,19 @@ module RuboCop
|
|
54
55
|
PATTERN
|
55
56
|
|
56
57
|
def on_send(node)
|
57
|
-
return unless redundant_regex?(node)
|
58
|
+
return unless (receiver, regex_str = redundant_regex?(node))
|
58
59
|
|
59
|
-
add_offense(node)
|
60
|
-
end
|
61
|
-
alias on_match_with_lvasgn on_send
|
62
|
-
|
63
|
-
def autocorrect(node)
|
64
|
-
redundant_regex?(node) do |receiver, regex_str|
|
60
|
+
add_offense(node) do |corrector|
|
65
61
|
receiver, regex_str = regex_str, receiver if receiver.is_a?(String)
|
66
62
|
regex_str = drop_start_metacharacter(regex_str)
|
67
63
|
regex_str = interpret_string_escapes(regex_str)
|
68
64
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
corrector.replace(node.source_range, new_source)
|
73
|
-
end
|
65
|
+
new_source = "#{receiver.source}.start_with?(#{to_string_literal(regex_str)})"
|
66
|
+
|
67
|
+
corrector.replace(node.source_range, new_source)
|
74
68
|
end
|
75
69
|
end
|
70
|
+
alias on_match_with_lvasgn on_send
|
76
71
|
end
|
77
72
|
end
|
78
73
|
end
|
@@ -19,7 +19,9 @@ module RuboCop
|
|
19
19
|
#
|
20
20
|
# # good
|
21
21
|
# 'abc'.include?('ab')
|
22
|
-
class StringInclude <
|
22
|
+
class StringInclude < Base
|
23
|
+
extend AutoCorrector
|
24
|
+
|
23
25
|
MSG = 'Use `String#include?` instead of a regex match with literal-only pattern.'
|
24
26
|
|
25
27
|
def_node_matcher :redundant_regex?, <<~PATTERN
|
@@ -29,24 +31,18 @@ module RuboCop
|
|
29
31
|
PATTERN
|
30
32
|
|
31
33
|
def on_send(node)
|
32
|
-
return unless redundant_regex?(node)
|
33
|
-
|
34
|
-
add_offense(node)
|
35
|
-
end
|
36
|
-
alias on_match_with_lvasgn on_send
|
34
|
+
return unless (receiver, regex_str = redundant_regex?(node))
|
37
35
|
|
38
|
-
|
39
|
-
redundant_regex?(node) do |receiver, regex_str|
|
36
|
+
add_offense(node) do |corrector|
|
40
37
|
receiver, regex_str = regex_str, receiver if receiver.is_a?(String)
|
41
38
|
regex_str = interpret_string_escapes(regex_str)
|
42
39
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
corrector.replace(node.source_range, new_source)
|
47
|
-
end
|
40
|
+
new_source = "#{receiver.source}.include?(#{to_string_literal(regex_str)})"
|
41
|
+
|
42
|
+
corrector.replace(node.source_range, new_source)
|
48
43
|
end
|
49
44
|
end
|
45
|
+
alias on_match_with_lvasgn on_send
|
50
46
|
|
51
47
|
private
|
52
48
|
|
@@ -18,8 +18,9 @@ module RuboCop
|
|
18
18
|
# 'abc'.gsub(/a+/, 'd')
|
19
19
|
# 'abc'.tr('b', 'd')
|
20
20
|
# 'a b c'.delete(' ')
|
21
|
-
class StringReplacement <
|
21
|
+
class StringReplacement < Base
|
22
22
|
include RangeHelp
|
23
|
+
extend AutoCorrector
|
23
24
|
|
24
25
|
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
25
26
|
DETERMINISTIC_REGEX = /\A(?:#{LITERAL_REGEX})+\Z/.freeze
|
@@ -42,33 +43,37 @@ module RuboCop
|
|
42
43
|
end
|
43
44
|
end
|
44
45
|
|
45
|
-
|
46
|
+
private
|
47
|
+
|
48
|
+
def offense(node, first_param, second_param)
|
49
|
+
first_source, = first_source(first_param)
|
50
|
+
first_source = interpret_string_escapes(first_source) unless first_param.str_type?
|
51
|
+
second_source, = *second_param
|
52
|
+
message = message(node, first_source, second_source)
|
53
|
+
|
54
|
+
add_offense(range(node), message: message) do |corrector|
|
55
|
+
autocorrect(corrector, node)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def autocorrect(corrector, node)
|
46
60
|
_string, _method, first_param, second_param = *node
|
47
61
|
first_source, = first_source(first_param)
|
48
62
|
second_source, = *second_param
|
49
63
|
|
50
64
|
first_source = interpret_string_escapes(first_source) unless first_param.str_type?
|
51
65
|
|
52
|
-
|
53
|
-
replacement_method(node, first_source, second_source)
|
54
|
-
|
55
|
-
replace_method(node, first_source, second_source, first_param,
|
56
|
-
replacement_method)
|
66
|
+
replace_method(corrector, node, first_source, second_source, first_param)
|
57
67
|
end
|
58
68
|
|
59
|
-
def replace_method(node,
|
60
|
-
|
61
|
-
corrector.replace(node.loc.selector, replacement)
|
62
|
-
unless first_param.str_type?
|
63
|
-
corrector.replace(first_param.source_range,
|
64
|
-
to_string_literal(first))
|
65
|
-
end
|
69
|
+
def replace_method(corrector, node, first_source, second_source, first_param)
|
70
|
+
replacement_method = replacement_method(node, first_source, second_source)
|
66
71
|
|
67
|
-
|
68
|
-
|
69
|
-
end
|
72
|
+
corrector.replace(node.loc.selector, replacement_method)
|
73
|
+
corrector.replace(first_param.source_range, to_string_literal(first_source)) unless first_param.str_type?
|
70
74
|
|
71
|
-
|
75
|
+
remove_second_param(corrector, node, first_param) if second_source.empty? && first_source.length == 1
|
76
|
+
end
|
72
77
|
|
73
78
|
def accept_second_param?(second_param)
|
74
79
|
second_source, = *second_param
|
@@ -92,15 +97,6 @@ module RuboCop
|
|
92
97
|
first_source.length != 1
|
93
98
|
end
|
94
99
|
|
95
|
-
def offense(node, first_param, second_param)
|
96
|
-
first_source, = first_source(first_param)
|
97
|
-
first_source = interpret_string_escapes(first_source) unless first_param.str_type?
|
98
|
-
second_source, = *second_param
|
99
|
-
message = message(node, first_source, second_source)
|
100
|
-
|
101
|
-
add_offense(node, location: range(node), message: message)
|
102
|
-
end
|
103
|
-
|
104
100
|
def first_source(first_param)
|
105
101
|
case first_param.type
|
106
102
|
when :regexp
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop identifies places where custom code finding the sum of elements
|
7
|
+
# in some Enumerable object can be replaced by `Enumerable#sum` method.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# [1, 2, 3].inject(:+)
|
12
|
+
# [1, 2, 3].reduce(10, :+)
|
13
|
+
# [1, 2, 3].reduce { |acc, elem| acc + elem }
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# [1, 2, 3].sum
|
17
|
+
# [1, 2, 3].sum(10)
|
18
|
+
# [1, 2, 3].sum
|
19
|
+
#
|
20
|
+
class Sum < Base
|
21
|
+
include RangeHelp
|
22
|
+
extend AutoCorrector
|
23
|
+
|
24
|
+
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
25
|
+
|
26
|
+
def_node_matcher :sum_candidate?, <<~PATTERN
|
27
|
+
(send _ ${:inject :reduce} $_init ? (sym :+))
|
28
|
+
PATTERN
|
29
|
+
|
30
|
+
def_node_matcher :sum_with_block_candidate?, <<~PATTERN
|
31
|
+
(block
|
32
|
+
$(send _ {:inject :reduce} $_init ?)
|
33
|
+
(args (arg $_acc) (arg $_elem))
|
34
|
+
$send)
|
35
|
+
PATTERN
|
36
|
+
|
37
|
+
def_node_matcher :acc_plus_elem?, <<~PATTERN
|
38
|
+
(send (lvar %1) :+ (lvar %2))
|
39
|
+
PATTERN
|
40
|
+
alias elem_plus_acc? acc_plus_elem?
|
41
|
+
|
42
|
+
def on_send(node)
|
43
|
+
sum_candidate?(node) do |method, init|
|
44
|
+
range = sum_method_range(node)
|
45
|
+
message = build_method_message(method, init)
|
46
|
+
|
47
|
+
add_offense(range, message: message) do |corrector|
|
48
|
+
autocorrect(corrector, init, range)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def on_block(node)
|
54
|
+
sum_with_block_candidate?(node) do |send, init, var_acc, var_elem, body|
|
55
|
+
if acc_plus_elem?(body, var_acc, var_elem) || elem_plus_acc?(body, var_elem, var_acc)
|
56
|
+
range = sum_block_range(send, node)
|
57
|
+
message = build_block_message(send, init, var_acc, var_elem, body)
|
58
|
+
|
59
|
+
add_offense(range, message: message) do |corrector|
|
60
|
+
autocorrect(corrector, init, range)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def autocorrect(corrector, init, range)
|
69
|
+
return if init.empty?
|
70
|
+
|
71
|
+
replacement = build_good_method(init)
|
72
|
+
|
73
|
+
corrector.replace(range, replacement)
|
74
|
+
end
|
75
|
+
|
76
|
+
def sum_method_range(node)
|
77
|
+
range_between(node.loc.selector.begin_pos, node.loc.end.end_pos)
|
78
|
+
end
|
79
|
+
|
80
|
+
def sum_block_range(send, node)
|
81
|
+
range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
|
82
|
+
end
|
83
|
+
|
84
|
+
def build_method_message(method, init)
|
85
|
+
good_method = build_good_method(init)
|
86
|
+
bad_method = build_method_bad_method(init, method)
|
87
|
+
format(MSG, good_method: good_method, bad_method: bad_method)
|
88
|
+
end
|
89
|
+
|
90
|
+
def build_block_message(send, init, var_acc, var_elem, body)
|
91
|
+
good_method = build_good_method(init)
|
92
|
+
bad_method = build_block_bad_method(send.method_name, init, var_acc, var_elem, body)
|
93
|
+
format(MSG, good_method: good_method, bad_method: bad_method)
|
94
|
+
end
|
95
|
+
|
96
|
+
def build_good_method(init)
|
97
|
+
good_method = 'sum'
|
98
|
+
|
99
|
+
unless init.empty?
|
100
|
+
init = init.first
|
101
|
+
good_method += "(#{init.source})" if init.source.to_i != 0
|
102
|
+
end
|
103
|
+
good_method
|
104
|
+
end
|
105
|
+
|
106
|
+
def build_method_bad_method(init, method)
|
107
|
+
bad_method = "#{method}("
|
108
|
+
unless init.empty?
|
109
|
+
init = init.first
|
110
|
+
bad_method += "#{init.source}, "
|
111
|
+
end
|
112
|
+
bad_method += ':+)'
|
113
|
+
bad_method
|
114
|
+
end
|
115
|
+
|
116
|
+
def build_block_bad_method(method, init, var_acc, var_elem, body)
|
117
|
+
bad_method = method.to_s
|
118
|
+
|
119
|
+
unless init.empty?
|
120
|
+
init = init.first
|
121
|
+
bad_method += "(#{init.source})"
|
122
|
+
end
|
123
|
+
bad_method += " { |#{var_acc}, #{var_elem}| #{body.source} }"
|
124
|
+
bad_method
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|