rubocop-performance 1.5.2 → 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/LICENSE.txt +1 -1
- data/README.md +5 -1
- data/config/default.yml +96 -13
- data/lib/rubocop/cop/mixin/regexp_metacharacter.rb +76 -0
- data/lib/rubocop/cop/mixin/sort_block.rb +28 -0
- data/lib/rubocop/cop/performance/ancestors_include.rb +48 -0
- data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +45 -0
- data/lib/rubocop/cop/performance/bind_call.rb +77 -0
- data/lib/rubocop/cop/performance/caller.rb +5 -4
- data/lib/rubocop/cop/performance/case_when_splat.rb +18 -11
- data/lib/rubocop/cop/performance/casecmp.rb +17 -23
- data/lib/rubocop/cop/performance/chain_array_allocation.rb +5 -11
- data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +140 -0
- data/lib/rubocop/cop/performance/compare_with_block.rb +12 -23
- data/lib/rubocop/cop/performance/count.rb +14 -17
- data/lib/rubocop/cop/performance/delete_prefix.rb +87 -0
- data/lib/rubocop/cop/performance/delete_suffix.rb +87 -0
- data/lib/rubocop/cop/performance/detect.rb +30 -27
- data/lib/rubocop/cop/performance/double_start_end_with.rb +18 -26
- data/lib/rubocop/cop/performance/end_with.rb +38 -25
- data/lib/rubocop/cop/performance/fixed_size.rb +2 -2
- data/lib/rubocop/cop/performance/flat_map.rb +21 -23
- data/lib/rubocop/cop/performance/inefficient_hash_search.rb +14 -15
- data/lib/rubocop/cop/performance/io_readlines.rb +116 -0
- data/lib/rubocop/cop/performance/open_struct.rb +3 -3
- data/lib/rubocop/cop/performance/range_include.rb +15 -12
- data/lib/rubocop/cop/performance/redundant_block_call.rb +14 -9
- data/lib/rubocop/cop/performance/redundant_match.rb +13 -8
- data/lib/rubocop/cop/performance/redundant_merge.rb +36 -23
- data/lib/rubocop/cop/performance/redundant_sort_block.rb +43 -0
- data/lib/rubocop/cop/performance/redundant_string_chars.rb +133 -0
- data/lib/rubocop/cop/performance/regexp_match.rb +32 -32
- data/lib/rubocop/cop/performance/reverse_each.rb +10 -5
- data/lib/rubocop/cop/performance/reverse_first.rb +72 -0
- data/lib/rubocop/cop/performance/size.rb +41 -43
- data/lib/rubocop/cop/performance/sort_reverse.rb +45 -0
- data/lib/rubocop/cop/performance/squeeze.rb +66 -0
- data/lib/rubocop/cop/performance/start_with.rb +38 -28
- data/lib/rubocop/cop/performance/string_include.rb +55 -0
- data/lib/rubocop/cop/performance/string_replacement.rb +25 -36
- data/lib/rubocop/cop/performance/sum.rb +129 -0
- data/lib/rubocop/cop/performance/times_map.rb +12 -19
- data/lib/rubocop/cop/performance/unfreeze_string.rb +4 -8
- data/lib/rubocop/cop/performance/uri_default_parser.rb +7 -13
- data/lib/rubocop/cop/performance_cops.rb +17 -0
- data/lib/rubocop/performance/inject.rb +1 -1
- data/lib/rubocop/performance/version.rb +1 -1
- metadata +27 -11
@@ -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,133 @@
|
|
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
|
+
REPLACEABLE_METHODS = %i[[] slice first last take drop length size empty?].freeze
|
48
|
+
|
49
|
+
def_node_matcher :redundant_chars_call?, <<~PATTERN
|
50
|
+
(send $(send _ :chars) $#replaceable_method? $...)
|
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 replaceable_method?(method_name)
|
70
|
+
REPLACEABLE_METHODS.include?(method_name)
|
71
|
+
end
|
72
|
+
|
73
|
+
def offense_range(receiver, node)
|
74
|
+
range_between(receiver.loc.selector.begin_pos, node.loc.expression.end_pos)
|
75
|
+
end
|
76
|
+
|
77
|
+
def correction_range(receiver, node)
|
78
|
+
range_between(receiver.loc.dot.begin_pos, node.loc.expression.end_pos)
|
79
|
+
end
|
80
|
+
|
81
|
+
def build_message(method, args)
|
82
|
+
good_method = build_good_method(method, args)
|
83
|
+
bad_method = build_bad_method(method, args)
|
84
|
+
format(MSG, good_method: good_method, bad_method: bad_method)
|
85
|
+
end
|
86
|
+
|
87
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
88
|
+
def build_good_method(method, args)
|
89
|
+
case method
|
90
|
+
when :[], :slice
|
91
|
+
"[#{build_call_args(args)}].chars"
|
92
|
+
when :first
|
93
|
+
if args.any?
|
94
|
+
"[0...#{args.first.source}].chars"
|
95
|
+
else
|
96
|
+
'[0]'
|
97
|
+
end
|
98
|
+
when :last
|
99
|
+
if args.any?
|
100
|
+
"[-#{args.first.source}..-1].chars"
|
101
|
+
else
|
102
|
+
'[-1]'
|
103
|
+
end
|
104
|
+
when :take
|
105
|
+
"[0...#{args.first.source}].chars"
|
106
|
+
when :drop
|
107
|
+
"[#{args.first.source}..-1].chars"
|
108
|
+
else
|
109
|
+
".#{method}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
113
|
+
|
114
|
+
def build_bad_method(method, args)
|
115
|
+
case method
|
116
|
+
when :[]
|
117
|
+
"chars[#{build_call_args(args)}]"
|
118
|
+
else
|
119
|
+
if args.any?
|
120
|
+
"chars.#{method}(#{build_call_args(args)})"
|
121
|
+
else
|
122
|
+
"chars.#{method}"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def build_call_args(call_args_node)
|
128
|
+
call_args_node.map(&:source).join(', ')
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -72,10 +72,8 @@ module RuboCop
|
|
72
72
|
# do_something($~)
|
73
73
|
# end
|
74
74
|
# end
|
75
|
-
class RegexpMatch <
|
76
|
-
extend
|
77
|
-
|
78
|
-
minimum_target_ruby_version 2.4
|
75
|
+
class RegexpMatch < Base
|
76
|
+
extend AutoCorrector
|
79
77
|
|
80
78
|
# Constants are included in this list because it is unlikely that
|
81
79
|
# someone will store `nil` as a constant and then use it for comparison
|
@@ -83,22 +81,22 @@ module RuboCop
|
|
83
81
|
MSG = 'Use `match?` instead of `%<current>s` when `MatchData` ' \
|
84
82
|
'is not used.'
|
85
83
|
|
86
|
-
def_node_matcher :match_method?,
|
84
|
+
def_node_matcher :match_method?, <<~PATTERN
|
87
85
|
{
|
88
86
|
(send _recv :match {regexp str sym})
|
89
87
|
(send {regexp str sym} :match _)
|
90
88
|
}
|
91
89
|
PATTERN
|
92
90
|
|
93
|
-
def_node_matcher :match_with_int_arg_method?,
|
91
|
+
def_node_matcher :match_with_int_arg_method?, <<~PATTERN
|
94
92
|
(send _recv :match _ (int ...))
|
95
93
|
PATTERN
|
96
94
|
|
97
|
-
def_node_matcher :match_operator?,
|
95
|
+
def_node_matcher :match_operator?, <<~PATTERN
|
98
96
|
(send !nil? {:=~ :!~} !nil?)
|
99
97
|
PATTERN
|
100
98
|
|
101
|
-
def_node_matcher :match_threequals?,
|
99
|
+
def_node_matcher :match_threequals?, <<~PATTERN
|
102
100
|
(send (regexp (str _) {(regopt) (regopt _)}) :=== !nil?)
|
103
101
|
PATTERN
|
104
102
|
|
@@ -109,7 +107,7 @@ module RuboCop
|
|
109
107
|
regexp.to_regexp.named_captures.empty?
|
110
108
|
end
|
111
109
|
|
112
|
-
MATCH_NODE_PATTERN =
|
110
|
+
MATCH_NODE_PATTERN = <<~PATTERN
|
113
111
|
{
|
114
112
|
#match_method?
|
115
113
|
#match_with_int_arg_method?
|
@@ -122,7 +120,7 @@ module RuboCop
|
|
122
120
|
def_node_matcher :match_node?, MATCH_NODE_PATTERN
|
123
121
|
def_node_search :search_match_nodes, MATCH_NODE_PATTERN
|
124
122
|
|
125
|
-
def_node_search :last_matches,
|
123
|
+
def_node_search :last_matches, <<~PATTERN
|
126
124
|
{
|
127
125
|
(send (const nil? :Regexp) :last_match)
|
128
126
|
(send (const nil? :Regexp) :last_match _)
|
@@ -145,27 +143,28 @@ module RuboCop
|
|
145
143
|
end
|
146
144
|
end
|
147
145
|
|
148
|
-
def autocorrect(node)
|
149
|
-
lambda do |corrector|
|
150
|
-
if match_method?(node) || match_with_int_arg_method?(node)
|
151
|
-
corrector.replace(node.loc.selector, 'match?')
|
152
|
-
elsif match_operator?(node) || match_threequals?(node)
|
153
|
-
recv, oper, arg = *node
|
154
|
-
correct_operator(corrector, recv, arg, oper)
|
155
|
-
elsif match_with_lvasgn?(node)
|
156
|
-
recv, arg = *node
|
157
|
-
correct_operator(corrector, recv, arg)
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
146
|
private
|
163
147
|
|
164
148
|
def check_condition(cond)
|
165
149
|
match_node?(cond) do
|
166
150
|
return if last_match_used?(cond)
|
167
151
|
|
168
|
-
|
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)
|
169
168
|
end
|
170
169
|
end
|
171
170
|
|
@@ -235,10 +234,7 @@ module RuboCop
|
|
235
234
|
|
236
235
|
def scope_root(node)
|
237
236
|
node.each_ancestor.find do |ancestor|
|
238
|
-
ancestor.def_type? ||
|
239
|
-
ancestor.defs_type? ||
|
240
|
-
ancestor.class_type? ||
|
241
|
-
ancestor.module_type?
|
237
|
+
ancestor.def_type? || ancestor.defs_type? || ancestor.class_type? || ancestor.module_type?
|
242
238
|
end
|
243
239
|
end
|
244
240
|
|
@@ -256,6 +252,13 @@ module RuboCop
|
|
256
252
|
def correct_operator(corrector, recv, arg, oper = nil)
|
257
253
|
op_range = correction_range(recv, arg)
|
258
254
|
|
255
|
+
replace_with_match_predicate_method(corrector, recv, arg, op_range)
|
256
|
+
|
257
|
+
corrector.insert_after(arg.loc.expression, ')') unless op_range.source.end_with?('(')
|
258
|
+
corrector.insert_before(recv.loc.expression, '!') if oper == :!~
|
259
|
+
end
|
260
|
+
|
261
|
+
def replace_with_match_predicate_method(corrector, recv, arg, op_range)
|
259
262
|
if TYPES_IMPLEMENTING_MATCH.include?(recv.type)
|
260
263
|
corrector.replace(op_range, '.match?(')
|
261
264
|
elsif TYPES_IMPLEMENTING_MATCH.include?(arg.type)
|
@@ -264,9 +267,6 @@ module RuboCop
|
|
264
267
|
else
|
265
268
|
corrector.replace(op_range, '&.match?(')
|
266
269
|
end
|
267
|
-
|
268
|
-
corrector.insert_after(arg.loc.expression, ')')
|
269
|
-
corrector.insert_before(recv.loc.expression, '!') if oper == :!~
|
270
270
|
end
|
271
271
|
|
272
272
|
def swap_receiver_and_arg(corrector, recv, arg)
|
@@ -12,13 +12,14 @@ 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 = '_'
|
20
21
|
|
21
|
-
def_node_matcher :reverse_each?,
|
22
|
+
def_node_matcher :reverse_each?, <<~MATCHER
|
22
23
|
(send $(send _ :reverse) :each)
|
23
24
|
MATCHER
|
24
25
|
|
@@ -29,12 +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
|
-
|
39
|
+
private
|
40
|
+
|
41
|
+
def replacement_range(node)
|
42
|
+
range_between(node.loc.dot.begin_pos, node.loc.selector.begin_pos)
|
38
43
|
end
|
39
44
|
end
|
40
45
|
end
|
@@ -0,0 +1,72 @@
|
|
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
|
+
|
25
|
+
def_node_matcher :reverse_first_candidate?, <<~PATTERN
|
26
|
+
(send $(send _ :reverse) :first (int _)?)
|
27
|
+
PATTERN
|
28
|
+
|
29
|
+
def on_send(node)
|
30
|
+
reverse_first_candidate?(node) do |receiver|
|
31
|
+
range = correction_range(receiver, node)
|
32
|
+
message = build_message(node)
|
33
|
+
|
34
|
+
add_offense(range, message: message) do |corrector|
|
35
|
+
replacement = build_good_method(node)
|
36
|
+
|
37
|
+
corrector.replace(range, replacement)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def correction_range(receiver, node)
|
45
|
+
range_between(receiver.loc.selector.begin_pos, node.loc.expression.end_pos)
|
46
|
+
end
|
47
|
+
|
48
|
+
def build_message(node)
|
49
|
+
good_method = build_good_method(node)
|
50
|
+
bad_method = build_bad_method(node)
|
51
|
+
format(MSG, good_method: good_method, bad_method: bad_method)
|
52
|
+
end
|
53
|
+
|
54
|
+
def build_good_method(node)
|
55
|
+
if node.arguments?
|
56
|
+
"last(#{node.arguments.first.source}).reverse"
|
57
|
+
else
|
58
|
+
'last'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def build_bad_method(node)
|
63
|
+
if node.arguments?
|
64
|
+
"reverse.first(#{node.arguments.first.source})"
|
65
|
+
else
|
66
|
+
'reverse.first'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -9,67 +9,65 @@ module RuboCop
|
|
9
9
|
# @example
|
10
10
|
# # bad
|
11
11
|
# [1, 2, 3].count
|
12
|
+
# (1..3).to_a.count
|
13
|
+
# Array[*1..3].count
|
14
|
+
# Array(1..3).count
|
12
15
|
#
|
13
16
|
# # bad
|
14
17
|
# {a: 1, b: 2, c: 3}.count
|
18
|
+
# [[:foo, :bar], [1, 2]].to_h.count
|
19
|
+
# Hash[*('a'..'z')].count
|
20
|
+
# Hash(key: :value).count
|
15
21
|
#
|
16
22
|
# # good
|
17
23
|
# [1, 2, 3].size
|
24
|
+
# (1..3).to_a.size
|
25
|
+
# Array[*1..3].size
|
26
|
+
# Array(1..3).size
|
18
27
|
#
|
19
28
|
# # good
|
20
29
|
# {a: 1, b: 2, c: 3}.size
|
30
|
+
# [[:foo, :bar], [1, 2]].to_h.size
|
31
|
+
# Hash[*('a'..'z')].size
|
32
|
+
# Hash(key: :value).size
|
21
33
|
#
|
22
34
|
# # good
|
23
35
|
# [1, 2, 3].count { |e| e > 2 }
|
24
36
|
# TODO: Add advanced detection of variables that could
|
25
37
|
# have been assigned to an array or a hash.
|
26
|
-
class Size <
|
27
|
-
|
28
|
-
|
29
|
-
def on_send(node)
|
30
|
-
return unless eligible_node?(node)
|
31
|
-
|
32
|
-
add_offense(node, location: :selector)
|
33
|
-
end
|
34
|
-
|
35
|
-
def autocorrect(node)
|
36
|
-
->(corrector) { corrector.replace(node.loc.selector, 'size') }
|
37
|
-
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def eligible_node?(node)
|
42
|
-
return false unless node.method?(:count) && !node.arguments?
|
43
|
-
|
44
|
-
eligible_receiver?(node.receiver) && !allowed_parent?(node.parent)
|
45
|
-
end
|
38
|
+
class Size < Base
|
39
|
+
extend AutoCorrector
|
46
40
|
|
47
|
-
|
48
|
-
return false unless node
|
49
|
-
|
50
|
-
array?(node) || hash?(node)
|
51
|
-
end
|
52
|
-
|
53
|
-
def allowed_parent?(node)
|
54
|
-
node&.block_type?
|
55
|
-
end
|
56
|
-
|
57
|
-
def array?(node)
|
58
|
-
return true if node.array_type?
|
59
|
-
return false unless node.send_type?
|
60
|
-
|
61
|
-
_, constant = *node.receiver
|
62
|
-
|
63
|
-
constant == :Array || node.method?(:to_a)
|
64
|
-
end
|
41
|
+
MSG = 'Use `size` instead of `count`.'
|
65
42
|
|
66
|
-
|
67
|
-
|
68
|
-
|
43
|
+
def_node_matcher :array?, <<~PATTERN
|
44
|
+
{
|
45
|
+
[!nil? array_type?]
|
46
|
+
(send _ :to_a)
|
47
|
+
(send (const nil? :Array) :[] _)
|
48
|
+
(send nil? :Array _)
|
49
|
+
}
|
50
|
+
PATTERN
|
51
|
+
|
52
|
+
def_node_matcher :hash?, <<~PATTERN
|
53
|
+
{
|
54
|
+
[!nil? hash_type?]
|
55
|
+
(send _ :to_h)
|
56
|
+
(send (const nil? :Hash) :[] _)
|
57
|
+
(send nil? :Hash _)
|
58
|
+
}
|
59
|
+
PATTERN
|
60
|
+
|
61
|
+
def_node_matcher :count?, <<~PATTERN
|
62
|
+
(send {#array? #hash?} :count)
|
63
|
+
PATTERN
|
69
64
|
|
70
|
-
|
65
|
+
def on_send(node)
|
66
|
+
return if node.parent&.block_type? || !count?(node)
|
71
67
|
|
72
|
-
|
68
|
+
add_offense(node.loc.selector) do |corrector|
|
69
|
+
corrector.replace(node.loc.selector, 'size')
|
70
|
+
end
|
73
71
|
end
|
74
72
|
end
|
75
73
|
end
|