rubocop-performance 1.11.3 → 1.13.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 +25 -8
- data/lib/rubocop/cop/performance/ancestors_include.rb +4 -0
- data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +8 -6
- data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +23 -12
- data/lib/rubocop/cop/performance/case_when_splat.rb +11 -13
- data/lib/rubocop/cop/performance/casecmp.rb +4 -2
- data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +1 -1
- data/lib/rubocop/cop/performance/concurrent_monotonic_time.rb +42 -0
- data/lib/rubocop/cop/performance/count.rb +22 -13
- data/lib/rubocop/cop/performance/delete_prefix.rb +4 -4
- data/lib/rubocop/cop/performance/delete_suffix.rb +4 -4
- data/lib/rubocop/cop/performance/detect.rb +10 -13
- data/lib/rubocop/cop/performance/double_start_end_with.rb +21 -0
- data/lib/rubocop/cop/performance/end_with.rb +5 -0
- data/lib/rubocop/cop/performance/inefficient_hash_search.rb +3 -0
- data/lib/rubocop/cop/performance/map_compact.rb +24 -9
- data/lib/rubocop/cop/performance/open_struct.rb +4 -0
- data/lib/rubocop/cop/performance/range_include.rb +3 -2
- data/lib/rubocop/cop/performance/redundant_block_call.rb +1 -0
- data/lib/rubocop/cop/performance/redundant_equality_comparison_block.rb +20 -7
- data/lib/rubocop/cop/performance/redundant_merge.rb +4 -3
- data/lib/rubocop/cop/performance/redundant_string_chars.rb +22 -24
- data/lib/rubocop/cop/performance/regexp_match.rb +1 -2
- data/lib/rubocop/cop/performance/start_with.rb +5 -0
- data/lib/rubocop/cop/performance/string_identifier_argument.rb +59 -0
- data/lib/rubocop/cop/performance/string_include.rb +2 -1
- data/lib/rubocop/cop/performance/sum.rb +47 -23
- data/lib/rubocop/cop/performance/times_map.rb +12 -0
- data/lib/rubocop/cop/performance/unfreeze_string.rb +9 -6
- data/lib/rubocop/cop/performance_cops.rb +2 -0
- data/lib/rubocop/performance/version.rb +1 -1
- metadata +7 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 874ce36b942ed539ead8c3329a7dd6831224459a286c2b3189605775e725fc60
|
|
4
|
+
data.tar.gz: 584a851432d98e548151baa8afc1f9a30d5dcca05f8eae0d86c68f17bf933f34
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 102cccbb3be2ba55702a2e7aff3c56c6325c80481d269f87921ca1d16a79fa12830c6f080f49bacdebf4b5726e4f4c42af2527c13a4abac5aae0cf4544fb018e
|
|
7
|
+
data.tar.gz: 23c8beebceef175caf7e226a136bb3eaff500b71f5e3e26cba9fee9a84e36aa7a3b315b11216844b7980a421a82fff7a5346560ef1418f58fbe5261599fe4dec
|
data/config/default.yml
CHANGED
|
@@ -17,7 +17,7 @@ Performance/ArraySemiInfiniteRangeSlice:
|
|
|
17
17
|
VersionAdded: '1.9'
|
|
18
18
|
|
|
19
19
|
Performance/BigDecimalWithNumericArgument:
|
|
20
|
-
Description: 'Convert numeric
|
|
20
|
+
Description: 'Convert numeric literal to string and pass it to `BigDecimal`.'
|
|
21
21
|
Enabled: 'pending'
|
|
22
22
|
VersionAdded: '1.7'
|
|
23
23
|
|
|
@@ -43,10 +43,9 @@ Performance/CaseWhenSplat:
|
|
|
43
43
|
Reordering `when` conditions with a splat to the end
|
|
44
44
|
of the `when` branches can improve performance.
|
|
45
45
|
Enabled: false
|
|
46
|
-
AutoCorrect: false
|
|
47
46
|
SafeAutoCorrect: false
|
|
48
47
|
VersionAdded: '0.34'
|
|
49
|
-
VersionChanged: '
|
|
48
|
+
VersionChanged: '1.13'
|
|
50
49
|
|
|
51
50
|
Performance/Casecmp:
|
|
52
51
|
Description: >-
|
|
@@ -76,6 +75,12 @@ Performance/CompareWithBlock:
|
|
|
76
75
|
Enabled: true
|
|
77
76
|
VersionAdded: '0.46'
|
|
78
77
|
|
|
78
|
+
Performance/ConcurrentMonotonicTime:
|
|
79
|
+
Description: 'Use `Process.clock_gettime(Process::CLOCK_MONOTONIC)` instead of `Concurrent.monotonic_time`.'
|
|
80
|
+
Reference: 'https://github.com/rails/rails/pull/43502'
|
|
81
|
+
Enabled: pending
|
|
82
|
+
VersionAdded: '1.12'
|
|
83
|
+
|
|
79
84
|
Performance/ConstantRegexp:
|
|
80
85
|
Description: 'Finds regular expressions with dynamic components that are all constants.'
|
|
81
86
|
Enabled: pending
|
|
@@ -96,14 +101,18 @@ Performance/Count:
|
|
|
96
101
|
Performance/DeletePrefix:
|
|
97
102
|
Description: 'Use `delete_prefix` instead of `gsub`.'
|
|
98
103
|
Enabled: true
|
|
104
|
+
Safe: false
|
|
99
105
|
SafeMultiline: true
|
|
100
106
|
VersionAdded: '1.6'
|
|
107
|
+
VersionChanged: '1.11'
|
|
101
108
|
|
|
102
109
|
Performance/DeleteSuffix:
|
|
103
110
|
Description: 'Use `delete_suffix` instead of `gsub`.'
|
|
104
111
|
Enabled: true
|
|
112
|
+
Safe: false
|
|
105
113
|
SafeMultiline: true
|
|
106
114
|
VersionAdded: '1.6'
|
|
115
|
+
VersionChanged: '1.11'
|
|
107
116
|
|
|
108
117
|
Performance/Detect:
|
|
109
118
|
Description: >-
|
|
@@ -127,7 +136,7 @@ Performance/DoubleStartEndWith:
|
|
|
127
136
|
VersionAdded: '0.36'
|
|
128
137
|
VersionChanged: '0.48'
|
|
129
138
|
# Used to check for `starts_with?` and `ends_with?`.
|
|
130
|
-
# These methods are defined by
|
|
139
|
+
# These methods are defined by Active Support.
|
|
131
140
|
IncludeActiveSupportAliases: false
|
|
132
141
|
|
|
133
142
|
Performance/EndWith:
|
|
@@ -302,12 +311,17 @@ Performance/StartWith:
|
|
|
302
311
|
VersionAdded: '0.36'
|
|
303
312
|
VersionChanged: '1.10'
|
|
304
313
|
|
|
314
|
+
Performance/StringIdentifierArgument:
|
|
315
|
+
Description: 'Use symbol identifier argument instead of string identifier argument.'
|
|
316
|
+
Enabled: pending
|
|
317
|
+
VersionAdded: '1.13'
|
|
318
|
+
|
|
305
319
|
Performance/StringInclude:
|
|
306
320
|
Description: 'Use `String#include?` instead of a regex match with literal-only pattern.'
|
|
307
321
|
Enabled: 'pending'
|
|
308
|
-
AutoCorrect: false
|
|
309
322
|
SafeAutoCorrect: false
|
|
310
323
|
VersionAdded: '1.7'
|
|
324
|
+
VersionChanged: '1.12'
|
|
311
325
|
|
|
312
326
|
Performance/StringReplacement:
|
|
313
327
|
Description: >-
|
|
@@ -320,17 +334,20 @@ Performance/StringReplacement:
|
|
|
320
334
|
|
|
321
335
|
Performance/Sum:
|
|
322
336
|
Description: 'Use `sum` instead of a custom array summation.'
|
|
337
|
+
SafeAutoCorrect: false
|
|
323
338
|
Reference: 'https://blog.bigbinary.com/2016/11/02/ruby-2-4-introduces-enumerable-sum.html'
|
|
324
339
|
Enabled: 'pending'
|
|
325
340
|
VersionAdded: '1.8'
|
|
341
|
+
VersionChanged: '1.13'
|
|
342
|
+
OnlySumOrWithInitialValue: false
|
|
326
343
|
|
|
327
344
|
Performance/TimesMap:
|
|
328
345
|
Description: 'Checks for .times.map calls.'
|
|
329
|
-
AutoCorrect: false
|
|
330
346
|
Enabled: true
|
|
347
|
+
# See https://github.com/rubocop/rubocop/issues/4658
|
|
348
|
+
SafeAutoCorrect: false
|
|
331
349
|
VersionAdded: '0.36'
|
|
332
|
-
VersionChanged: '
|
|
333
|
-
SafeAutoCorrect: false # see https://github.com/rubocop/rubocop/issues/4658
|
|
350
|
+
VersionChanged: '1.13'
|
|
334
351
|
|
|
335
352
|
Performance/UnfreezeString:
|
|
336
353
|
Description: 'Use unary plus to get an unfrozen string literal.'
|
|
@@ -6,6 +6,10 @@ module RuboCop
|
|
|
6
6
|
# This cop is used to identify usages of `ancestors.include?` and
|
|
7
7
|
# change them to use `<=` instead.
|
|
8
8
|
#
|
|
9
|
+
# @safety
|
|
10
|
+
# This cop is unsafe because it can't tell whether the receiver is a class or an object.
|
|
11
|
+
# e.g. the false positive was for `Nokogiri::XML::Node#ancestors`.
|
|
12
|
+
#
|
|
9
13
|
# @example
|
|
10
14
|
# # bad
|
|
11
15
|
# A.ancestors.include?(B)
|
|
@@ -7,15 +7,17 @@ module RuboCop
|
|
|
7
7
|
# can be replaced by `Array#take` and `Array#drop`.
|
|
8
8
|
# This cop was created due to a mistake in microbenchmark and hence is disabled by default.
|
|
9
9
|
# Refer https://github.com/rubocop/rubocop-performance/pull/175#issuecomment-731892717
|
|
10
|
-
#
|
|
10
|
+
#
|
|
11
|
+
# @safety
|
|
12
|
+
# This cop is unsafe for string slices because strings do not have `#take` and `#drop` methods.
|
|
11
13
|
#
|
|
12
14
|
# @example
|
|
13
15
|
# # bad
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
16
|
+
# array[..2]
|
|
17
|
+
# array[...2]
|
|
18
|
+
# array[2..]
|
|
19
|
+
# array[2...]
|
|
20
|
+
# array.slice(..2)
|
|
19
21
|
#
|
|
20
22
|
# # good
|
|
21
23
|
# array.take(3)
|
|
@@ -10,36 +10,47 @@ module RuboCop
|
|
|
10
10
|
# @example
|
|
11
11
|
# # bad
|
|
12
12
|
# BigDecimal(1, 2)
|
|
13
|
+
# 4.to_d(6)
|
|
13
14
|
# BigDecimal(1.2, 3, exception: true)
|
|
15
|
+
# 4.5.to_d(6, exception: true)
|
|
14
16
|
#
|
|
15
17
|
# # good
|
|
16
18
|
# BigDecimal('1', 2)
|
|
19
|
+
# BigDecimal('4', 6)
|
|
17
20
|
# BigDecimal('1.2', 3, exception: true)
|
|
21
|
+
# BigDecimal('4.5', 6, exception: true)
|
|
18
22
|
#
|
|
19
23
|
class BigDecimalWithNumericArgument < Base
|
|
20
24
|
extend AutoCorrector
|
|
21
25
|
|
|
22
|
-
MSG = 'Convert numeric
|
|
23
|
-
RESTRICT_ON_SEND = %i[BigDecimal].freeze
|
|
26
|
+
MSG = 'Convert numeric literal to string and pass it to `BigDecimal`.'
|
|
27
|
+
RESTRICT_ON_SEND = %i[BigDecimal to_d].freeze
|
|
24
28
|
|
|
25
29
|
def_node_matcher :big_decimal_with_numeric_argument?, <<~PATTERN
|
|
26
30
|
(send nil? :BigDecimal $numeric_type? ...)
|
|
27
31
|
PATTERN
|
|
28
32
|
|
|
33
|
+
def_node_matcher :to_d?, <<~PATTERN
|
|
34
|
+
(send [!nil? $numeric_type?] :to_d ...)
|
|
35
|
+
PATTERN
|
|
36
|
+
|
|
29
37
|
def on_send(node)
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
if (numeric = big_decimal_with_numeric_argument?(node))
|
|
39
|
+
add_offense(numeric.source_range) do |corrector|
|
|
40
|
+
corrector.wrap(numeric, "'", "'")
|
|
41
|
+
end
|
|
42
|
+
elsif (numeric_to_d = to_d?(node))
|
|
43
|
+
add_offense(numeric_to_d.source_range) do |corrector|
|
|
44
|
+
big_decimal_args = node
|
|
45
|
+
.arguments
|
|
46
|
+
.map(&:source)
|
|
47
|
+
.unshift("'#{numeric_to_d.source}'")
|
|
48
|
+
.join(', ')
|
|
32
49
|
|
|
33
|
-
|
|
34
|
-
|
|
50
|
+
corrector.replace(node, "BigDecimal(#{big_decimal_args})")
|
|
51
|
+
end
|
|
35
52
|
end
|
|
36
53
|
end
|
|
37
|
-
|
|
38
|
-
private
|
|
39
|
-
|
|
40
|
-
def specifies_precision?(node)
|
|
41
|
-
node.arguments.size > 1 && !node.arguments[1].hash_type?
|
|
42
|
-
end
|
|
43
54
|
end
|
|
44
55
|
end
|
|
45
56
|
end
|
|
@@ -17,11 +17,13 @@ module RuboCop
|
|
|
17
17
|
# this defining a higher level when condition to override a condition
|
|
18
18
|
# that is inside of the splat expansion.
|
|
19
19
|
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
20
|
+
# @safety
|
|
21
|
+
# This cop is not unsafe auto-correction because it is not a guaranteed
|
|
22
|
+
# performance improvement. If the data being processed by the `case` condition is
|
|
23
|
+
# normalized in a manner that favors hitting a condition in the splat expansion,
|
|
24
|
+
# it is possible that moving the splat condition to the end will use more memory,
|
|
25
|
+
# and run slightly slower.
|
|
26
|
+
# See for more details: https://github.com/rubocop/rubocop/pull/6163
|
|
25
27
|
#
|
|
26
28
|
# @example
|
|
27
29
|
# # bad
|
|
@@ -58,10 +60,8 @@ module RuboCop
|
|
|
58
60
|
include RangeHelp
|
|
59
61
|
extend AutoCorrector
|
|
60
62
|
|
|
61
|
-
MSG = 'Reordering `when` conditions with a splat to the end '
|
|
62
|
-
|
|
63
|
-
ARRAY_MSG = 'Pass the contents of array literals ' \
|
|
64
|
-
'directly to `when` conditions.'
|
|
63
|
+
MSG = 'Reordering `when` conditions with a splat to the end of the `when` branches can improve performance.'
|
|
64
|
+
ARRAY_MSG = 'Pass the contents of array literals directly to `when` conditions.'
|
|
65
65
|
|
|
66
66
|
def on_case(case_node)
|
|
67
67
|
when_conditions = case_node.when_branches.flat_map(&:conditions)
|
|
@@ -134,13 +134,11 @@ module RuboCop
|
|
|
134
134
|
end
|
|
135
135
|
|
|
136
136
|
def new_condition_with_then(node, new_condition)
|
|
137
|
-
"\n#{indent_for(node)}when "
|
|
138
|
-
"#{new_condition} then #{node.body.source}"
|
|
137
|
+
"\n#{indent_for(node)}when #{new_condition} then #{node.body.source}"
|
|
139
138
|
end
|
|
140
139
|
|
|
141
140
|
def new_branch_without_then(node, new_condition)
|
|
142
|
-
"\n#{indent_for(node)}when #{new_condition}"
|
|
143
|
-
"\n#{indent_for(node.body)}#{node.body.source}"
|
|
141
|
+
"\n#{indent_for(node)}when #{new_condition}\n#{indent_for(node.body)}#{node.body.source}"
|
|
144
142
|
end
|
|
145
143
|
|
|
146
144
|
def indent_for(node)
|
|
@@ -5,8 +5,10 @@ module RuboCop
|
|
|
5
5
|
module Performance
|
|
6
6
|
# This cop identifies places where a case-insensitive string comparison
|
|
7
7
|
# can better be implemented using `casecmp`.
|
|
8
|
-
#
|
|
9
|
-
#
|
|
8
|
+
#
|
|
9
|
+
# @safety
|
|
10
|
+
# This cop is unsafe because `String#casecmp` and `String#casecmp?` behave
|
|
11
|
+
# differently when using Non-ASCII characters.
|
|
10
12
|
#
|
|
11
13
|
# @example
|
|
12
14
|
# # bad
|
|
@@ -33,7 +33,7 @@ module RuboCop
|
|
|
33
33
|
#
|
|
34
34
|
class CollectionLiteralInLoop < Base
|
|
35
35
|
MSG = 'Avoid immutable %<literal_class>s literals in loops. '\
|
|
36
|
-
|
|
36
|
+
'It is better to extract it into a local variable or a constant.'
|
|
37
37
|
|
|
38
38
|
POST_CONDITION_LOOP_TYPES = %i[while_post until_post].freeze
|
|
39
39
|
LOOP_TYPES = (POST_CONDITION_LOOP_TYPES + %i[while until for]).freeze
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Performance
|
|
6
|
+
# This cop identifies places where `Concurrent.monotonic_time`
|
|
7
|
+
# can be replaced by `Process.clock_gettime(Process::CLOCK_MONOTONIC)`.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
#
|
|
11
|
+
# # bad
|
|
12
|
+
# Concurrent.monotonic_time
|
|
13
|
+
#
|
|
14
|
+
# # good
|
|
15
|
+
# Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
16
|
+
#
|
|
17
|
+
class ConcurrentMonotonicTime < Base
|
|
18
|
+
extend AutoCorrector
|
|
19
|
+
|
|
20
|
+
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
|
21
|
+
RESTRICT_ON_SEND = %i[monotonic_time].freeze
|
|
22
|
+
|
|
23
|
+
def_node_matcher :concurrent_monotonic_time?, <<~PATTERN
|
|
24
|
+
(send
|
|
25
|
+
(const {nil? cbase} :Concurrent) :monotonic_time ...)
|
|
26
|
+
PATTERN
|
|
27
|
+
|
|
28
|
+
def on_send(node)
|
|
29
|
+
return unless concurrent_monotonic_time?(node)
|
|
30
|
+
|
|
31
|
+
optional_unit_parameter = ", #{node.first_argument.source}" if node.first_argument
|
|
32
|
+
prefer = "Process.clock_gettime(Process::CLOCK_MONOTONIC#{optional_unit_parameter})"
|
|
33
|
+
message = format(MSG, prefer: prefer, current: node.source)
|
|
34
|
+
|
|
35
|
+
add_offense(node, message: message) do |corrector|
|
|
36
|
+
corrector.replace(node, prefer)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -7,6 +7,28 @@ module RuboCop
|
|
|
7
7
|
# follow calls to `select`, `find_all`, `filter` or `reject`. Querying logic can instead be
|
|
8
8
|
# passed to the `count` call.
|
|
9
9
|
#
|
|
10
|
+
# @safety
|
|
11
|
+
# This cop is unsafe because it has known compatibility issues with `ActiveRecord` and other
|
|
12
|
+
# frameworks. ActiveRecord's `count` ignores the block that is passed to it.
|
|
13
|
+
# `ActiveRecord` will ignore the block that is passed to `count`.
|
|
14
|
+
# Other methods, such as `select`, will convert the association to an
|
|
15
|
+
# array and then run the block on the array. A simple work around to
|
|
16
|
+
# make `count` work with a block is to call `to_a.count {...}`.
|
|
17
|
+
#
|
|
18
|
+
# For example:
|
|
19
|
+
#
|
|
20
|
+
# [source,ruby]
|
|
21
|
+
# ----
|
|
22
|
+
# `Model.where(id: [1, 2, 3]).select { |m| m.method == true }.size`
|
|
23
|
+
# ----
|
|
24
|
+
#
|
|
25
|
+
# becomes:
|
|
26
|
+
#
|
|
27
|
+
# [source,ruby]
|
|
28
|
+
# ----
|
|
29
|
+
# `Model.where(id: [1, 2, 3]).to_a.count { |m| m.method == true }`
|
|
30
|
+
# ----
|
|
31
|
+
#
|
|
10
32
|
# @example
|
|
11
33
|
# # bad
|
|
12
34
|
# [1, 2, 3].select { |e| e > 2 }.size
|
|
@@ -24,19 +46,6 @@ module RuboCop
|
|
|
24
46
|
# [1, 2, 3].count { |e| e < 2 && e.even? }
|
|
25
47
|
# Model.select('field AS field_one').count
|
|
26
48
|
# Model.select(:value).count
|
|
27
|
-
#
|
|
28
|
-
# `ActiveRecord` compatibility:
|
|
29
|
-
# `ActiveRecord` will ignore the block that is passed to `count`.
|
|
30
|
-
# Other methods, such as `select`, will convert the association to an
|
|
31
|
-
# array and then run the block on the array. A simple work around to
|
|
32
|
-
# make `count` work with a block is to call `to_a.count {...}`.
|
|
33
|
-
#
|
|
34
|
-
# Example:
|
|
35
|
-
# `Model.where(id: [1, 2, 3]).select { |m| m.method == true }.size`
|
|
36
|
-
#
|
|
37
|
-
# becomes:
|
|
38
|
-
#
|
|
39
|
-
# `Model.where(id: [1, 2, 3]).to_a.count { |m| m.method == true }`
|
|
40
49
|
class Count < Base
|
|
41
50
|
include RangeHelp
|
|
42
51
|
extend AutoCorrector
|
|
@@ -14,6 +14,9 @@ module RuboCop
|
|
|
14
14
|
#
|
|
15
15
|
# The `delete_prefix('prefix')` method is faster than `gsub(/\Aprefix/, '')`.
|
|
16
16
|
#
|
|
17
|
+
# @safety
|
|
18
|
+
# This cop is unsafe because `Pathname` has `sub` but not `delete_prefix`.
|
|
19
|
+
#
|
|
17
20
|
# @example
|
|
18
21
|
#
|
|
19
22
|
# # bad
|
|
@@ -46,9 +49,6 @@ module RuboCop
|
|
|
46
49
|
class DeletePrefix < Base
|
|
47
50
|
include RegexpMetacharacter
|
|
48
51
|
extend AutoCorrector
|
|
49
|
-
extend TargetRubyVersion
|
|
50
|
-
|
|
51
|
-
minimum_target_ruby_version 2.5
|
|
52
52
|
|
|
53
53
|
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
|
54
54
|
RESTRICT_ON_SEND = %i[gsub gsub! sub sub!].freeze
|
|
@@ -66,7 +66,7 @@ module RuboCop
|
|
|
66
66
|
|
|
67
67
|
def on_send(node)
|
|
68
68
|
return unless (receiver, bad_method, regexp_str, replace_string = delete_prefix_candidate?(node))
|
|
69
|
-
return unless replace_string.
|
|
69
|
+
return unless replace_string.empty?
|
|
70
70
|
|
|
71
71
|
good_method = PREFERRED_METHODS[bad_method]
|
|
72
72
|
|
|
@@ -14,6 +14,9 @@ module RuboCop
|
|
|
14
14
|
#
|
|
15
15
|
# The `delete_suffix('suffix')` method is faster than `gsub(/suffix\z/, '')`.
|
|
16
16
|
#
|
|
17
|
+
# @safety
|
|
18
|
+
# This cop is unsafe because `Pathname` has `sub` but not `delete_suffix`.
|
|
19
|
+
#
|
|
17
20
|
# @example
|
|
18
21
|
#
|
|
19
22
|
# # bad
|
|
@@ -46,9 +49,6 @@ module RuboCop
|
|
|
46
49
|
class DeleteSuffix < Base
|
|
47
50
|
include RegexpMetacharacter
|
|
48
51
|
extend AutoCorrector
|
|
49
|
-
extend TargetRubyVersion
|
|
50
|
-
|
|
51
|
-
minimum_target_ruby_version 2.5
|
|
52
52
|
|
|
53
53
|
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
|
54
54
|
RESTRICT_ON_SEND = %i[gsub gsub! sub sub!].freeze
|
|
@@ -66,7 +66,7 @@ module RuboCop
|
|
|
66
66
|
|
|
67
67
|
def on_send(node)
|
|
68
68
|
return unless (receiver, bad_method, regexp_str, replace_string = delete_suffix_candidate?(node))
|
|
69
|
-
return unless replace_string.
|
|
69
|
+
return unless replace_string.empty?
|
|
70
70
|
|
|
71
71
|
good_method = PREFERRED_METHODS[bad_method]
|
|
72
72
|
|
|
@@ -7,6 +7,11 @@ module RuboCop
|
|
|
7
7
|
# chained to `select`, `find_all` or `filter` and change them to use
|
|
8
8
|
# `detect` instead.
|
|
9
9
|
#
|
|
10
|
+
# @safety
|
|
11
|
+
# This cop is unsafe because is has known compatibility issues with `ActiveRecord` and other
|
|
12
|
+
# frameworks. `ActiveRecord` does not implement a `detect` method and `find` has its own
|
|
13
|
+
# meaning. Correcting `ActiveRecord` methods with this cop should be considered unsafe.
|
|
14
|
+
#
|
|
10
15
|
# @example
|
|
11
16
|
# # bad
|
|
12
17
|
# [].select { |item| true }.first
|
|
@@ -22,23 +27,15 @@ module RuboCop
|
|
|
22
27
|
# [].detect { |item| true }
|
|
23
28
|
# [].reverse.detect { |item| true }
|
|
24
29
|
#
|
|
25
|
-
# `ActiveRecord` compatibility:
|
|
26
|
-
# `ActiveRecord` does not implement a `detect` method and `find` has its
|
|
27
|
-
# own meaning. Correcting ActiveRecord methods with this cop should be
|
|
28
|
-
# considered unsafe.
|
|
29
30
|
class Detect < Base
|
|
30
31
|
extend AutoCorrector
|
|
31
32
|
|
|
32
33
|
CANDIDATE_METHODS = Set[:select, :find_all, :filter].freeze
|
|
33
34
|
|
|
34
|
-
MSG = 'Use `%<prefer>s` instead of '
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
INDEX_MSG = 'Use `%<prefer>s` instead of ' \
|
|
39
|
-
'`%<first_method>s[%<index>i]`.'
|
|
40
|
-
INDEX_REVERSE_MSG = 'Use `reverse.%<prefer>s` instead of ' \
|
|
41
|
-
'`%<first_method>s[%<index>i]`.'
|
|
35
|
+
MSG = 'Use `%<prefer>s` instead of `%<first_method>s.%<second_method>s`.'
|
|
36
|
+
REVERSE_MSG = 'Use `reverse.%<prefer>s` instead of `%<first_method>s.%<second_method>s`.'
|
|
37
|
+
INDEX_MSG = 'Use `%<prefer>s` instead of `%<first_method>s[%<index>i]`.'
|
|
38
|
+
INDEX_REVERSE_MSG = 'Use `reverse.%<prefer>s` instead of `%<first_method>s[%<index>i]`.'
|
|
42
39
|
RESTRICT_ON_SEND = %i[first last []].freeze
|
|
43
40
|
|
|
44
41
|
def_node_matcher :detect_candidate?, <<~PATTERN
|
|
@@ -94,7 +91,7 @@ module RuboCop
|
|
|
94
91
|
end
|
|
95
92
|
|
|
96
93
|
def replacement(method, index)
|
|
97
|
-
if method == :last || method == :[] && index == -1
|
|
94
|
+
if method == :last || (method == :[] && index == -1)
|
|
98
95
|
"reverse.#{preferred_method}"
|
|
99
96
|
else
|
|
100
97
|
preferred_method
|
|
@@ -7,6 +7,9 @@ module RuboCop
|
|
|
7
7
|
# separated by `||`. In some cases such calls can be replaced
|
|
8
8
|
# with an single `#start_with?`/`#end_with?` call.
|
|
9
9
|
#
|
|
10
|
+
# `IncludeActiveSupportAliases` configuration option is used to check for
|
|
11
|
+
# `starts_with?` and `ends_with?`. These methods are defined by Active Support.
|
|
12
|
+
#
|
|
10
13
|
# @example
|
|
11
14
|
# # bad
|
|
12
15
|
# str.start_with?("a") || str.start_with?(Some::CONST)
|
|
@@ -17,6 +20,24 @@ module RuboCop
|
|
|
17
20
|
# str.start_with?("a", Some::CONST)
|
|
18
21
|
# str.start_with?("a", "b", "c")
|
|
19
22
|
# str.end_with?(var1, var2)
|
|
23
|
+
#
|
|
24
|
+
# @example IncludeActiveSupportAliases: false (default)
|
|
25
|
+
# # good
|
|
26
|
+
# str.starts_with?("a", "b") || str.starts_with?("c")
|
|
27
|
+
# str.ends_with?(var1) || str.ends_with?(var2)
|
|
28
|
+
#
|
|
29
|
+
# str.starts_with?("a", "b", "c")
|
|
30
|
+
# str.ends_with?(var1, var2)
|
|
31
|
+
#
|
|
32
|
+
# @example IncludeActiveSupportAliases: true
|
|
33
|
+
# # bad
|
|
34
|
+
# str.starts_with?("a", "b") || str.starts_with?("c")
|
|
35
|
+
# str.ends_with?(var1) || str.ends_with?(var2)
|
|
36
|
+
#
|
|
37
|
+
# # good
|
|
38
|
+
# str.starts_with?("a", "b", "c")
|
|
39
|
+
# str.ends_with?(var1, var2)
|
|
40
|
+
#
|
|
20
41
|
class DoubleStartEndWith < Base
|
|
21
42
|
extend AutoCorrector
|
|
22
43
|
|
|
@@ -9,6 +9,11 @@ module RuboCop
|
|
|
9
9
|
# `end$` is unsafe as it will behave incompatible with `end_with?`
|
|
10
10
|
# for receiver is multiline string.
|
|
11
11
|
#
|
|
12
|
+
# @safety
|
|
13
|
+
# This will change to a new method call which isn't guaranteed to be on the
|
|
14
|
+
# object. Switching these methods has to be done with knowledge of the types
|
|
15
|
+
# of the variables which rubocop doesn't have.
|
|
16
|
+
#
|
|
12
17
|
# @example
|
|
13
18
|
# # bad
|
|
14
19
|
# 'abc'.match?(/bc\Z/)
|
|
@@ -15,6 +15,9 @@ module RuboCop
|
|
|
15
15
|
# both perform an O(n) search through all of the values, calling `values`
|
|
16
16
|
# allocates a new array while using `value?` does not.
|
|
17
17
|
#
|
|
18
|
+
# @safety
|
|
19
|
+
# This cop is unsafe because it can't tell whether the receiver is a hash object.
|
|
20
|
+
#
|
|
18
21
|
# @example
|
|
19
22
|
# # bad
|
|
20
23
|
# { a: 1, b: 2 }.keys.include?(:a)
|
|
@@ -6,8 +6,10 @@ module RuboCop
|
|
|
6
6
|
# In Ruby 2.7, `Enumerable#filter_map` has been added.
|
|
7
7
|
#
|
|
8
8
|
# This cop identifies places where `map { ... }.compact` can be replaced by `filter_map`.
|
|
9
|
-
#
|
|
10
|
-
#
|
|
9
|
+
#
|
|
10
|
+
# @safety
|
|
11
|
+
# This cop's autocorrection is unsafe because `map { ... }.compact` that is not
|
|
12
|
+
# compatible with `filter_map`.
|
|
11
13
|
#
|
|
12
14
|
# [source,ruby]
|
|
13
15
|
# ----
|
|
@@ -56,28 +58,41 @@ module RuboCop
|
|
|
56
58
|
|
|
57
59
|
add_offense(range) do |corrector|
|
|
58
60
|
corrector.replace(map_node.loc.selector, 'filter_map')
|
|
59
|
-
corrector.
|
|
60
|
-
corrector.remove(compact_method_range(node))
|
|
61
|
+
remove_compact_method(corrector, node, node.parent)
|
|
61
62
|
end
|
|
62
63
|
end
|
|
63
64
|
|
|
64
65
|
private
|
|
65
66
|
|
|
66
|
-
def
|
|
67
|
-
chained_method = compact_node.parent
|
|
67
|
+
def remove_compact_method(corrector, compact_node, chained_method)
|
|
68
68
|
compact_method_range = compact_node.loc.selector
|
|
69
69
|
|
|
70
|
-
if compact_node.multiline? && chained_method&.loc.respond_to?(:selector) &&
|
|
70
|
+
if compact_node.multiline? && chained_method&.loc.respond_to?(:selector) && chained_method.dot? &&
|
|
71
|
+
!map_method_and_compact_method_on_same_line?(compact_node) &&
|
|
71
72
|
!invoke_method_after_map_compact_on_same_line?(compact_node, chained_method)
|
|
72
|
-
|
|
73
|
+
compact_method_range = compact_method_with_final_newline_range(compact_method_range)
|
|
73
74
|
else
|
|
74
|
-
|
|
75
|
+
corrector.remove(compact_node.loc.dot)
|
|
75
76
|
end
|
|
77
|
+
|
|
78
|
+
corrector.remove(compact_method_range)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def map_method_and_compact_method_on_same_line?(compact_node)
|
|
82
|
+
return false unless compact_node.children.first.respond_to?(:send_node)
|
|
83
|
+
|
|
84
|
+
map_node = compact_node.children.first.send_node
|
|
85
|
+
|
|
86
|
+
compact_node.loc.selector.line == map_node.loc.selector.line
|
|
76
87
|
end
|
|
77
88
|
|
|
78
89
|
def invoke_method_after_map_compact_on_same_line?(compact_node, chained_method)
|
|
79
90
|
compact_node.loc.selector.line == chained_method.loc.selector.line
|
|
80
91
|
end
|
|
92
|
+
|
|
93
|
+
def compact_method_with_final_newline_range(compact_method_range)
|
|
94
|
+
range_by_whole_lines(compact_method_range, include_final_newline: true)
|
|
95
|
+
end
|
|
81
96
|
end
|
|
82
97
|
end
|
|
83
98
|
end
|
|
@@ -11,6 +11,10 @@ module RuboCop
|
|
|
11
11
|
# especially in case of single-threaded
|
|
12
12
|
# applications with multiple `OpenStruct` instantiations.
|
|
13
13
|
#
|
|
14
|
+
# @safety
|
|
15
|
+
# This cop is unsafe because `OpenStruct.new` and `Struct.new`
|
|
16
|
+
# are not equivalent.
|
|
17
|
+
#
|
|
14
18
|
# @example
|
|
15
19
|
# # bad
|
|
16
20
|
# class MyClass
|
|
@@ -9,8 +9,9 @@ module RuboCop
|
|
|
9
9
|
# end points of the `Range`. In a great majority of cases, this is what
|
|
10
10
|
# is wanted.
|
|
11
11
|
#
|
|
12
|
-
#
|
|
13
|
-
# `Range#
|
|
12
|
+
# @safety
|
|
13
|
+
# This cop is unsafe because `Range#include?` (or `Range#member?`) and `Range#cover?`
|
|
14
|
+
# are not equivalent behaviour.
|
|
14
15
|
#
|
|
15
16
|
# @example
|
|
16
17
|
# # bad
|
|
@@ -9,8 +9,9 @@ module RuboCop
|
|
|
9
9
|
# By default, `Object#===` behaves the same as `Object#==`, but this
|
|
10
10
|
# behavior is appropriately overridden in subclass. For example,
|
|
11
11
|
# `Range#===` returns `true` when argument is within the range.
|
|
12
|
-
#
|
|
13
|
-
#
|
|
12
|
+
#
|
|
13
|
+
# @safety
|
|
14
|
+
# This cop is unsafe because `===` and `==` do not always behave the same.
|
|
14
15
|
#
|
|
15
16
|
# @example
|
|
16
17
|
# # bad
|
|
@@ -24,9 +25,6 @@ module RuboCop
|
|
|
24
25
|
#
|
|
25
26
|
class RedundantEqualityComparisonBlock < Base
|
|
26
27
|
extend AutoCorrector
|
|
27
|
-
extend TargetRubyVersion
|
|
28
|
-
|
|
29
|
-
minimum_target_ruby_version 2.5
|
|
30
28
|
|
|
31
29
|
MSG = 'Use `%<prefer>s` instead of block.'
|
|
32
30
|
|
|
@@ -74,12 +72,27 @@ module RuboCop
|
|
|
74
72
|
|
|
75
73
|
def new_argument(block_argument, block_body)
|
|
76
74
|
if block_argument.source == block_body.receiver.source
|
|
77
|
-
block_body.first_argument
|
|
75
|
+
rhs = block_body.first_argument
|
|
76
|
+
return if use_block_argument_in_method_argument_of_operand?(block_argument, rhs)
|
|
77
|
+
|
|
78
|
+
rhs.source
|
|
78
79
|
elsif block_argument.source == block_body.first_argument.source
|
|
79
|
-
block_body.receiver
|
|
80
|
+
lhs = block_body.receiver
|
|
81
|
+
return if use_block_argument_in_method_argument_of_operand?(block_argument, lhs)
|
|
82
|
+
|
|
83
|
+
lhs.source
|
|
80
84
|
end
|
|
81
85
|
end
|
|
82
86
|
|
|
87
|
+
def use_block_argument_in_method_argument_of_operand?(block_argument, operand)
|
|
88
|
+
return false unless operand.send_type?
|
|
89
|
+
|
|
90
|
+
arguments = operand.arguments
|
|
91
|
+
arguments.inject(arguments.map(&:source)) do |operand_sources, argument|
|
|
92
|
+
operand_sources + argument.each_descendant(:lvar).map(&:source)
|
|
93
|
+
end.any?(block_argument.source)
|
|
94
|
+
end
|
|
95
|
+
|
|
83
96
|
def offense_range(node)
|
|
84
97
|
node.send_node.loc.selector.join(node.source_range.end)
|
|
85
98
|
end
|
|
@@ -8,8 +8,9 @@ module RuboCop
|
|
|
8
8
|
# You can set the maximum number of key-value pairs to consider
|
|
9
9
|
# an offense with `MaxKeyValuePairs`.
|
|
10
10
|
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
11
|
+
# @safety
|
|
12
|
+
# This cop is unsafe because RuboCop cannot determine if the
|
|
13
|
+
# receiver of `merge!` is actually a hash or not.
|
|
13
14
|
#
|
|
14
15
|
# @example
|
|
15
16
|
# # bad
|
|
@@ -91,7 +92,7 @@ module RuboCop
|
|
|
91
92
|
end
|
|
92
93
|
|
|
93
94
|
def non_redundant_pairs?(receiver, pairs)
|
|
94
|
-
pairs.size > 1 && !receiver.pure? || pairs.size > max_key_value_pairs
|
|
95
|
+
(pairs.size > 1 && !receiver.pure?) || pairs.size > max_key_value_pairs
|
|
95
96
|
end
|
|
96
97
|
|
|
97
98
|
def kwsplat_used?(pairs)
|
|
@@ -16,35 +16,35 @@ module RuboCop
|
|
|
16
16
|
# # bad
|
|
17
17
|
# str.chars.first
|
|
18
18
|
# str.chars.first(2)
|
|
19
|
-
# str.chars.last
|
|
20
|
-
# str.chars.last(2)
|
|
21
19
|
#
|
|
22
20
|
# # good
|
|
23
21
|
# str[0]
|
|
24
22
|
# str[0...2].chars
|
|
25
|
-
# str[-1]
|
|
26
|
-
# str[-2..-1].chars
|
|
27
23
|
#
|
|
28
24
|
# # bad
|
|
29
25
|
# str.chars.take(2)
|
|
30
|
-
# str.chars.drop(2)
|
|
31
26
|
# str.chars.length
|
|
32
27
|
# str.chars.size
|
|
33
28
|
# str.chars.empty?
|
|
34
29
|
#
|
|
35
30
|
# # good
|
|
36
31
|
# str[0...2].chars
|
|
37
|
-
# str[2..-1].chars
|
|
38
32
|
# str.length
|
|
39
33
|
# str.size
|
|
40
34
|
# str.empty?
|
|
41
35
|
#
|
|
36
|
+
# # For example, if the receiver is a blank string, it will be incompatible.
|
|
37
|
+
# # If a negative value is specified for the receiver, `nil` is returned.
|
|
38
|
+
# str.chars.last # Incompatible with `str[-1]`.
|
|
39
|
+
# str.chars.last(2) # Incompatible with `str[-2..-1].chars`.
|
|
40
|
+
# str.chars.drop(2) # Incompatible with `str[2..-1].chars`.
|
|
41
|
+
#
|
|
42
42
|
class RedundantStringChars < Base
|
|
43
43
|
include RangeHelp
|
|
44
44
|
extend AutoCorrector
|
|
45
45
|
|
|
46
46
|
MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
|
|
47
|
-
RESTRICT_ON_SEND = %i[[] slice first
|
|
47
|
+
RESTRICT_ON_SEND = %i[[] slice first take length size empty?].freeze
|
|
48
48
|
|
|
49
49
|
def_node_matcher :redundant_chars_call?, <<~PATTERN
|
|
50
50
|
(send $(send _ :chars) $_ $...)
|
|
@@ -80,32 +80,30 @@ module RuboCop
|
|
|
80
80
|
format(MSG, good_method: good_method, bad_method: bad_method)
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
84
83
|
def build_good_method(method, args)
|
|
85
84
|
case method
|
|
86
|
-
when :
|
|
85
|
+
when :slice
|
|
87
86
|
"[#{build_call_args(args)}].chars"
|
|
88
|
-
when :first
|
|
89
|
-
|
|
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
|
|
87
|
+
when :[], :first
|
|
88
|
+
build_good_method_for_brackets_or_first_method(method, args)
|
|
100
89
|
when :take
|
|
101
90
|
"[0...#{args.first.source}].chars"
|
|
102
|
-
when :drop
|
|
103
|
-
"[#{args.first.source}..-1].chars"
|
|
104
91
|
else
|
|
105
92
|
".#{method}"
|
|
106
93
|
end
|
|
107
94
|
end
|
|
108
|
-
|
|
95
|
+
|
|
96
|
+
def build_good_method_for_brackets_or_first_method(method, args)
|
|
97
|
+
first_arg = args.first
|
|
98
|
+
|
|
99
|
+
if first_arg&.range_type?
|
|
100
|
+
"[#{build_call_args(args)}].chars"
|
|
101
|
+
elsif method == :first && args.any?
|
|
102
|
+
"[0...#{args.first.source}].chars"
|
|
103
|
+
else
|
|
104
|
+
first_arg ? "[#{first_arg.source}]" : '[0]'
|
|
105
|
+
end
|
|
106
|
+
end
|
|
109
107
|
|
|
110
108
|
def build_bad_method(method, args)
|
|
111
109
|
case method
|
|
@@ -78,8 +78,7 @@ module RuboCop
|
|
|
78
78
|
# Constants are included in this list because it is unlikely that
|
|
79
79
|
# someone will store `nil` as a constant and then use it for comparison
|
|
80
80
|
TYPES_IMPLEMENTING_MATCH = %i[const regexp str sym].freeze
|
|
81
|
-
MSG = 'Use `match?` instead of `%<current>s` when `MatchData` '
|
|
82
|
-
'is not used.'
|
|
81
|
+
MSG = 'Use `match?` instead of `%<current>s` when `MatchData` is not used.'
|
|
83
82
|
|
|
84
83
|
def_node_matcher :match_method?, <<~PATTERN
|
|
85
84
|
{
|
|
@@ -9,6 +9,11 @@ module RuboCop
|
|
|
9
9
|
# `^start` is unsafe as it will behave incompatible with `start_with?`
|
|
10
10
|
# for receiver is multiline string.
|
|
11
11
|
#
|
|
12
|
+
# @safety
|
|
13
|
+
# This will change to a new method call which isn't guaranteed to be on the
|
|
14
|
+
# object. Switching these methods has to be done with knowledge of the types
|
|
15
|
+
# of the variables which rubocop doesn't have.
|
|
16
|
+
#
|
|
12
17
|
# @example
|
|
13
18
|
# # bad
|
|
14
19
|
# 'abc'.match?(/\Aab/)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Performance
|
|
6
|
+
# This cop identifies places where string identifier argument can be replaced
|
|
7
|
+
# by symbol identifier argument.
|
|
8
|
+
# It prevents the redundancy of the internal string-to-symbol conversion.
|
|
9
|
+
#
|
|
10
|
+
# This cop targets methods that take identifier (e.g. method name) argument
|
|
11
|
+
# and the following examples are parts of it.
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
#
|
|
15
|
+
# # bad
|
|
16
|
+
# send('do_something')
|
|
17
|
+
# attr_accessor 'do_something'
|
|
18
|
+
# instance_variable_get('@ivar')
|
|
19
|
+
#
|
|
20
|
+
# # good
|
|
21
|
+
# send(:do_something)
|
|
22
|
+
# attr_accessor :do_something
|
|
23
|
+
# instance_variable_get(:@ivar)
|
|
24
|
+
#
|
|
25
|
+
class StringIdentifierArgument < Base
|
|
26
|
+
extend AutoCorrector
|
|
27
|
+
|
|
28
|
+
MSG = 'Use `%<symbol_arg>s` instead of `%<string_arg>s`.'
|
|
29
|
+
|
|
30
|
+
RESTRICT_ON_SEND = %i[
|
|
31
|
+
alias_method attr attr_accessor attr_reader attr_writer autoload autoload?
|
|
32
|
+
class_variable_defined? const_defined? const_get const_set const_source_location
|
|
33
|
+
define_method instance_method method_defined? private_class_method? private_method_defined?
|
|
34
|
+
protected_method_defined? public_class_method public_instance_method public_method_defined?
|
|
35
|
+
remove_class_variable remove_method undef_method class_variable_get class_variable_set
|
|
36
|
+
deprecate_constant module_function private private_constant protected public public_constant
|
|
37
|
+
remove_const ruby2_keywords
|
|
38
|
+
define_singleton_method instance_variable_defined instance_variable_get instance_variable_set
|
|
39
|
+
method public_method public_send remove_instance_variable respond_to? send singleton_method
|
|
40
|
+
__send__
|
|
41
|
+
].freeze
|
|
42
|
+
|
|
43
|
+
def on_send(node)
|
|
44
|
+
return unless (first_argument = node.first_argument)
|
|
45
|
+
return unless first_argument.str_type?
|
|
46
|
+
return if first_argument.value.include?(' ')
|
|
47
|
+
|
|
48
|
+
replacement = first_argument.value.to_sym.inspect
|
|
49
|
+
|
|
50
|
+
message = format(MSG, symbol_arg: replacement, string_arg: first_argument.source)
|
|
51
|
+
|
|
52
|
+
add_offense(first_argument, message: message) do |corrector|
|
|
53
|
+
corrector.replace(first_argument, replacement)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -6,7 +6,8 @@ module RuboCop
|
|
|
6
6
|
# This cop identifies unnecessary use of a regex where
|
|
7
7
|
# `String#include?` would suffice.
|
|
8
8
|
#
|
|
9
|
-
#
|
|
9
|
+
# @safety
|
|
10
|
+
# This cop's offenses are not safe to auto-correct if a receiver is nil.
|
|
10
11
|
#
|
|
11
12
|
# @example
|
|
12
13
|
# # bad
|
|
@@ -6,35 +6,46 @@ module RuboCop
|
|
|
6
6
|
# This cop identifies places where custom code finding the sum of elements
|
|
7
7
|
# in some Enumerable object can be replaced by `Enumerable#sum` method.
|
|
8
8
|
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
# Its auto-correction is marked as safe by default (`SafeAutoCorrect: true`)
|
|
12
|
-
# to prevent `TypeError` in auto-correced code when initial value is not
|
|
13
|
-
# specified as shown below:
|
|
9
|
+
# @safety
|
|
10
|
+
# Auto-corrections are unproblematic wherever an initial value is provided explicitly:
|
|
14
11
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
12
|
+
# [source,ruby]
|
|
13
|
+
# ----
|
|
14
|
+
# [1, 2, 3].reduce(4, :+) # => 10
|
|
15
|
+
# [1, 2, 3].sum(4) # => 10
|
|
19
16
|
#
|
|
20
|
-
#
|
|
17
|
+
# [].reduce(4, :+) # => 4
|
|
18
|
+
# [].sum(4) # => 4
|
|
19
|
+
# ----
|
|
21
20
|
#
|
|
22
|
-
#
|
|
21
|
+
# This also holds true for non-numeric types which implement a `:+` method:
|
|
23
22
|
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
23
|
+
# [source,ruby]
|
|
24
|
+
# ----
|
|
25
|
+
# ['l', 'o'].reduce('Hel', :+) # => "Hello"
|
|
26
|
+
# ['l', 'o'].sum('Hel') # => "Hello"
|
|
27
|
+
# ----
|
|
29
28
|
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
29
|
+
# When no initial value is provided though, `Enumerable#reduce` will pick the first enumerated value
|
|
30
|
+
# as initial value and successively add all following values to it, whereas
|
|
31
|
+
# `Enumerable#sum` will set an initial value of `0` (`Integer`) which can lead to a `TypeError`:
|
|
32
32
|
#
|
|
33
|
-
#
|
|
33
|
+
# [source,ruby]
|
|
34
|
+
# ----
|
|
35
|
+
# [].reduce(:+) # => nil
|
|
36
|
+
# [1, 2, 3].reduce(:+) # => 6
|
|
37
|
+
# ['H', 'e', 'l', 'l', 'o'].reduce(:+) # => "Hello"
|
|
38
|
+
#
|
|
39
|
+
# [].sum # => 0
|
|
40
|
+
# [1, 2, 3].sum # => 6
|
|
41
|
+
# ['H', 'e', 'l', 'l', 'o'].sum # => in `+': String can't be coerced into Integer (TypeError)
|
|
42
|
+
# ----
|
|
43
|
+
#
|
|
44
|
+
# @example OnlySumOrWithInitialValue: false (default)
|
|
34
45
|
# # bad
|
|
35
|
-
# [1, 2, 3].inject(:+) #
|
|
36
|
-
# [1, 2, 3].inject(&:+) # will
|
|
37
|
-
# [1, 2, 3].reduce { |acc, elem| acc + elem } #
|
|
46
|
+
# [1, 2, 3].inject(:+) # Auto-corrections for cases without initial value are unsafe
|
|
47
|
+
# [1, 2, 3].inject(&:+) # and will only be performed when using the `-A` option.
|
|
48
|
+
# [1, 2, 3].reduce { |acc, elem| acc + elem } # They can be prohibited completely using `SafeAutoCorrect: true`.
|
|
38
49
|
# [1, 2, 3].reduce(10, :+)
|
|
39
50
|
# [1, 2, 3].map { |elem| elem ** 2 }.sum
|
|
40
51
|
# [1, 2, 3].collect(&:count).sum(10)
|
|
@@ -45,6 +56,17 @@ module RuboCop
|
|
|
45
56
|
# [1, 2, 3].sum { |elem| elem ** 2 }
|
|
46
57
|
# [1, 2, 3].sum(10, &:count)
|
|
47
58
|
#
|
|
59
|
+
# @example OnlySumOrWithInitialValue: true
|
|
60
|
+
# # bad
|
|
61
|
+
# [1, 2, 3].reduce(10, :+)
|
|
62
|
+
# [1, 2, 3].map { |elem| elem ** 2 }.sum
|
|
63
|
+
# [1, 2, 3].collect(&:count).sum(10)
|
|
64
|
+
#
|
|
65
|
+
# # good
|
|
66
|
+
# [1, 2, 3].sum(10)
|
|
67
|
+
# [1, 2, 3].sum { |elem| elem ** 2 }
|
|
68
|
+
# [1, 2, 3].sum(10, &:count)
|
|
69
|
+
#
|
|
48
70
|
class Sum < Base
|
|
49
71
|
include RangeHelp
|
|
50
72
|
extend AutoCorrector
|
|
@@ -103,6 +125,8 @@ module RuboCop
|
|
|
103
125
|
|
|
104
126
|
def handle_sum_candidate(node)
|
|
105
127
|
sum_candidate?(node) do |method, init, operation|
|
|
128
|
+
next if cop_config['OnlySumOrWithInitialValue'] && init.empty?
|
|
129
|
+
|
|
106
130
|
range = sum_method_range(node)
|
|
107
131
|
message = build_method_message(node, method, init, operation)
|
|
108
132
|
|
|
@@ -156,7 +180,7 @@ module RuboCop
|
|
|
156
180
|
end
|
|
157
181
|
|
|
158
182
|
def sum_method_range(node)
|
|
159
|
-
range_between(node.loc.selector.begin_pos, node.loc.
|
|
183
|
+
range_between(node.loc.selector.begin_pos, node.loc.expression.end_pos)
|
|
160
184
|
end
|
|
161
185
|
|
|
162
186
|
def sum_map_range(map, sum)
|
|
@@ -7,6 +7,18 @@ module RuboCop
|
|
|
7
7
|
# In most cases such calls can be replaced
|
|
8
8
|
# with an explicit array creation.
|
|
9
9
|
#
|
|
10
|
+
# @safety
|
|
11
|
+
# This cop's autocorrection is unsafe because `Integer#times` does nothing if receiver is 0
|
|
12
|
+
# or less. However, `Array.new` raises an error if argument is less than 0.
|
|
13
|
+
#
|
|
14
|
+
# For example:
|
|
15
|
+
#
|
|
16
|
+
# [source,ruby]
|
|
17
|
+
# ----
|
|
18
|
+
# -1.times{} # does nothing
|
|
19
|
+
# Array.new(-1) # ArgumentError: negative array size
|
|
20
|
+
# ----
|
|
21
|
+
#
|
|
10
22
|
# @example
|
|
11
23
|
# # bad
|
|
12
24
|
# 9.times.map do |i|
|
|
@@ -7,11 +7,11 @@ module RuboCop
|
|
|
7
7
|
# literal instead of `String#dup` and `String.new`.
|
|
8
8
|
# Unary plus operator is faster than `String#dup`.
|
|
9
9
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
10
|
+
# @safety
|
|
11
|
+
# This cop's autocorrection is unsafe because `String.new` (without operator) is not
|
|
12
|
+
# exactly the same as `+''`. These differ in encoding. `String.new.encoding` is always
|
|
13
|
+
# `ASCII-8BIT`. However, `(+'').encoding` is the same as script encoding(e.g. `UTF-8`).
|
|
14
|
+
# if you expect `ASCII-8BIT` encoding, disable this cop.
|
|
15
15
|
#
|
|
16
16
|
# @example
|
|
17
17
|
# # bad
|
|
@@ -45,7 +45,10 @@ module RuboCop
|
|
|
45
45
|
return unless dup_string?(node) || string_new?(node)
|
|
46
46
|
|
|
47
47
|
add_offense(node) do |corrector|
|
|
48
|
-
|
|
48
|
+
string_value = "+#{string_value(node)}"
|
|
49
|
+
string_value = "(#{string_value})" if node.parent&.send_type?
|
|
50
|
+
|
|
51
|
+
corrector.replace(node, string_value)
|
|
49
52
|
end
|
|
50
53
|
end
|
|
51
54
|
|
|
@@ -13,6 +13,7 @@ require_relative 'performance/case_when_splat'
|
|
|
13
13
|
require_relative 'performance/casecmp'
|
|
14
14
|
require_relative 'performance/collection_literal_in_loop'
|
|
15
15
|
require_relative 'performance/compare_with_block'
|
|
16
|
+
require_relative 'performance/concurrent_monotonic_time'
|
|
16
17
|
require_relative 'performance/constant_regexp'
|
|
17
18
|
require_relative 'performance/count'
|
|
18
19
|
require_relative 'performance/delete_prefix'
|
|
@@ -43,6 +44,7 @@ require_relative 'performance/size'
|
|
|
43
44
|
require_relative 'performance/sort_reverse'
|
|
44
45
|
require_relative 'performance/squeeze'
|
|
45
46
|
require_relative 'performance/start_with'
|
|
47
|
+
require_relative 'performance/string_identifier_argument'
|
|
46
48
|
require_relative 'performance/string_include'
|
|
47
49
|
require_relative 'performance/string_replacement'
|
|
48
50
|
require_relative 'performance/sum'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubocop-performance
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.13.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bozhidar Batsov
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2021-
|
|
13
|
+
date: 2021-12-25 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: rubocop
|
|
@@ -74,6 +74,7 @@ files:
|
|
|
74
74
|
- lib/rubocop/cop/performance/chain_array_allocation.rb
|
|
75
75
|
- lib/rubocop/cop/performance/collection_literal_in_loop.rb
|
|
76
76
|
- lib/rubocop/cop/performance/compare_with_block.rb
|
|
77
|
+
- lib/rubocop/cop/performance/concurrent_monotonic_time.rb
|
|
77
78
|
- lib/rubocop/cop/performance/constant_regexp.rb
|
|
78
79
|
- lib/rubocop/cop/performance/count.rb
|
|
79
80
|
- lib/rubocop/cop/performance/delete_prefix.rb
|
|
@@ -104,6 +105,7 @@ files:
|
|
|
104
105
|
- lib/rubocop/cop/performance/sort_reverse.rb
|
|
105
106
|
- lib/rubocop/cop/performance/squeeze.rb
|
|
106
107
|
- lib/rubocop/cop/performance/start_with.rb
|
|
108
|
+
- lib/rubocop/cop/performance/string_identifier_argument.rb
|
|
107
109
|
- lib/rubocop/cop/performance/string_include.rb
|
|
108
110
|
- lib/rubocop/cop/performance/string_replacement.rb
|
|
109
111
|
- lib/rubocop/cop/performance/sum.rb
|
|
@@ -121,8 +123,9 @@ metadata:
|
|
|
121
123
|
homepage_uri: https://docs.rubocop.org/rubocop-performance/
|
|
122
124
|
changelog_uri: https://github.com/rubocop/rubocop-performance/blob/master/CHANGELOG.md
|
|
123
125
|
source_code_uri: https://github.com/rubocop/rubocop-performance/
|
|
124
|
-
documentation_uri: https://docs.rubocop.org/rubocop-performance/1.
|
|
126
|
+
documentation_uri: https://docs.rubocop.org/rubocop-performance/1.13/
|
|
125
127
|
bug_tracker_uri: https://github.com/rubocop/rubocop-performance/issues
|
|
128
|
+
rubygems_mfa_required: 'true'
|
|
126
129
|
post_install_message:
|
|
127
130
|
rdoc_options: []
|
|
128
131
|
require_paths:
|
|
@@ -138,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
138
141
|
- !ruby/object:Gem::Version
|
|
139
142
|
version: '0'
|
|
140
143
|
requirements: []
|
|
141
|
-
rubygems_version: 3.
|
|
144
|
+
rubygems_version: 3.3.0
|
|
142
145
|
signing_key:
|
|
143
146
|
specification_version: 4
|
|
144
147
|
summary: Automatic performance checking tool for Ruby code.
|