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
@@ -36,7 +36,9 @@ module RuboCop
|
|
36
36
|
# { a: 1, b: 2 }.has_value?('garbage')
|
37
37
|
# h = { a: 1, b: 2 }; h.value?(nil)
|
38
38
|
#
|
39
|
-
class InefficientHashSearch <
|
39
|
+
class InefficientHashSearch < Base
|
40
|
+
extend AutoCorrector
|
41
|
+
|
40
42
|
def_node_matcher :inefficient_include?, <<~PATTERN
|
41
43
|
(send (send $_ {:keys :values}) :include? _)
|
42
44
|
PATTERN
|
@@ -45,19 +47,16 @@ module RuboCop
|
|
45
47
|
inefficient_include?(node) do |receiver|
|
46
48
|
return if receiver.nil?
|
47
49
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
"#{autocorrect_hash_expression(node)}."\
|
59
|
-
"#{autocorrect_method(node)}(#{autocorrect_argument(node)})"
|
60
|
-
)
|
50
|
+
message = message(node)
|
51
|
+
add_offense(node, message: message) do |corrector|
|
52
|
+
# Replace `keys.include?` or `values.include?` with the appropriate
|
53
|
+
# `key?`/`value?` method.
|
54
|
+
corrector.replace(
|
55
|
+
node.loc.expression,
|
56
|
+
"#{autocorrect_hash_expression(node)}."\
|
57
|
+
"#{autocorrect_method(node)}(#{autocorrect_argument(node)})"
|
58
|
+
)
|
59
|
+
end
|
61
60
|
end
|
62
61
|
end
|
63
62
|
|
@@ -24,8 +24,9 @@ module RuboCop
|
|
24
24
|
# file.each_line.find { |l| l.start_with?('#') }
|
25
25
|
# file.each_line { |l| puts l }
|
26
26
|
#
|
27
|
-
class IoReadlines <
|
27
|
+
class IoReadlines < Base
|
28
28
|
include RangeHelp
|
29
|
+
extend AutoCorrector
|
29
30
|
|
30
31
|
MSG = 'Use `%<good>s` instead of `%<bad>s`.'
|
31
32
|
ENUMERABLE_METHODS = (Enumerable.instance_methods + [:each]).freeze
|
@@ -39,34 +40,16 @@ module RuboCop
|
|
39
40
|
PATTERN
|
40
41
|
|
41
42
|
def on_send(node)
|
42
|
-
readlines_on_class?(node)
|
43
|
-
offense(node, enumerable_call, readlines_call)
|
44
|
-
end
|
43
|
+
return unless (captured_values = readlines_on_class?(node) || readlines_on_instance?(node))
|
45
44
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
45
|
+
enumerable_call, readlines_call, receiver = *captured_values
|
46
|
+
|
47
|
+
range = offense_range(enumerable_call, readlines_call)
|
48
|
+
good_method = build_good_method(enumerable_call)
|
49
|
+
bad_method = build_bad_method(enumerable_call)
|
50
50
|
|
51
|
-
|
52
|
-
|
53
|
-
# We cannot safely correct `.readlines` method called on IO/File classes
|
54
|
-
# due to its signature and we are not sure with implicit receiver
|
55
|
-
# if it is called in the context of some instance or mentioned class.
|
56
|
-
return if receiver.nil?
|
57
|
-
|
58
|
-
lambda do |corrector|
|
59
|
-
range = correction_range(enumerable_call, readlines_call)
|
60
|
-
|
61
|
-
if readlines_call.arguments?
|
62
|
-
call_args = build_call_args(readlines_call.arguments)
|
63
|
-
replacement = "each_line(#{call_args})"
|
64
|
-
else
|
65
|
-
replacement = 'each_line'
|
66
|
-
end
|
67
|
-
|
68
|
-
corrector.replace(range, replacement)
|
69
|
-
end
|
51
|
+
add_offense(range, message: format(MSG, good: good_method, bad: bad_method)) do |corrector|
|
52
|
+
autocorrect(corrector, enumerable_call, readlines_call, receiver)
|
70
53
|
end
|
71
54
|
end
|
72
55
|
|
@@ -76,16 +59,22 @@ module RuboCop
|
|
76
59
|
ENUMERABLE_METHODS.include?(node.to_sym)
|
77
60
|
end
|
78
61
|
|
79
|
-
def
|
80
|
-
|
81
|
-
|
82
|
-
|
62
|
+
def autocorrect(corrector, enumerable_call, readlines_call, receiver)
|
63
|
+
# We cannot safely correct `.readlines` method called on IO/File classes
|
64
|
+
# due to its signature and we are not sure with implicit receiver
|
65
|
+
# if it is called in the context of some instance or mentioned class.
|
66
|
+
return if receiver.nil?
|
67
|
+
|
68
|
+
range = correction_range(enumerable_call, readlines_call)
|
69
|
+
|
70
|
+
if readlines_call.arguments?
|
71
|
+
call_args = build_call_args(readlines_call.arguments)
|
72
|
+
replacement = "each_line(#{call_args})"
|
73
|
+
else
|
74
|
+
replacement = 'each_line'
|
75
|
+
end
|
83
76
|
|
84
|
-
|
85
|
-
node,
|
86
|
-
location: range,
|
87
|
-
message: format(MSG, good: good_method, bad: bad_method)
|
88
|
-
)
|
77
|
+
corrector.replace(range, replacement)
|
89
78
|
end
|
90
79
|
|
91
80
|
def offense_range(enumerable_call, readlines_call)
|
@@ -27,7 +27,7 @@ module RuboCop
|
|
27
27
|
# end
|
28
28
|
# end
|
29
29
|
#
|
30
|
-
class OpenStruct <
|
30
|
+
class OpenStruct < Base
|
31
31
|
MSG = 'Consider using `Struct` over `OpenStruct` ' \
|
32
32
|
'to optimize the performance.'
|
33
33
|
|
@@ -37,7 +37,7 @@ module RuboCop
|
|
37
37
|
|
38
38
|
def on_send(node)
|
39
39
|
open_struct(node) do
|
40
|
-
add_offense(node
|
40
|
+
add_offense(node.loc.selector)
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
@@ -24,7 +24,9 @@ module RuboCop
|
|
24
24
|
# # the desired result:
|
25
25
|
#
|
26
26
|
# ('a'..'z').cover?('yellow') # => true
|
27
|
-
class RangeInclude <
|
27
|
+
class RangeInclude < Base
|
28
|
+
extend AutoCorrector
|
29
|
+
|
28
30
|
MSG = 'Use `Range#cover?` instead of `Range#%<bad_method>s`.'
|
29
31
|
|
30
32
|
# TODO: If we traced out assignments of variables to their uses, we
|
@@ -39,12 +41,11 @@ module RuboCop
|
|
39
41
|
def on_send(node)
|
40
42
|
range_include(node) do |bad_method|
|
41
43
|
message = format(MSG, bad_method: bad_method)
|
42
|
-
add_offense(node, location: :selector, message: message)
|
43
|
-
end
|
44
|
-
end
|
45
44
|
|
46
|
-
|
47
|
-
|
45
|
+
add_offense(node.loc.selector, message: message) do |corrector|
|
46
|
+
corrector.replace(node.loc.selector, 'cover?')
|
47
|
+
end
|
48
|
+
end
|
48
49
|
end
|
49
50
|
end
|
50
51
|
end
|
@@ -22,7 +22,9 @@ module RuboCop
|
|
22
22
|
# def another
|
23
23
|
# yield 1, 2, 3
|
24
24
|
# end
|
25
|
-
class RedundantBlockCall <
|
25
|
+
class RedundantBlockCall < Base
|
26
|
+
extend AutoCorrector
|
27
|
+
|
26
28
|
MSG = 'Use `yield` instead of `%<argname>s.call`.'
|
27
29
|
YIELD = 'yield'
|
28
30
|
OPEN_PAREN = '('
|
@@ -47,13 +49,17 @@ module RuboCop
|
|
47
49
|
next unless body
|
48
50
|
|
49
51
|
calls_to_report(argname, body).each do |blockcall|
|
50
|
-
add_offense(blockcall, message: format(MSG, argname: argname))
|
52
|
+
add_offense(blockcall, message: format(MSG, argname: argname)) do |corrector|
|
53
|
+
autocorrect(corrector, blockcall)
|
54
|
+
end
|
51
55
|
end
|
52
56
|
end
|
53
57
|
end
|
54
58
|
|
59
|
+
private
|
60
|
+
|
55
61
|
# offenses are registered on the `block.call` nodes
|
56
|
-
def autocorrect(node)
|
62
|
+
def autocorrect(corrector, node)
|
57
63
|
_receiver, _method, *args = *node
|
58
64
|
new_source = String.new(YIELD)
|
59
65
|
unless args.empty?
|
@@ -67,10 +73,9 @@ module RuboCop
|
|
67
73
|
end
|
68
74
|
|
69
75
|
new_source << CLOSE_PAREN if parentheses?(node) && !args.empty?
|
70
|
-
->(corrector) { corrector.replace(node.source_range, new_source) }
|
71
|
-
end
|
72
76
|
|
73
|
-
|
77
|
+
corrector.replace(node.source_range, new_source)
|
78
|
+
end
|
74
79
|
|
75
80
|
def calls_to_report(argname, body)
|
76
81
|
return [] if blockarg_assigned?(body, argname)
|
@@ -17,7 +17,9 @@ module RuboCop
|
|
17
17
|
# # good
|
18
18
|
# method(str =~ /regex/)
|
19
19
|
# return value unless regex =~ 'str'
|
20
|
-
class RedundantMatch <
|
20
|
+
class RedundantMatch < Base
|
21
|
+
extend AutoCorrector
|
22
|
+
|
21
23
|
MSG = 'Use `=~` in places where the `MatchData` returned by ' \
|
22
24
|
'`#match` will not be used.'
|
23
25
|
|
@@ -37,18 +39,21 @@ module RuboCop
|
|
37
39
|
(!node.value_used? || only_truthiness_matters?(node)) &&
|
38
40
|
!(node.parent && node.parent.block_type?)
|
39
41
|
|
40
|
-
add_offense(node)
|
42
|
+
add_offense(node) do |corrector|
|
43
|
+
autocorrect(corrector, node)
|
44
|
+
end
|
41
45
|
end
|
42
46
|
|
43
|
-
|
47
|
+
private
|
48
|
+
|
49
|
+
def autocorrect(corrector, node)
|
44
50
|
# Regexp#match can take a second argument, but this cop doesn't
|
45
51
|
# register an offense in that case
|
46
52
|
return unless node.first_argument.regexp_type?
|
47
53
|
|
48
|
-
new_source =
|
49
|
-
node.receiver.source + ' =~ ' + node.first_argument.source
|
54
|
+
new_source = "#{node.receiver.source} =~ #{node.first_argument.source}"
|
50
55
|
|
51
|
-
|
56
|
+
corrector.replace(node.source_range, new_source)
|
52
57
|
end
|
53
58
|
end
|
54
59
|
end
|
@@ -24,7 +24,9 @@ module RuboCop
|
|
24
24
|
# # good
|
25
25
|
# hash[:a] = 1
|
26
26
|
# hash[:b] = 2
|
27
|
-
class RedundantMerge <
|
27
|
+
class RedundantMerge < Base
|
28
|
+
extend AutoCorrector
|
29
|
+
|
28
30
|
AREF_ASGN = '%<receiver>s[%<key>s] = %<value>s'
|
29
31
|
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
30
32
|
|
@@ -44,18 +46,17 @@ module RuboCop
|
|
44
46
|
|
45
47
|
def on_send(node)
|
46
48
|
each_redundant_merge(node) do |redundant_merge_node|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
correct_single_element(node, new_source)
|
49
|
+
message = message(node)
|
50
|
+
add_offense(redundant_merge_node, message: message) do |corrector|
|
51
|
+
redundant_merge_candidate(node) do |receiver, pairs|
|
52
|
+
new_source = to_assignments(receiver, pairs).join("\n")
|
53
|
+
|
54
|
+
if node.parent && pairs.size > 1
|
55
|
+
correct_multiple_elements(corrector, node, node.parent, new_source)
|
56
|
+
else
|
57
|
+
correct_single_element(corrector, node, new_source)
|
58
|
+
end
|
59
|
+
end
|
59
60
|
end
|
60
61
|
end
|
61
62
|
end
|
@@ -98,7 +99,7 @@ module RuboCop
|
|
98
99
|
!EachWithObjectInspector.new(node, receiver).value_used?
|
99
100
|
end
|
100
101
|
|
101
|
-
def correct_multiple_elements(node, parent, new_source)
|
102
|
+
def correct_multiple_elements(corrector, node, parent, new_source)
|
102
103
|
if modifier_flow_control?(parent)
|
103
104
|
new_source = rewrite_with_modifier(node, parent, new_source)
|
104
105
|
node = parent
|
@@ -107,11 +108,11 @@ module RuboCop
|
|
107
108
|
new_source.gsub!(/\n/, padding)
|
108
109
|
end
|
109
110
|
|
110
|
-
|
111
|
+
corrector.replace(node.source_range, new_source)
|
111
112
|
end
|
112
113
|
|
113
|
-
def correct_single_element(node, new_source)
|
114
|
-
|
114
|
+
def correct_single_element(corrector, node, new_source)
|
115
|
+
corrector.replace(node.source_range, new_source)
|
115
116
|
end
|
116
117
|
|
117
118
|
def to_assignments(receiver, pairs)
|
@@ -13,29 +13,19 @@ module RuboCop
|
|
13
13
|
# # good
|
14
14
|
# array.sort
|
15
15
|
#
|
16
|
-
class RedundantSortBlock <
|
16
|
+
class RedundantSortBlock < Base
|
17
17
|
include SortBlock
|
18
|
+
extend AutoCorrector
|
18
19
|
|
19
20
|
MSG = 'Use `sort` instead of `%<bad_method>s`.'
|
20
21
|
|
21
22
|
def on_block(node)
|
22
|
-
|
23
|
-
replaceable_body?(body, var_a, var_b) do
|
24
|
-
range = sort_range(send, node)
|
23
|
+
return unless (send, var_a, var_b, body = sort_with_block?(node))
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
location: range,
|
29
|
-
message: message(var_a, var_b)
|
30
|
-
)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
25
|
+
replaceable_body?(body, var_a, var_b) do
|
26
|
+
range = sort_range(send, node)
|
34
27
|
|
35
|
-
|
36
|
-
sort_with_block?(node) do |send, _var_a, _var_b, _body|
|
37
|
-
lambda do |corrector|
|
38
|
-
range = sort_range(send, node)
|
28
|
+
add_offense(range, message: message(var_a, var_b)) do |corrector|
|
39
29
|
corrector.replace(range, 'sort')
|
40
30
|
end
|
41
31
|
end
|
@@ -39,8 +39,9 @@ module RuboCop
|
|
39
39
|
# str.size
|
40
40
|
# str.empty?
|
41
41
|
#
|
42
|
-
class RedundantStringChars <
|
42
|
+
class RedundantStringChars < Base
|
43
43
|
include RangeHelp
|
44
|
+
extend AutoCorrector
|
44
45
|
|
45
46
|
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
46
47
|
REPLACEABLE_METHODS = %i[[] slice first last take drop length size empty?].freeze
|
@@ -50,21 +51,16 @@ module RuboCop
|
|
50
51
|
PATTERN
|
51
52
|
|
52
53
|
def on_send(node)
|
53
|
-
|
54
|
-
range = offense_range(receiver, node)
|
55
|
-
message = build_message(method, args)
|
56
|
-
add_offense(node, location: range, message: message)
|
57
|
-
end
|
58
|
-
end
|
54
|
+
return unless (receiver, method, args = redundant_chars_call?(node))
|
59
55
|
|
60
|
-
|
61
|
-
|
56
|
+
range = offense_range(receiver, node)
|
57
|
+
message = build_message(method, args)
|
58
|
+
|
59
|
+
add_offense(range, message: message) do |corrector|
|
62
60
|
range = correction_range(receiver, node)
|
63
61
|
replacement = build_good_method(method, args)
|
64
62
|
|
65
|
-
|
66
|
-
corrector.replace(range, replacement)
|
67
|
-
end
|
63
|
+
corrector.replace(range, replacement)
|
68
64
|
end
|
69
65
|
end
|
70
66
|
|
@@ -72,7 +72,9 @@ module RuboCop
|
|
72
72
|
# do_something($~)
|
73
73
|
# end
|
74
74
|
# end
|
75
|
-
class RegexpMatch <
|
75
|
+
class RegexpMatch < Base
|
76
|
+
extend AutoCorrector
|
77
|
+
|
76
78
|
# Constants are included in this list because it is unlikely that
|
77
79
|
# someone will store `nil` as a constant and then use it for comparison
|
78
80
|
TYPES_IMPLEMENTING_MATCH = %i[const regexp str sym].freeze
|
@@ -141,27 +143,28 @@ module RuboCop
|
|
141
143
|
end
|
142
144
|
end
|
143
145
|
|
144
|
-
def autocorrect(node)
|
145
|
-
lambda do |corrector|
|
146
|
-
if match_method?(node) || match_with_int_arg_method?(node)
|
147
|
-
corrector.replace(node.loc.selector, 'match?')
|
148
|
-
elsif match_operator?(node) || match_threequals?(node)
|
149
|
-
recv, oper, arg = *node
|
150
|
-
correct_operator(corrector, recv, arg, oper)
|
151
|
-
elsif match_with_lvasgn?(node)
|
152
|
-
recv, arg = *node
|
153
|
-
correct_operator(corrector, recv, arg)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
146
|
private
|
159
147
|
|
160
148
|
def check_condition(cond)
|
161
149
|
match_node?(cond) do
|
162
150
|
return if last_match_used?(cond)
|
163
151
|
|
164
|
-
|
152
|
+
message = message(cond)
|
153
|
+
add_offense(cond, message: message) do |corrector|
|
154
|
+
autocorrect(corrector, cond)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def autocorrect(corrector, node)
|
160
|
+
if match_method?(node) || match_with_int_arg_method?(node)
|
161
|
+
corrector.replace(node.loc.selector, 'match?')
|
162
|
+
elsif match_operator?(node) || match_threequals?(node)
|
163
|
+
recv, oper, arg = *node
|
164
|
+
correct_operator(corrector, recv, arg, oper)
|
165
|
+
elsif match_with_lvasgn?(node)
|
166
|
+
recv, arg = *node
|
167
|
+
correct_operator(corrector, recv, arg)
|
165
168
|
end
|
166
169
|
end
|
167
170
|
|
@@ -231,10 +234,7 @@ module RuboCop
|
|
231
234
|
|
232
235
|
def scope_root(node)
|
233
236
|
node.each_ancestor.find do |ancestor|
|
234
|
-
ancestor.def_type? ||
|
235
|
-
ancestor.defs_type? ||
|
236
|
-
ancestor.class_type? ||
|
237
|
-
ancestor.module_type?
|
237
|
+
ancestor.def_type? || ancestor.defs_type? || ancestor.class_type? || ancestor.module_type?
|
238
238
|
end
|
239
239
|
end
|
240
240
|
|