rubocop-performance 1.6.1 → 1.9.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/README.md +8 -0
- data/config/default.yml +95 -8
- data/lib/rubocop/cop/mixin/regexp_metacharacter.rb +4 -4
- data/lib/rubocop/cop/mixin/sort_block.rb +28 -0
- data/lib/rubocop/cop/performance/ancestors_include.rb +49 -0
- data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +74 -0
- data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +46 -0
- data/lib/rubocop/cop/performance/bind_call.rb +9 -18
- data/lib/rubocop/cop/performance/block_given_with_explicit_block.rb +52 -0
- data/lib/rubocop/cop/performance/caller.rb +14 -15
- data/lib/rubocop/cop/performance/case_when_splat.rb +18 -11
- data/lib/rubocop/cop/performance/casecmp.rb +13 -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/constant_regexp.rb +68 -0
- data/lib/rubocop/cop/performance/count.rb +14 -16
- data/lib/rubocop/cop/performance/delete_prefix.rb +14 -22
- data/lib/rubocop/cop/performance/delete_suffix.rb +14 -22
- data/lib/rubocop/cop/performance/detect.rb +65 -32
- data/lib/rubocop/cop/performance/double_start_end_with.rb +16 -24
- data/lib/rubocop/cop/performance/end_with.rb +9 -13
- data/lib/rubocop/cop/performance/fixed_size.rb +2 -1
- data/lib/rubocop/cop/performance/flat_map.rb +21 -22
- data/lib/rubocop/cop/performance/inefficient_hash_search.rb +15 -14
- data/lib/rubocop/cop/performance/io_readlines.rb +112 -0
- data/lib/rubocop/cop/performance/method_object_as_block.rb +32 -0
- data/lib/rubocop/cop/performance/open_struct.rb +3 -2
- data/lib/rubocop/cop/performance/range_include.rb +15 -11
- data/lib/rubocop/cop/performance/redundant_block_call.rb +15 -10
- data/lib/rubocop/cop/performance/redundant_match.rb +12 -6
- data/lib/rubocop/cop/performance/redundant_merge.rb +19 -17
- data/lib/rubocop/cop/performance/redundant_sort_block.rb +43 -0
- data/lib/rubocop/cop/performance/redundant_string_chars.rb +129 -0
- data/lib/rubocop/cop/performance/regexp_match.rb +20 -20
- data/lib/rubocop/cop/performance/reverse_each.rb +10 -5
- data/lib/rubocop/cop/performance/reverse_first.rb +73 -0
- data/lib/rubocop/cop/performance/size.rb +42 -43
- data/lib/rubocop/cop/performance/sort_reverse.rb +45 -0
- data/lib/rubocop/cop/performance/squeeze.rb +67 -0
- data/lib/rubocop/cop/performance/start_with.rb +9 -13
- data/lib/rubocop/cop/performance/string_include.rb +56 -0
- data/lib/rubocop/cop/performance/string_replacement.rb +24 -27
- data/lib/rubocop/cop/performance/sum.rb +236 -0
- data/lib/rubocop/cop/performance/times_map.rb +12 -18
- data/lib/rubocop/cop/performance/unfreeze_string.rb +20 -2
- data/lib/rubocop/cop/performance/uri_default_parser.rb +7 -12
- data/lib/rubocop/cop/performance_cops.rb +16 -0
- data/lib/rubocop/performance/version.rb +6 -1
- metadata +35 -13
@@ -17,9 +17,12 @@ 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.'
|
25
|
+
RESTRICT_ON_SEND = %i[match].freeze
|
23
26
|
|
24
27
|
# 'match' is a fairly generic name, so we don't flag it unless we see
|
25
28
|
# a string or regexp literal on one side or the other
|
@@ -37,18 +40,21 @@ module RuboCop
|
|
37
40
|
(!node.value_used? || only_truthiness_matters?(node)) &&
|
38
41
|
!(node.parent && node.parent.block_type?)
|
39
42
|
|
40
|
-
add_offense(node)
|
43
|
+
add_offense(node) do |corrector|
|
44
|
+
autocorrect(corrector, node)
|
45
|
+
end
|
41
46
|
end
|
42
47
|
|
43
|
-
|
48
|
+
private
|
49
|
+
|
50
|
+
def autocorrect(corrector, node)
|
44
51
|
# Regexp#match can take a second argument, but this cop doesn't
|
45
52
|
# register an offense in that case
|
46
53
|
return unless node.first_argument.regexp_type?
|
47
54
|
|
48
|
-
new_source =
|
49
|
-
node.receiver.source + ' =~ ' + node.first_argument.source
|
55
|
+
new_source = "#{node.receiver.source} =~ #{node.first_argument.source}"
|
50
56
|
|
51
|
-
|
57
|
+
corrector.replace(node.source_range, new_source)
|
52
58
|
end
|
53
59
|
end
|
54
60
|
end
|
@@ -24,9 +24,12 @@ 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`.'
|
32
|
+
RESTRICT_ON_SEND = %i[merge!].freeze
|
30
33
|
|
31
34
|
WITH_MODIFIER_CORRECTION = <<~RUBY
|
32
35
|
%<keyword>s %<condition>s
|
@@ -44,18 +47,17 @@ module RuboCop
|
|
44
47
|
|
45
48
|
def on_send(node)
|
46
49
|
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)
|
50
|
+
message = message(node)
|
51
|
+
add_offense(redundant_merge_node, message: message) do |corrector|
|
52
|
+
redundant_merge_candidate(node) do |receiver, pairs|
|
53
|
+
new_source = to_assignments(receiver, pairs).join("\n")
|
54
|
+
|
55
|
+
if node.parent && pairs.size > 1
|
56
|
+
correct_multiple_elements(corrector, node, node.parent, new_source)
|
57
|
+
else
|
58
|
+
correct_single_element(corrector, node, new_source)
|
59
|
+
end
|
60
|
+
end
|
59
61
|
end
|
60
62
|
end
|
61
63
|
end
|
@@ -98,7 +100,7 @@ module RuboCop
|
|
98
100
|
!EachWithObjectInspector.new(node, receiver).value_used?
|
99
101
|
end
|
100
102
|
|
101
|
-
def correct_multiple_elements(node, parent, new_source)
|
103
|
+
def correct_multiple_elements(corrector, node, parent, new_source)
|
102
104
|
if modifier_flow_control?(parent)
|
103
105
|
new_source = rewrite_with_modifier(node, parent, new_source)
|
104
106
|
node = parent
|
@@ -107,11 +109,11 @@ module RuboCop
|
|
107
109
|
new_source.gsub!(/\n/, padding)
|
108
110
|
end
|
109
111
|
|
110
|
-
|
112
|
+
corrector.replace(node.source_range, new_source)
|
111
113
|
end
|
112
114
|
|
113
|
-
def correct_single_element(node, new_source)
|
114
|
-
|
115
|
+
def correct_single_element(corrector, node, new_source)
|
116
|
+
corrector.replace(node.source_range, new_source)
|
115
117
|
end
|
116
118
|
|
117
119
|
def to_assignments(receiver, pairs)
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop identifies places where `sort { |a, b| a <=> b }`
|
7
|
+
# can be replaced with `sort`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# array.sort { |a, b| a <=> b }
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# array.sort
|
15
|
+
#
|
16
|
+
class RedundantSortBlock < Base
|
17
|
+
include SortBlock
|
18
|
+
extend AutoCorrector
|
19
|
+
|
20
|
+
MSG = 'Use `sort` instead of `%<bad_method>s`.'
|
21
|
+
|
22
|
+
def on_block(node)
|
23
|
+
return unless (send, var_a, var_b, body = sort_with_block?(node))
|
24
|
+
|
25
|
+
replaceable_body?(body, var_a, var_b) do
|
26
|
+
range = sort_range(send, node)
|
27
|
+
|
28
|
+
add_offense(range, message: message(var_a, var_b)) do |corrector|
|
29
|
+
corrector.replace(range, 'sort')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def message(var_a, var_b)
|
37
|
+
bad_method = "sort { |#{var_a}, #{var_b}| #{var_a} <=> #{var_b} }"
|
38
|
+
format(MSG, bad_method: bad_method)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop checks for redundant `String#chars`.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# str.chars[0..2]
|
11
|
+
# str.chars.slice(0..2)
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# str[0..2].chars
|
15
|
+
#
|
16
|
+
# # bad
|
17
|
+
# str.chars.first
|
18
|
+
# str.chars.first(2)
|
19
|
+
# str.chars.last
|
20
|
+
# str.chars.last(2)
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# str[0]
|
24
|
+
# str[0...2].chars
|
25
|
+
# str[-1]
|
26
|
+
# str[-2..-1].chars
|
27
|
+
#
|
28
|
+
# # bad
|
29
|
+
# str.chars.take(2)
|
30
|
+
# str.chars.drop(2)
|
31
|
+
# str.chars.length
|
32
|
+
# str.chars.size
|
33
|
+
# str.chars.empty?
|
34
|
+
#
|
35
|
+
# # good
|
36
|
+
# str[0...2].chars
|
37
|
+
# str[2..-1].chars
|
38
|
+
# str.length
|
39
|
+
# str.size
|
40
|
+
# str.empty?
|
41
|
+
#
|
42
|
+
class RedundantStringChars < Base
|
43
|
+
include RangeHelp
|
44
|
+
extend AutoCorrector
|
45
|
+
|
46
|
+
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
47
|
+
RESTRICT_ON_SEND = %i[[] slice first last take drop length size empty?].freeze
|
48
|
+
|
49
|
+
def_node_matcher :redundant_chars_call?, <<~PATTERN
|
50
|
+
(send $(send _ :chars) $_ $...)
|
51
|
+
PATTERN
|
52
|
+
|
53
|
+
def on_send(node)
|
54
|
+
return unless (receiver, method, args = redundant_chars_call?(node))
|
55
|
+
|
56
|
+
range = offense_range(receiver, node)
|
57
|
+
message = build_message(method, args)
|
58
|
+
|
59
|
+
add_offense(range, message: message) do |corrector|
|
60
|
+
range = correction_range(receiver, node)
|
61
|
+
replacement = build_good_method(method, args)
|
62
|
+
|
63
|
+
corrector.replace(range, replacement)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def offense_range(receiver, node)
|
70
|
+
range_between(receiver.loc.selector.begin_pos, node.loc.expression.end_pos)
|
71
|
+
end
|
72
|
+
|
73
|
+
def correction_range(receiver, node)
|
74
|
+
range_between(receiver.loc.dot.begin_pos, node.loc.expression.end_pos)
|
75
|
+
end
|
76
|
+
|
77
|
+
def build_message(method, args)
|
78
|
+
good_method = build_good_method(method, args)
|
79
|
+
bad_method = build_bad_method(method, args)
|
80
|
+
format(MSG, good_method: good_method, bad_method: bad_method)
|
81
|
+
end
|
82
|
+
|
83
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
84
|
+
def build_good_method(method, args)
|
85
|
+
case method
|
86
|
+
when :[], :slice
|
87
|
+
"[#{build_call_args(args)}].chars"
|
88
|
+
when :first
|
89
|
+
if args.any?
|
90
|
+
"[0...#{args.first.source}].chars"
|
91
|
+
else
|
92
|
+
'[0]'
|
93
|
+
end
|
94
|
+
when :last
|
95
|
+
if args.any?
|
96
|
+
"[-#{args.first.source}..-1].chars"
|
97
|
+
else
|
98
|
+
'[-1]'
|
99
|
+
end
|
100
|
+
when :take
|
101
|
+
"[0...#{args.first.source}].chars"
|
102
|
+
when :drop
|
103
|
+
"[#{args.first.source}..-1].chars"
|
104
|
+
else
|
105
|
+
".#{method}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
109
|
+
|
110
|
+
def build_bad_method(method, args)
|
111
|
+
case method
|
112
|
+
when :[]
|
113
|
+
"chars[#{build_call_args(args)}]"
|
114
|
+
else
|
115
|
+
if args.any?
|
116
|
+
"chars.#{method}(#{build_call_args(args)})"
|
117
|
+
else
|
118
|
+
"chars.#{method}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def build_call_args(call_args_node)
|
124
|
+
call_args_node.map(&:source).join(', ')
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -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
|
|
@@ -12,10 +12,12 @@ 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`.'
|
20
|
+
RESTRICT_ON_SEND = %i[each].freeze
|
19
21
|
UNDERSCORE = '_'
|
20
22
|
|
21
23
|
def_node_matcher :reverse_each?, <<~MATCHER
|
@@ -29,13 +31,16 @@ module RuboCop
|
|
29
31
|
|
30
32
|
range = range_between(location_of_reverse, end_location)
|
31
33
|
|
32
|
-
add_offense(
|
34
|
+
add_offense(range) do |corrector|
|
35
|
+
corrector.replace(replacement_range(node), UNDERSCORE)
|
36
|
+
end
|
33
37
|
end
|
34
38
|
end
|
35
39
|
|
36
|
-
|
37
|
-
|
38
|
-
|
40
|
+
private
|
41
|
+
|
42
|
+
def replacement_range(node)
|
43
|
+
range_between(node.loc.dot.begin_pos, node.loc.selector.begin_pos)
|
39
44
|
end
|
40
45
|
end
|
41
46
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop identifies places where `reverse.first(n)` and `reverse.first`
|
7
|
+
# can be replaced by `last(n).reverse` and `last`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# # bad
|
12
|
+
# array.reverse.first(5)
|
13
|
+
# array.reverse.first
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# array.last(5).reverse
|
17
|
+
# array.last
|
18
|
+
#
|
19
|
+
class ReverseFirst < Base
|
20
|
+
include RangeHelp
|
21
|
+
extend AutoCorrector
|
22
|
+
|
23
|
+
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
24
|
+
RESTRICT_ON_SEND = %i[first].freeze
|
25
|
+
|
26
|
+
def_node_matcher :reverse_first_candidate?, <<~PATTERN
|
27
|
+
(send $(send _ :reverse) :first (int _)?)
|
28
|
+
PATTERN
|
29
|
+
|
30
|
+
def on_send(node)
|
31
|
+
reverse_first_candidate?(node) do |receiver|
|
32
|
+
range = correction_range(receiver, node)
|
33
|
+
message = build_message(node)
|
34
|
+
|
35
|
+
add_offense(range, message: message) do |corrector|
|
36
|
+
replacement = build_good_method(node)
|
37
|
+
|
38
|
+
corrector.replace(range, replacement)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def correction_range(receiver, node)
|
46
|
+
range_between(receiver.loc.selector.begin_pos, node.loc.expression.end_pos)
|
47
|
+
end
|
48
|
+
|
49
|
+
def build_message(node)
|
50
|
+
good_method = build_good_method(node)
|
51
|
+
bad_method = build_bad_method(node)
|
52
|
+
format(MSG, good_method: good_method, bad_method: bad_method)
|
53
|
+
end
|
54
|
+
|
55
|
+
def build_good_method(node)
|
56
|
+
if node.arguments?
|
57
|
+
"last(#{node.arguments.first.source}).reverse"
|
58
|
+
else
|
59
|
+
'last'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def build_bad_method(node)
|
64
|
+
if node.arguments?
|
65
|
+
"reverse.first(#{node.arguments.first.source})"
|
66
|
+
else
|
67
|
+
'reverse.first'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|