rubocop-performance 1.18.0 → 1.20.2
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 +2 -0
- data/config/default.yml +22 -16
- data/lib/rubocop/cop/mixin/sort_block.rb +2 -2
- data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +2 -1
- data/lib/rubocop/cop/performance/block_given_with_explicit_block.rb +4 -0
- data/lib/rubocop/cop/performance/count.rb +5 -5
- data/lib/rubocop/cop/performance/delete_prefix.rb +5 -2
- data/lib/rubocop/cop/performance/delete_suffix.rb +5 -2
- data/lib/rubocop/cop/performance/detect.rb +3 -2
- data/lib/rubocop/cop/performance/end_with.rb +4 -2
- data/lib/rubocop/cop/performance/fixed_size.rb +4 -3
- data/lib/rubocop/cop/performance/flat_map.rb +4 -3
- data/lib/rubocop/cop/performance/inefficient_hash_search.rb +16 -10
- data/lib/rubocop/cop/performance/map_compact.rb +5 -4
- data/lib/rubocop/cop/performance/map_method_chain.rb +89 -0
- data/lib/rubocop/cop/performance/range_include.rb +8 -6
- data/lib/rubocop/cop/performance/redundant_equality_comparison_block.rb +1 -1
- data/lib/rubocop/cop/performance/redundant_match.rb +30 -1
- data/lib/rubocop/cop/performance/redundant_merge.rb +5 -3
- data/lib/rubocop/cop/performance/redundant_split_regexp_argument.rb +2 -1
- data/lib/rubocop/cop/performance/reverse_each.rb +2 -1
- data/lib/rubocop/cop/performance/reverse_first.rb +6 -13
- data/lib/rubocop/cop/performance/select_map.rb +3 -6
- data/lib/rubocop/cop/performance/size.rb +4 -3
- data/lib/rubocop/cop/performance/sort_reverse.rb +5 -4
- data/lib/rubocop/cop/performance/squeeze.rb +5 -2
- data/lib/rubocop/cop/performance/start_with.rb +4 -2
- data/lib/rubocop/cop/performance/string_identifier_argument.rb +58 -10
- data/lib/rubocop/cop/performance/string_include.rb +9 -4
- data/lib/rubocop/cop/performance/string_replacement.rb +2 -1
- data/lib/rubocop/cop/performance/sum.rb +11 -9
- data/lib/rubocop/cop/performance/times_map.rb +14 -2
- data/lib/rubocop/cop/performance/unfreeze_string.rb +6 -3
- data/lib/rubocop/cop/performance/uri_default_parser.rb +1 -1
- data/lib/rubocop/cop/performance_cops.rb +1 -0
- data/lib/rubocop/performance/version.rb +1 -1
- data/lib/rubocop-performance.rb +8 -0
- metadata +15 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1dad3fdab16d4dbcb3047e3319ab415b6e9ae3e2cbf14a73a1874831bf473701
|
|
4
|
+
data.tar.gz: ca98aec2aac71f768e63c5b5ed3c73c2a5ef8242433fc8fe078e9930f47a4d38
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 16536c625e9f66d73368d70153a139f72da96dee225807c1154c07e7a2df999be9f06278b1c6db95836baa6832524da39715f8240151236c5b29ec47ce99e91b
|
|
7
|
+
data.tar.gz: bfd72acdc38b7337c4b783fc8e5e14516aa8926980cfbf2051487350eb939648ac9af302e1f5c0abc889f3564efb98ff5a7551d107d8d28b2912c4c9a0128b79
|
data/README.md
CHANGED
data/config/default.yml
CHANGED
|
@@ -6,7 +6,7 @@ Performance:
|
|
|
6
6
|
|
|
7
7
|
Performance/AncestorsInclude:
|
|
8
8
|
Description: 'Use `A <= B` instead of `A.ancestors.include?(B)`.'
|
|
9
|
-
Reference: 'https://github.com/
|
|
9
|
+
Reference: 'https://github.com/fastruby/fast-ruby#ancestorsinclude-vs--code'
|
|
10
10
|
Enabled: 'pending'
|
|
11
11
|
Safe: false
|
|
12
12
|
VersionAdded: '1.7'
|
|
@@ -54,7 +54,7 @@ Performance/CaseWhenSplat:
|
|
|
54
54
|
Performance/Casecmp:
|
|
55
55
|
Description: >-
|
|
56
56
|
Use `casecmp` rather than `downcase ==`, `upcase ==`, `== downcase`, or `== upcase`..
|
|
57
|
-
Reference: 'https://github.com/
|
|
57
|
+
Reference: 'https://github.com/fastruby/fast-ruby#stringcasecmp-vs--stringcasecmp-vs-stringdowncase---code'
|
|
58
58
|
Enabled: true
|
|
59
59
|
Safe: false
|
|
60
60
|
VersionAdded: '0.36'
|
|
@@ -122,7 +122,7 @@ Performance/Detect:
|
|
|
122
122
|
Description: >-
|
|
123
123
|
Use `detect` instead of `select.first`, `find_all.first`, `filter.first`,
|
|
124
124
|
`select.last`, `find_all.last`, and `filter.last`.
|
|
125
|
-
Reference: 'https://github.com/
|
|
125
|
+
Reference: 'https://github.com/fastruby/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code'
|
|
126
126
|
# This cop has known compatibility issues with `ActiveRecord` and other
|
|
127
127
|
# frameworks. `ActiveRecord` does not implement a `detect` method and `find`
|
|
128
128
|
# has its own meaning. Correcting `ActiveRecord` methods with this cop
|
|
@@ -145,7 +145,7 @@ Performance/DoubleStartEndWith:
|
|
|
145
145
|
|
|
146
146
|
Performance/EndWith:
|
|
147
147
|
Description: 'Use `end_with?` instead of a regex match anchored to the end of a string.'
|
|
148
|
-
Reference: 'https://github.com/
|
|
148
|
+
Reference: 'https://github.com/fastruby/fast-ruby#stringmatch-vs-stringmatch-vs-stringstart_withstringend_with-code-start-code-end'
|
|
149
149
|
# This will change to a new method call which isn't guaranteed to be on the
|
|
150
150
|
# object. Switching these methods has to be done with knowledge of the types
|
|
151
151
|
# of the variables which rubocop doesn't have.
|
|
@@ -165,7 +165,7 @@ Performance/FlatMap:
|
|
|
165
165
|
Use `Enumerable#flat_map`
|
|
166
166
|
instead of `Enumerable#map...Array#flatten(1)`
|
|
167
167
|
or `Enumerable#collect..Array#flatten(1)`.
|
|
168
|
-
Reference: 'https://github.com/
|
|
168
|
+
Reference: 'https://github.com/fastruby/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code'
|
|
169
169
|
Enabled: true
|
|
170
170
|
VersionAdded: '0.30'
|
|
171
171
|
EnabledForFlattenWithoutParams: false
|
|
@@ -176,7 +176,7 @@ Performance/FlatMap:
|
|
|
176
176
|
|
|
177
177
|
Performance/InefficientHashSearch:
|
|
178
178
|
Description: 'Use `key?` or `value?` instead of `keys.include?` or `values.include?`.'
|
|
179
|
-
Reference: 'https://github.com/
|
|
179
|
+
Reference: 'https://github.com/fastruby/fast-ruby#hashkey-instead-of-hashkeysinclude-code'
|
|
180
180
|
Enabled: true
|
|
181
181
|
VersionAdded: '0.56'
|
|
182
182
|
Safe: false
|
|
@@ -193,9 +193,15 @@ Performance/MapCompact:
|
|
|
193
193
|
SafeAutoCorrect: false
|
|
194
194
|
VersionAdded: '1.11'
|
|
195
195
|
|
|
196
|
+
Performance/MapMethodChain:
|
|
197
|
+
Description: 'Checks if the `map` method is used in a chain.'
|
|
198
|
+
Enabled: pending
|
|
199
|
+
Safe: false
|
|
200
|
+
VersionAdded: '1.19'
|
|
201
|
+
|
|
196
202
|
Performance/MethodObjectAsBlock:
|
|
197
203
|
Description: 'Use block explicitly instead of block-passing a method object.'
|
|
198
|
-
Reference: 'https://github.com/
|
|
204
|
+
Reference: 'https://github.com/fastruby/fast-ruby#normal-way-to-apply-method-vs-method-code'
|
|
199
205
|
Enabled: pending
|
|
200
206
|
VersionAdded: '1.9'
|
|
201
207
|
|
|
@@ -207,7 +213,7 @@ Performance/OpenStruct:
|
|
|
207
213
|
|
|
208
214
|
Performance/RangeInclude:
|
|
209
215
|
Description: 'Use `Range#cover?` instead of `Range#include?` (or `Range#member?`).'
|
|
210
|
-
Reference: 'https://github.com/
|
|
216
|
+
Reference: 'https://github.com/fastruby/fast-ruby#cover-vs-include-code'
|
|
211
217
|
Enabled: true
|
|
212
218
|
VersionAdded: '0.36'
|
|
213
219
|
VersionChanged: '1.7'
|
|
@@ -215,7 +221,7 @@ Performance/RangeInclude:
|
|
|
215
221
|
|
|
216
222
|
Performance/RedundantBlockCall:
|
|
217
223
|
Description: 'Use `yield` instead of `block.call`.'
|
|
218
|
-
Reference: 'https://github.com/
|
|
224
|
+
Reference: 'https://github.com/fastruby/fast-ruby#proccall-and-block-arguments-vs-yieldcode'
|
|
219
225
|
Enabled: true
|
|
220
226
|
VersionAdded: '0.36'
|
|
221
227
|
|
|
@@ -238,7 +244,7 @@ Performance/RedundantMatch:
|
|
|
238
244
|
|
|
239
245
|
Performance/RedundantMerge:
|
|
240
246
|
Description: 'Use Hash#[]=, rather than Hash#merge! with a single key-value pair.'
|
|
241
|
-
Reference: 'https://github.com/
|
|
247
|
+
Reference: 'https://github.com/fastruby/fast-ruby#hashmerge-vs-hash-code'
|
|
242
248
|
Enabled: true
|
|
243
249
|
Safe: false
|
|
244
250
|
VersionAdded: '0.36'
|
|
@@ -265,13 +271,13 @@ Performance/RegexpMatch:
|
|
|
265
271
|
Description: >-
|
|
266
272
|
Use `match?` instead of `Regexp#match`, `String#match`, `Symbol#match`,
|
|
267
273
|
`Regexp#===`, or `=~` when `MatchData` is not used.
|
|
268
|
-
Reference: 'https://github.com/
|
|
274
|
+
Reference: 'https://github.com/fastruby/fast-ruby#regexp-vs-regexpmatch-vs-regexpmatch-vs-stringmatch-vs-string-vs-stringmatch-code-'
|
|
269
275
|
Enabled: true
|
|
270
276
|
VersionAdded: '0.47'
|
|
271
277
|
|
|
272
278
|
Performance/ReverseEach:
|
|
273
279
|
Description: 'Use `reverse_each` instead of `reverse.each`.'
|
|
274
|
-
Reference: 'https://github.com/
|
|
280
|
+
Reference: 'https://github.com/fastruby/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code'
|
|
275
281
|
Enabled: true
|
|
276
282
|
VersionAdded: '0.30'
|
|
277
283
|
|
|
@@ -289,7 +295,7 @@ Performance/Size:
|
|
|
289
295
|
Description: >-
|
|
290
296
|
Use `size` instead of `count` for counting
|
|
291
297
|
the number of elements in `Array` and `Hash`.
|
|
292
|
-
Reference: 'https://github.com/
|
|
298
|
+
Reference: 'https://github.com/fastruby/fast-ruby#arraylength-vs-arraysize-vs-arraycount-code'
|
|
293
299
|
Enabled: true
|
|
294
300
|
VersionAdded: '0.30'
|
|
295
301
|
|
|
@@ -300,13 +306,13 @@ Performance/SortReverse:
|
|
|
300
306
|
|
|
301
307
|
Performance/Squeeze:
|
|
302
308
|
Description: "Use `squeeze('a')` instead of `gsub(/a+/, 'a')`."
|
|
303
|
-
Reference: 'https://github.com/
|
|
309
|
+
Reference: 'https://github.com/fastruby/fast-ruby#remove-extra-spaces-or-other-contiguous-characters-code'
|
|
304
310
|
Enabled: 'pending'
|
|
305
311
|
VersionAdded: '1.7'
|
|
306
312
|
|
|
307
313
|
Performance/StartWith:
|
|
308
314
|
Description: 'Use `start_with?` instead of a regex match anchored to the beginning of a string.'
|
|
309
|
-
Reference: 'https://github.com/
|
|
315
|
+
Reference: 'https://github.com/fastruby/fast-ruby#stringmatch-vs-stringmatch-vs-stringstart_withstringend_with-code-start-code-end'
|
|
310
316
|
# This will change to a new method call which isn't guaranteed to be on the
|
|
311
317
|
# object. Switching these methods has to be done with knowledge of the types
|
|
312
318
|
# of the variables which rubocop doesn't have.
|
|
@@ -333,7 +339,7 @@ Performance/StringReplacement:
|
|
|
333
339
|
Use `tr` instead of `gsub` when you are replacing the same
|
|
334
340
|
number of characters. Use `delete` instead of `gsub` when
|
|
335
341
|
you are deleting characters.
|
|
336
|
-
Reference: 'https://github.com/
|
|
342
|
+
Reference: 'https://github.com/fastruby/fast-ruby#stringgsub-vs-stringtr-code'
|
|
337
343
|
Enabled: true
|
|
338
344
|
VersionAdded: '0.33'
|
|
339
345
|
|
|
@@ -9,14 +9,14 @@ module RuboCop
|
|
|
9
9
|
|
|
10
10
|
def_node_matcher :sort_with_block?, <<~PATTERN
|
|
11
11
|
(block
|
|
12
|
-
$(
|
|
12
|
+
$(call _ :sort)
|
|
13
13
|
(args (arg $_a) (arg $_b))
|
|
14
14
|
$send)
|
|
15
15
|
PATTERN
|
|
16
16
|
|
|
17
17
|
def_node_matcher :sort_with_numblock?, <<~PATTERN
|
|
18
18
|
(numblock
|
|
19
|
-
$(
|
|
19
|
+
$(call _ :sort)
|
|
20
20
|
$_arg_count
|
|
21
21
|
$send)
|
|
22
22
|
PATTERN
|
|
@@ -39,7 +39,7 @@ module RuboCop
|
|
|
39
39
|
RESTRICT_ON_SEND = SLICE_METHODS
|
|
40
40
|
|
|
41
41
|
def_node_matcher :endless_range_slice?, <<~PATTERN
|
|
42
|
-
(
|
|
42
|
+
(call $!{str dstr xstr} $%SLICE_METHODS $#endless_range?)
|
|
43
43
|
PATTERN
|
|
44
44
|
|
|
45
45
|
def_node_matcher :endless_range?, <<~PATTERN
|
|
@@ -59,6 +59,7 @@ module RuboCop
|
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
|
+
alias on_csend on_send
|
|
62
63
|
|
|
63
64
|
private
|
|
64
65
|
|
|
@@ -9,8 +9,7 @@ module RuboCop
|
|
|
9
9
|
#
|
|
10
10
|
# @safety
|
|
11
11
|
# This cop is unsafe because it has known compatibility issues with `ActiveRecord` and other
|
|
12
|
-
# frameworks.
|
|
13
|
-
# `ActiveRecord` will ignore the block that is passed to `count`.
|
|
12
|
+
# frameworks. Before Rails 5.1, `ActiveRecord` will ignore the block that is passed to `count`.
|
|
14
13
|
# Other methods, such as `select`, will convert the association to an
|
|
15
14
|
# array and then run the block on the array. A simple work around to
|
|
16
15
|
# make `count` work with a block is to call `to_a.count {...}`.
|
|
@@ -55,8 +54,8 @@ module RuboCop
|
|
|
55
54
|
|
|
56
55
|
def_node_matcher :count_candidate?, <<~PATTERN
|
|
57
56
|
{
|
|
58
|
-
(
|
|
59
|
-
(
|
|
57
|
+
(call (block $(call _ ${:select :filter :find_all :reject}) ...) ${:count :length :size})
|
|
58
|
+
(call $(call _ ${:select :filter :find_all :reject} (:block_pass _)) ${:count :length :size})
|
|
60
59
|
}
|
|
61
60
|
PATTERN
|
|
62
61
|
|
|
@@ -73,6 +72,7 @@ module RuboCop
|
|
|
73
72
|
end
|
|
74
73
|
end
|
|
75
74
|
end
|
|
75
|
+
alias on_csend on_send
|
|
76
76
|
|
|
77
77
|
private
|
|
78
78
|
|
|
@@ -101,7 +101,7 @@ module RuboCop
|
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
def negate_reject(corrector, node)
|
|
104
|
-
if node.receiver.
|
|
104
|
+
if node.receiver.call_type?
|
|
105
105
|
negate_block_pass_reject(corrector, node)
|
|
106
106
|
else
|
|
107
107
|
negate_block_reject(corrector, node)
|
|
@@ -64,9 +64,10 @@ module RuboCop
|
|
|
64
64
|
}.freeze
|
|
65
65
|
|
|
66
66
|
def_node_matcher :delete_prefix_candidate?, <<~PATTERN
|
|
67
|
-
(
|
|
67
|
+
(call $!nil? ${:gsub :gsub! :sub :sub!} (regexp (str $#literal_at_start?) (regopt)) (str $_))
|
|
68
68
|
PATTERN
|
|
69
69
|
|
|
70
|
+
# rubocop:disable Metrics/AbcSize
|
|
70
71
|
def on_send(node)
|
|
71
72
|
return unless (receiver, bad_method, regexp_str, replace_string = delete_prefix_candidate?(node))
|
|
72
73
|
return unless replace_string.empty?
|
|
@@ -80,11 +81,13 @@ module RuboCop
|
|
|
80
81
|
regexp_str = interpret_string_escapes(regexp_str)
|
|
81
82
|
string_literal = to_string_literal(regexp_str)
|
|
82
83
|
|
|
83
|
-
new_code = "#{receiver.source}
|
|
84
|
+
new_code = "#{receiver.source}#{node.loc.dot.source}#{good_method}(#{string_literal})"
|
|
84
85
|
|
|
85
86
|
corrector.replace(node, new_code)
|
|
86
87
|
end
|
|
87
88
|
end
|
|
89
|
+
# rubocop:enable Metrics/AbcSize
|
|
90
|
+
alias on_csend on_send
|
|
88
91
|
end
|
|
89
92
|
end
|
|
90
93
|
end
|
|
@@ -64,9 +64,10 @@ module RuboCop
|
|
|
64
64
|
}.freeze
|
|
65
65
|
|
|
66
66
|
def_node_matcher :delete_suffix_candidate?, <<~PATTERN
|
|
67
|
-
(
|
|
67
|
+
(call $!nil? ${:gsub :gsub! :sub :sub!} (regexp (str $#literal_at_end?) (regopt)) (str $_))
|
|
68
68
|
PATTERN
|
|
69
69
|
|
|
70
|
+
# rubocop:disable Metrics/AbcSize
|
|
70
71
|
def on_send(node)
|
|
71
72
|
return unless (receiver, bad_method, regexp_str, replace_string = delete_suffix_candidate?(node))
|
|
72
73
|
return unless replace_string.empty?
|
|
@@ -80,11 +81,13 @@ module RuboCop
|
|
|
80
81
|
regexp_str = interpret_string_escapes(regexp_str)
|
|
81
82
|
string_literal = to_string_literal(regexp_str)
|
|
82
83
|
|
|
83
|
-
new_code = "#{receiver.source}
|
|
84
|
+
new_code = "#{receiver.source}#{node.loc.dot.source}#{good_method}(#{string_literal})"
|
|
84
85
|
|
|
85
86
|
corrector.replace(node, new_code)
|
|
86
87
|
end
|
|
87
88
|
end
|
|
89
|
+
# rubocop:enable Metrics/AbcSize
|
|
90
|
+
alias on_csend on_send
|
|
88
91
|
end
|
|
89
92
|
end
|
|
90
93
|
end
|
|
@@ -40,9 +40,9 @@ module RuboCop
|
|
|
40
40
|
|
|
41
41
|
def_node_matcher :detect_candidate?, <<~PATTERN
|
|
42
42
|
{
|
|
43
|
-
(send $(block (
|
|
43
|
+
(send $(block (call _ %CANDIDATE_METHODS) ...) ${:first :last} $...)
|
|
44
44
|
(send $(block (send _ %CANDIDATE_METHODS) ...) $:[] (int ${0 -1}))
|
|
45
|
-
(send $(
|
|
45
|
+
(send $(call _ %CANDIDATE_METHODS ...) ${:first :last} $...)
|
|
46
46
|
(send $(send _ %CANDIDATE_METHODS ...) $:[] (int ${0 -1}))
|
|
47
47
|
}
|
|
48
48
|
PATTERN
|
|
@@ -63,6 +63,7 @@ module RuboCop
|
|
|
63
63
|
register_offense(node, receiver, second_method, index)
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
|
+
alias on_csend on_send
|
|
66
67
|
|
|
67
68
|
private
|
|
68
69
|
|
|
@@ -54,7 +54,7 @@ module RuboCop
|
|
|
54
54
|
RESTRICT_ON_SEND = %i[match =~ match?].freeze
|
|
55
55
|
|
|
56
56
|
def_node_matcher :redundant_regex?, <<~PATTERN
|
|
57
|
-
{(
|
|
57
|
+
{(call $!nil? {:match :=~ :match?} (regexp (str $#literal_at_end?) (regopt)))
|
|
58
58
|
(send (regexp (str $#literal_at_end?) (regopt)) {:match :match?} $_)
|
|
59
59
|
(match-with-lvasgn (regexp (str $#literal_at_end?) (regopt)) $_)}
|
|
60
60
|
PATTERN
|
|
@@ -66,12 +66,14 @@ module RuboCop
|
|
|
66
66
|
receiver, regex_str = regex_str, receiver if receiver.is_a?(String)
|
|
67
67
|
regex_str = drop_end_metacharacter(regex_str)
|
|
68
68
|
regex_str = interpret_string_escapes(regex_str)
|
|
69
|
+
dot = node.loc.dot ? node.loc.dot.source : '.'
|
|
69
70
|
|
|
70
|
-
new_source = "#{receiver.source}
|
|
71
|
+
new_source = "#{receiver.source}#{dot}end_with?(#{to_string_literal(regex_str)})"
|
|
71
72
|
|
|
72
73
|
corrector.replace(node, new_source)
|
|
73
74
|
end
|
|
74
75
|
end
|
|
76
|
+
alias on_csend on_send
|
|
75
77
|
alias on_match_with_lvasgn on_send
|
|
76
78
|
end
|
|
77
79
|
end
|
|
@@ -50,7 +50,7 @@ module RuboCop
|
|
|
50
50
|
RESTRICT_ON_SEND = %i[count length size].freeze
|
|
51
51
|
|
|
52
52
|
def_node_matcher :counter, <<~MATCHER
|
|
53
|
-
(
|
|
53
|
+
(call ${array hash str sym} {:count :length :size} $...)
|
|
54
54
|
MATCHER
|
|
55
55
|
|
|
56
56
|
def on_send(node)
|
|
@@ -62,6 +62,7 @@ module RuboCop
|
|
|
62
62
|
add_offense(node)
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
|
+
alias on_csend on_send
|
|
65
66
|
|
|
66
67
|
private
|
|
67
68
|
|
|
@@ -78,13 +79,13 @@ module RuboCop
|
|
|
78
79
|
end
|
|
79
80
|
|
|
80
81
|
def contains_splat?(node)
|
|
81
|
-
return unless node.array_type?
|
|
82
|
+
return false unless node.array_type?
|
|
82
83
|
|
|
83
84
|
node.each_child_node(:splat).any?
|
|
84
85
|
end
|
|
85
86
|
|
|
86
87
|
def contains_double_splat?(node)
|
|
87
|
-
return unless node.hash_type?
|
|
88
|
+
return false unless node.hash_type?
|
|
88
89
|
|
|
89
90
|
node.each_child_node(:kwsplat).any?
|
|
90
91
|
end
|
|
@@ -26,10 +26,10 @@ module RuboCop
|
|
|
26
26
|
'multiple levels.'
|
|
27
27
|
|
|
28
28
|
def_node_matcher :flat_map_candidate?, <<~PATTERN
|
|
29
|
-
(
|
|
29
|
+
(call
|
|
30
30
|
{
|
|
31
|
-
$(block (
|
|
32
|
-
$(
|
|
31
|
+
$(block (call _ ${:collect :map}) ...)
|
|
32
|
+
$(call _ ${:collect :map} (block_pass _))
|
|
33
33
|
}
|
|
34
34
|
${:flatten :flatten!}
|
|
35
35
|
$...
|
|
@@ -46,6 +46,7 @@ module RuboCop
|
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
end
|
|
49
|
+
alias on_csend on_send
|
|
49
50
|
|
|
50
51
|
private
|
|
51
52
|
|
|
@@ -45,7 +45,7 @@ module RuboCop
|
|
|
45
45
|
RESTRICT_ON_SEND = %i[include?].freeze
|
|
46
46
|
|
|
47
47
|
def_node_matcher :inefficient_include?, <<~PATTERN
|
|
48
|
-
(
|
|
48
|
+
(call (call $_ {:keys :values}) :include? _)
|
|
49
49
|
PATTERN
|
|
50
50
|
|
|
51
51
|
def on_send(node)
|
|
@@ -56,21 +56,23 @@ module RuboCop
|
|
|
56
56
|
add_offense(node, message: message) do |corrector|
|
|
57
57
|
# Replace `keys.include?` or `values.include?` with the appropriate
|
|
58
58
|
# `key?`/`value?` method.
|
|
59
|
-
corrector.replace(
|
|
60
|
-
node,
|
|
61
|
-
"#{autocorrect_hash_expression(node)}.#{autocorrect_method(node)}(#{autocorrect_argument(node)})"
|
|
62
|
-
)
|
|
59
|
+
corrector.replace(node, replacement(node))
|
|
63
60
|
end
|
|
64
61
|
end
|
|
65
62
|
end
|
|
63
|
+
alias on_csend on_send
|
|
66
64
|
|
|
67
65
|
private
|
|
68
66
|
|
|
69
67
|
def message(node)
|
|
70
|
-
"Use `##{
|
|
68
|
+
"Use `##{correct_method(node)}` instead of `##{current_method(node)}.include?`."
|
|
71
69
|
end
|
|
72
70
|
|
|
73
|
-
def
|
|
71
|
+
def replacement(node)
|
|
72
|
+
"#{correct_hash_expression(node)}#{correct_dot(node)}#{correct_method(node)}(#{correct_argument(node)})"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def correct_method(node)
|
|
74
76
|
case current_method(node)
|
|
75
77
|
when :keys then use_long_method ? 'has_key?' : 'key?'
|
|
76
78
|
when :values then use_long_method ? 'has_value?' : 'value?'
|
|
@@ -86,13 +88,17 @@ module RuboCop
|
|
|
86
88
|
preferred_config && preferred_config['EnforcedStyle'] == 'long' && preferred_config['Enabled']
|
|
87
89
|
end
|
|
88
90
|
|
|
89
|
-
def
|
|
90
|
-
node.
|
|
91
|
+
def correct_argument(node)
|
|
92
|
+
node.first_argument.source
|
|
91
93
|
end
|
|
92
94
|
|
|
93
|
-
def
|
|
95
|
+
def correct_hash_expression(node)
|
|
94
96
|
node.receiver.receiver.source
|
|
95
97
|
end
|
|
98
|
+
|
|
99
|
+
def correct_dot(node)
|
|
100
|
+
node.receiver.loc.dot.source
|
|
101
|
+
end
|
|
96
102
|
end
|
|
97
103
|
end
|
|
98
104
|
end
|
|
@@ -39,13 +39,13 @@ module RuboCop
|
|
|
39
39
|
|
|
40
40
|
def_node_matcher :map_compact, <<~PATTERN
|
|
41
41
|
{
|
|
42
|
-
(
|
|
43
|
-
$(
|
|
42
|
+
(call
|
|
43
|
+
$(call _ {:map :collect}
|
|
44
44
|
(block_pass
|
|
45
45
|
(sym _))) _)
|
|
46
|
-
(
|
|
46
|
+
(call
|
|
47
47
|
(block
|
|
48
|
-
$(
|
|
48
|
+
$(call _ {:map :collect})
|
|
49
49
|
(args ...) _) _)
|
|
50
50
|
}
|
|
51
51
|
PATTERN
|
|
@@ -61,6 +61,7 @@ module RuboCop
|
|
|
61
61
|
remove_compact_method(corrector, map_node, node, node.parent)
|
|
62
62
|
end
|
|
63
63
|
end
|
|
64
|
+
alias on_csend on_send
|
|
64
65
|
|
|
65
66
|
private
|
|
66
67
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Performance
|
|
6
|
+
# Checks if the map method is used in a chain.
|
|
7
|
+
#
|
|
8
|
+
# Autocorrection is not supported because an appropriate block variable name cannot be determined automatically.
|
|
9
|
+
#
|
|
10
|
+
# @safety
|
|
11
|
+
# This cop is unsafe because false positives occur if the number of times the first method is executed
|
|
12
|
+
# affects the return value of subsequent methods.
|
|
13
|
+
#
|
|
14
|
+
# [source,ruby]
|
|
15
|
+
# ----
|
|
16
|
+
# class X
|
|
17
|
+
# def initialize
|
|
18
|
+
# @@num = 0
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# def foo
|
|
22
|
+
# @@num += 1
|
|
23
|
+
# self
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# def bar
|
|
27
|
+
# @@num * 2
|
|
28
|
+
# end
|
|
29
|
+
# end
|
|
30
|
+
#
|
|
31
|
+
# [X.new, X.new].map(&:foo).map(&:bar) # => [4, 4]
|
|
32
|
+
# [X.new, X.new].map { |x| x.foo.bar } # => [2, 4]
|
|
33
|
+
# ----
|
|
34
|
+
#
|
|
35
|
+
# @example
|
|
36
|
+
#
|
|
37
|
+
# # bad
|
|
38
|
+
# array.map(&:foo).map(&:bar)
|
|
39
|
+
#
|
|
40
|
+
# # good
|
|
41
|
+
# array.map { |item| item.foo.bar }
|
|
42
|
+
#
|
|
43
|
+
class MapMethodChain < Base
|
|
44
|
+
include IgnoredNode
|
|
45
|
+
|
|
46
|
+
MSG = 'Use `%<method_name>s { |x| x.%<map_args>s }` instead of `%<method_name>s` method chain.'
|
|
47
|
+
RESTRICT_ON_SEND = %i[map collect].freeze
|
|
48
|
+
|
|
49
|
+
def_node_matcher :block_pass_with_symbol_arg?, <<~PATTERN
|
|
50
|
+
(:block_pass (:sym $_))
|
|
51
|
+
PATTERN
|
|
52
|
+
|
|
53
|
+
def on_send(node)
|
|
54
|
+
return if part_of_ignored_node?(node)
|
|
55
|
+
return unless (map_arg = block_pass_with_symbol_arg?(node.first_argument))
|
|
56
|
+
|
|
57
|
+
map_args = [map_arg]
|
|
58
|
+
|
|
59
|
+
return unless (begin_of_chained_map_method = find_begin_of_chained_map_method(node, map_args))
|
|
60
|
+
|
|
61
|
+
range = begin_of_chained_map_method.loc.selector.begin.join(node.source_range.end)
|
|
62
|
+
message = format(MSG, method_name: begin_of_chained_map_method.method_name, map_args: map_args.join('.'))
|
|
63
|
+
|
|
64
|
+
add_offense(range, message: message)
|
|
65
|
+
|
|
66
|
+
ignore_node(node)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
|
72
|
+
def find_begin_of_chained_map_method(node, map_args)
|
|
73
|
+
return unless (chained_map_method = node.receiver)
|
|
74
|
+
return if !chained_map_method.call_type? || !RESTRICT_ON_SEND.include?(chained_map_method.method_name)
|
|
75
|
+
return unless (map_arg = block_pass_with_symbol_arg?(chained_map_method.first_argument))
|
|
76
|
+
|
|
77
|
+
map_args.unshift(map_arg)
|
|
78
|
+
|
|
79
|
+
receiver = chained_map_method.receiver
|
|
80
|
+
|
|
81
|
+
return chained_map_method unless receiver&.call_type? && block_pass_with_symbol_arg?(receiver.first_argument)
|
|
82
|
+
|
|
83
|
+
find_begin_of_chained_map_method(chained_map_method, map_args)
|
|
84
|
+
end
|
|
85
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -12,6 +12,12 @@ module RuboCop
|
|
|
12
12
|
# @safety
|
|
13
13
|
# This cop is unsafe because `Range#include?` (or `Range#member?`) and `Range#cover?`
|
|
14
14
|
# are not equivalent behavior.
|
|
15
|
+
# Example of a case where `Range#cover?` may not provide the desired result:
|
|
16
|
+
#
|
|
17
|
+
# [source,ruby]
|
|
18
|
+
# ----
|
|
19
|
+
# ('a'..'z').cover?('yellow') # => true
|
|
20
|
+
# ----
|
|
15
21
|
#
|
|
16
22
|
# @example
|
|
17
23
|
# # bad
|
|
@@ -20,11 +26,6 @@ module RuboCop
|
|
|
20
26
|
#
|
|
21
27
|
# # good
|
|
22
28
|
# ('a'..'z').cover?('b') # => true
|
|
23
|
-
#
|
|
24
|
-
# # Example of a case where `Range#cover?` may not provide
|
|
25
|
-
# # the desired result:
|
|
26
|
-
#
|
|
27
|
-
# ('a'..'z').cover?('yellow') # => true
|
|
28
29
|
class RangeInclude < Base
|
|
29
30
|
extend AutoCorrector
|
|
30
31
|
|
|
@@ -37,7 +38,7 @@ module RuboCop
|
|
|
37
38
|
# (We don't even catch it if the Range is in double parens)
|
|
38
39
|
|
|
39
40
|
def_node_matcher :range_include, <<~PATTERN
|
|
40
|
-
(
|
|
41
|
+
(call {irange erange (begin {irange erange})} ${:include? :member?} ...)
|
|
41
42
|
PATTERN
|
|
42
43
|
|
|
43
44
|
def on_send(node)
|
|
@@ -49,6 +50,7 @@ module RuboCop
|
|
|
49
50
|
end
|
|
50
51
|
end
|
|
51
52
|
end
|
|
53
|
+
alias on_csend on_send
|
|
52
54
|
end
|
|
53
55
|
end
|
|
54
56
|
end
|
|
@@ -63,7 +63,7 @@ module RuboCop
|
|
|
63
63
|
return unless TARGET_METHODS.include?(node.method_name)
|
|
64
64
|
return unless one_block_argument?(node.arguments)
|
|
65
65
|
|
|
66
|
-
block_argument = node.
|
|
66
|
+
block_argument = node.first_argument
|
|
67
67
|
block_body = node.body
|
|
68
68
|
return unless use_equality_comparison_block?(block_body)
|
|
69
69
|
return if same_block_argument_and_is_a_argument?(block_body, block_argument)
|