rubocop-performance 1.5.1 → 1.7.1
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 +78 -6
- 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 +47 -0
- data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +50 -0
- data/lib/rubocop/cop/performance/bind_call.rb +87 -0
- data/lib/rubocop/cop/performance/caller.rb +2 -2
- data/lib/rubocop/cop/performance/casecmp.rb +5 -3
- data/lib/rubocop/cop/performance/chain_array_allocation.rb +1 -1
- data/lib/rubocop/cop/performance/compare_with_block.rb +2 -2
- data/lib/rubocop/cop/performance/count.rb +3 -3
- data/lib/rubocop/cop/performance/delete_prefix.rb +96 -0
- data/lib/rubocop/cop/performance/delete_suffix.rb +96 -0
- data/lib/rubocop/cop/performance/detect.rb +1 -1
- data/lib/rubocop/cop/performance/double_start_end_with.rb +2 -2
- data/lib/rubocop/cop/performance/end_with.rb +30 -12
- data/lib/rubocop/cop/performance/fixed_size.rb +1 -1
- data/lib/rubocop/cop/performance/flat_map.rb +1 -1
- data/lib/rubocop/cop/performance/inefficient_hash_search.rb +1 -1
- data/lib/rubocop/cop/performance/io_readlines.rb +127 -0
- data/lib/rubocop/cop/performance/open_struct.rb +1 -1
- data/lib/rubocop/cop/performance/range_include.rb +10 -8
- data/lib/rubocop/cop/performance/redundant_block_call.rb +3 -3
- data/lib/rubocop/cop/performance/redundant_match.rb +2 -2
- data/lib/rubocop/cop/performance/redundant_merge.rb +20 -7
- data/lib/rubocop/cop/performance/redundant_sort_block.rb +53 -0
- data/lib/rubocop/cop/performance/redundant_string_chars.rb +137 -0
- data/lib/rubocop/cop/performance/regexp_match.rb +13 -13
- data/lib/rubocop/cop/performance/reverse_each.rb +3 -2
- data/lib/rubocop/cop/performance/reverse_first.rb +78 -0
- data/lib/rubocop/cop/performance/size.rb +35 -37
- data/lib/rubocop/cop/performance/sort_reverse.rb +54 -0
- data/lib/rubocop/cop/performance/squeeze.rb +70 -0
- data/lib/rubocop/cop/performance/start_with.rb +30 -15
- data/lib/rubocop/cop/performance/string_include.rb +59 -0
- data/lib/rubocop/cop/performance/string_replacement.rb +4 -11
- data/lib/rubocop/cop/performance/times_map.rb +1 -1
- data/lib/rubocop/cop/performance/unfreeze_string.rb +3 -7
- data/lib/rubocop/cop/performance/uri_default_parser.rb +1 -1
- data/lib/rubocop/cop/performance_cops.rb +15 -0
- data/lib/rubocop/performance/inject.rb +1 -1
- data/lib/rubocop/performance/version.rb +1 -1
- metadata +25 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 34605a937c1a3e040428bee06e322220659c105b6e835423d9e0e73869fd83ee
|
4
|
+
data.tar.gz: e3fa0103bd2518cec45334bc162e144963eef9e3d09ab9fd6bd7b2d35b654e84
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5fe72ab5a0370aa8e0cc434080a27a38d8979084c53c50cd18e8cbc0896572ddc9d011130d56283f9d588268253ec0e2e44fc32b81483e1458689c60c17e835b
|
7
|
+
data.tar.gz: a60d627b0d1ded2fb6103a78fb4414fc1e1f1ff13ab9f4e82c399289c9a40c3c6e0385ee2509074175bd724755f768dd4625bb9791af993032a1f840a0533ef6
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -16,7 +16,7 @@ gem install rubocop-performance
|
|
16
16
|
or if you use bundler put this in your `Gemfile`
|
17
17
|
|
18
18
|
```ruby
|
19
|
-
gem 'rubocop-performance'
|
19
|
+
gem 'rubocop-performance', require: false
|
20
20
|
```
|
21
21
|
|
22
22
|
## Usage
|
@@ -72,6 +72,10 @@ Performance/Size:
|
|
72
72
|
- lib/example.rb
|
73
73
|
```
|
74
74
|
|
75
|
+
## Documentation
|
76
|
+
|
77
|
+
You can read a lot more about RuboCop Performance in its [official docs](https://docs.rubocop.org/rubocop-performance/).
|
78
|
+
|
75
79
|
## Contributing
|
76
80
|
|
77
81
|
Checkout the [contribution guidelines](CONTRIBUTING.md).
|
data/config/default.yml
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
# This is the default configuration file.
|
2
2
|
|
3
|
+
Performance/AncestorsInclude:
|
4
|
+
Description: 'Use `A <= B` instead of `A.ancestors.include?(B)`.'
|
5
|
+
Reference: 'https://github.com/JuanitoFatas/fast-ruby#ancestorsinclude-vs--code'
|
6
|
+
Enabled: 'pending'
|
7
|
+
Safe: false
|
8
|
+
VersionAdded: '1.7'
|
9
|
+
|
10
|
+
Performance/BigDecimalWithNumericArgument:
|
11
|
+
Description: 'Convert numeric argument to string before passing to BigDecimal.'
|
12
|
+
Enabled: 'pending'
|
13
|
+
VersionAdded: '1.7'
|
14
|
+
|
15
|
+
Performance/BindCall:
|
16
|
+
Description: 'Use `bind_call(obj, args, ...)` instead of `bind(obj).call(args, ...)`.'
|
17
|
+
Enabled: true
|
18
|
+
VersionAdded: '1.6'
|
19
|
+
|
3
20
|
Performance/Caller:
|
4
21
|
Description: >-
|
5
22
|
Use `caller(n..n)` instead of `caller`.
|
@@ -21,6 +38,7 @@ Performance/Casecmp:
|
|
21
38
|
Use `casecmp` rather than `downcase ==`, `upcase ==`, `== downcase`, or `== upcase`..
|
22
39
|
Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringcasecmp-vs-stringdowncase---code'
|
23
40
|
Enabled: true
|
41
|
+
Safe: false
|
24
42
|
VersionAdded: '0.36'
|
25
43
|
|
26
44
|
Performance/ChainArrayAllocation:
|
@@ -49,6 +67,18 @@ Performance/Count:
|
|
49
67
|
VersionAdded: '0.31'
|
50
68
|
VersionChanged: '1.5'
|
51
69
|
|
70
|
+
Performance/DeletePrefix:
|
71
|
+
Description: 'Use `delete_prefix` instead of `gsub`.'
|
72
|
+
Enabled: true
|
73
|
+
SafeMultiline: true
|
74
|
+
VersionAdded: '1.6'
|
75
|
+
|
76
|
+
Performance/DeleteSuffix:
|
77
|
+
Description: 'Use `delete_suffix` instead of `gsub`.'
|
78
|
+
Enabled: true
|
79
|
+
SafeMultiline: true
|
80
|
+
VersionAdded: '1.6'
|
81
|
+
|
52
82
|
Performance/Detect:
|
53
83
|
Description: >-
|
54
84
|
Use `detect` instead of `select.first`, `find_all.first`,
|
@@ -83,11 +113,12 @@ Performance/EndWith:
|
|
83
113
|
SafeAutoCorrect: false
|
84
114
|
AutoCorrect: false
|
85
115
|
Enabled: true
|
116
|
+
SafeMultiline: true
|
86
117
|
VersionAdded: '0.36'
|
87
|
-
VersionChanged: '
|
118
|
+
VersionChanged: '1.6'
|
88
119
|
|
89
120
|
Performance/FixedSize:
|
90
|
-
Description: 'Do not compute the size of statically sized objects except in constants'
|
121
|
+
Description: 'Do not compute the size of statically sized objects except in constants.'
|
91
122
|
Enabled: true
|
92
123
|
VersionAdded: '0.35'
|
93
124
|
|
@@ -95,7 +126,7 @@ Performance/FlatMap:
|
|
95
126
|
Description: >-
|
96
127
|
Use `Enumerable#flat_map`
|
97
128
|
instead of `Enumerable#map...Array#flatten(1)`
|
98
|
-
or `Enumberable#collect..Array#flatten(1)
|
129
|
+
or `Enumberable#collect..Array#flatten(1)`.
|
99
130
|
Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code'
|
100
131
|
Enabled: true
|
101
132
|
VersionAdded: '0.30'
|
@@ -106,12 +137,18 @@ Performance/FlatMap:
|
|
106
137
|
# `flatten` without any parameters can flatten multiple levels.
|
107
138
|
|
108
139
|
Performance/InefficientHashSearch:
|
109
|
-
Description: 'Use `key?` or `value?` instead of `keys.include?` or `values.include
|
140
|
+
Description: 'Use `key?` or `value?` instead of `keys.include?` or `values.include?`.'
|
110
141
|
Reference: 'https://github.com/JuanitoFatas/fast-ruby#hashkey-instead-of-hashkeysinclude-code'
|
111
142
|
Enabled: true
|
112
143
|
VersionAdded: '0.56'
|
113
144
|
Safe: false
|
114
145
|
|
146
|
+
Performance/IoReadlines:
|
147
|
+
Description: 'Use `IO.each_line` (`IO#each_line`) instead of `IO.readlines` (`IO#readlines`).'
|
148
|
+
Reference: 'https://docs.gitlab.com/ee/development/performance.html#reading-from-files-and-other-data-sources'
|
149
|
+
Enabled: false
|
150
|
+
VersionAdded: '1.7'
|
151
|
+
|
115
152
|
Performance/OpenStruct:
|
116
153
|
Description: 'Use `Struct` instead of `OpenStruct`.'
|
117
154
|
Enabled: false
|
@@ -119,10 +156,11 @@ Performance/OpenStruct:
|
|
119
156
|
Safe: false
|
120
157
|
|
121
158
|
Performance/RangeInclude:
|
122
|
-
Description: 'Use `Range#cover?` instead of `Range#include
|
159
|
+
Description: 'Use `Range#cover?` instead of `Range#include?` (or `Range#member?`).'
|
123
160
|
Reference: 'https://github.com/JuanitoFatas/fast-ruby#cover-vs-include-code'
|
124
161
|
Enabled: true
|
125
162
|
VersionAdded: '0.36'
|
163
|
+
VersionChanged: '1.7'
|
126
164
|
Safe: false
|
127
165
|
|
128
166
|
Performance/RedundantBlockCall:
|
@@ -146,6 +184,16 @@ Performance/RedundantMerge:
|
|
146
184
|
# Max number of key-value pairs to consider an offense
|
147
185
|
MaxKeyValuePairs: 2
|
148
186
|
|
187
|
+
Performance/RedundantSortBlock:
|
188
|
+
Description: 'Use `sort` instead of `sort { |a, b| a <=> b }`.'
|
189
|
+
Enabled: 'pending'
|
190
|
+
VersionAdded: '1.7'
|
191
|
+
|
192
|
+
Performance/RedundantStringChars:
|
193
|
+
Description: 'Checks for redundant `String#chars`.'
|
194
|
+
Enabled: 'pending'
|
195
|
+
VersionAdded: '1.7'
|
196
|
+
|
149
197
|
Performance/RegexpMatch:
|
150
198
|
Description: >-
|
151
199
|
Use `match?` instead of `Regexp#match`, `String#match`, `Symbol#match`,
|
@@ -160,6 +208,11 @@ Performance/ReverseEach:
|
|
160
208
|
Enabled: true
|
161
209
|
VersionAdded: '0.30'
|
162
210
|
|
211
|
+
Performance/ReverseFirst:
|
212
|
+
Description: 'Use `last(n).reverse` instead of `reverse.first(n)`.'
|
213
|
+
Enabled: 'pending'
|
214
|
+
VersionAdded: '1.7'
|
215
|
+
|
163
216
|
Performance/Size:
|
164
217
|
Description: >-
|
165
218
|
Use `size` instead of `count` for counting
|
@@ -168,6 +221,17 @@ Performance/Size:
|
|
168
221
|
Enabled: true
|
169
222
|
VersionAdded: '0.30'
|
170
223
|
|
224
|
+
Performance/SortReverse:
|
225
|
+
Description: 'Use `sort.reverse` instead of `sort { |a, b| b <=> a }`.'
|
226
|
+
Enabled: 'pending'
|
227
|
+
VersionAdded: '1.7'
|
228
|
+
|
229
|
+
Performance/Squeeze:
|
230
|
+
Description: "Use `squeeze('a')` instead of `gsub(/a+/, 'a')`."
|
231
|
+
Reference: 'https://github.com/JuanitoFatas/fast-ruby#remove-extra-spaces-or-other-contiguous-characters-code'
|
232
|
+
Enabled: 'pending'
|
233
|
+
VersionAdded: '1.7'
|
234
|
+
|
171
235
|
Performance/StartWith:
|
172
236
|
Description: 'Use `start_with?` instead of a regex match anchored to the beginning of a string.'
|
173
237
|
Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end'
|
@@ -177,8 +241,16 @@ Performance/StartWith:
|
|
177
241
|
SafeAutoCorrect: false
|
178
242
|
AutoCorrect: false
|
179
243
|
Enabled: true
|
244
|
+
SafeMultiline: true
|
180
245
|
VersionAdded: '0.36'
|
181
|
-
VersionChanged: '
|
246
|
+
VersionChanged: '1.6'
|
247
|
+
|
248
|
+
Performance/StringInclude:
|
249
|
+
Description: 'Use `String#include?` instead of a regex match with literal-only pattern.'
|
250
|
+
Enabled: 'pending'
|
251
|
+
AutoCorrect: false
|
252
|
+
SafeAutoCorrect: false
|
253
|
+
VersionAdded: '1.7'
|
182
254
|
|
183
255
|
Performance/StringReplacement:
|
184
256
|
Description: >-
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
# Common functionality for handling regexp metacharacters.
|
6
|
+
module RegexpMetacharacter
|
7
|
+
private
|
8
|
+
|
9
|
+
def literal_at_start?(regexp)
|
10
|
+
return true if literal_at_start_with_backslash_a?(regexp)
|
11
|
+
|
12
|
+
!safe_multiline? && literal_at_start_with_caret?(regexp)
|
13
|
+
end
|
14
|
+
|
15
|
+
def literal_at_end?(regexp)
|
16
|
+
return true if literal_at_end_with_backslash_z?(regexp)
|
17
|
+
|
18
|
+
!safe_multiline? && literal_at_end_with_dollar?(regexp)
|
19
|
+
end
|
20
|
+
|
21
|
+
def literal_at_start_with_backslash_a?(regex_str)
|
22
|
+
# is this regexp 'literal' in the sense of only matching literal
|
23
|
+
# chars, rather than using metachars like `.` and `*` and so on?
|
24
|
+
# also, is it anchored at the start of the string?
|
25
|
+
# (tricky: \s, \d, and so on are metacharacters, but other characters
|
26
|
+
# escaped with a slash are just literals. LITERAL_REGEX takes all
|
27
|
+
# that into account.)
|
28
|
+
/\A\\A(?:#{Util::LITERAL_REGEX})+\z/.match?(regex_str)
|
29
|
+
end
|
30
|
+
|
31
|
+
def literal_at_start_with_caret?(regex_str)
|
32
|
+
# is this regexp 'literal' in the sense of only matching literal
|
33
|
+
# chars, rather than using metachars like `.` and `*` and so on?
|
34
|
+
# also, is it anchored at the start of the string?
|
35
|
+
# (tricky: \s, \d, and so on are metacharacters, but other characters
|
36
|
+
# escaped with a slash are just literals. LITERAL_REGEX takes all
|
37
|
+
# that into account.)
|
38
|
+
/\A\^(?:#{Util::LITERAL_REGEX})+\z/.match?(regex_str)
|
39
|
+
end
|
40
|
+
|
41
|
+
def literal_at_end_with_backslash_z?(regex_str)
|
42
|
+
# is this regexp 'literal' in the sense of only matching literal
|
43
|
+
# chars, rather than using metachars like . and * and so on?
|
44
|
+
# also, is it anchored at the end of the string?
|
45
|
+
/\A(?:#{Util::LITERAL_REGEX})+\\z\z/.match?(regex_str)
|
46
|
+
end
|
47
|
+
|
48
|
+
def literal_at_end_with_dollar?(regex_str)
|
49
|
+
# is this regexp 'literal' in the sense of only matching literal
|
50
|
+
# chars, rather than using metachars like . and * and so on?
|
51
|
+
# also, is it anchored at the end of the string?
|
52
|
+
/\A(?:#{Util::LITERAL_REGEX})+\$\z/.match?(regex_str)
|
53
|
+
end
|
54
|
+
|
55
|
+
def drop_start_metacharacter(regexp_string)
|
56
|
+
if regexp_string.start_with?('\\A')
|
57
|
+
regexp_string[2..-1] # drop `\A` anchor
|
58
|
+
else
|
59
|
+
regexp_string[1..-1] # drop `^` anchor
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def drop_end_metacharacter(regexp_string)
|
64
|
+
if regexp_string.end_with?('\\z')
|
65
|
+
regexp_string.chomp('\z') # drop `\z` anchor
|
66
|
+
else
|
67
|
+
regexp_string.chop # drop `$` anchor
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def safe_multiline?
|
72
|
+
cop_config.fetch('SafeMultiline', true)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
# Common functionality for cops checking `Enumerable#sort` blocks.
|
6
|
+
module SortBlock
|
7
|
+
extend NodePattern::Macros
|
8
|
+
include RangeHelp
|
9
|
+
|
10
|
+
def_node_matcher :sort_with_block?, <<~PATTERN
|
11
|
+
(block
|
12
|
+
$(send _ :sort)
|
13
|
+
(args (arg $_a) (arg $_b))
|
14
|
+
$send)
|
15
|
+
PATTERN
|
16
|
+
|
17
|
+
def_node_matcher :replaceable_body?, <<~PATTERN
|
18
|
+
(send (lvar %1) :<=> (lvar %2))
|
19
|
+
PATTERN
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def sort_range(send, node)
|
24
|
+
range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop is used to identify usages of `ancestors.include?` and
|
7
|
+
# change them to use `<=` instead.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# A.ancestors.include?(B)
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# A <= B
|
15
|
+
#
|
16
|
+
class AncestorsInclude < Cop
|
17
|
+
include RangeHelp
|
18
|
+
|
19
|
+
MSG = 'Use `<=` instead of `ancestors.include?`.'
|
20
|
+
|
21
|
+
def_node_matcher :ancestors_include_candidate?, <<~PATTERN
|
22
|
+
(send (send $_subclass :ancestors) :include? $_superclass)
|
23
|
+
PATTERN
|
24
|
+
|
25
|
+
def on_send(node)
|
26
|
+
return unless ancestors_include_candidate?(node)
|
27
|
+
|
28
|
+
location_of_ancestors = node.children[0].loc.selector.begin_pos
|
29
|
+
end_location = node.loc.selector.end_pos
|
30
|
+
range = range_between(location_of_ancestors, end_location)
|
31
|
+
|
32
|
+
add_offense(node, location: range)
|
33
|
+
end
|
34
|
+
|
35
|
+
def autocorrect(node)
|
36
|
+
ancestors_include_candidate?(node) do |subclass, superclass|
|
37
|
+
lambda do |corrector|
|
38
|
+
subclass_source = subclass ? subclass.source : 'self'
|
39
|
+
|
40
|
+
corrector.replace(node, "#{subclass_source} <= #{superclass.source}")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop identifies places where numeric argument to BigDecimal should be
|
7
|
+
# converted to string. Initializing from String is faster
|
8
|
+
# than from Numeric for BigDecimal.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# # bad
|
12
|
+
# BigDecimal(1, 2)
|
13
|
+
# BigDecimal(1.2, 3, exception: true)
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# BigDecimal('1', 2)
|
17
|
+
# BigDecimal('1.2', 3, exception: true)
|
18
|
+
#
|
19
|
+
class BigDecimalWithNumericArgument < Cop
|
20
|
+
MSG = 'Convert numeric argument to string before passing to `BigDecimal`.'
|
21
|
+
|
22
|
+
def_node_matcher :big_decimal_with_numeric_argument?, <<~PATTERN
|
23
|
+
(send nil? :BigDecimal $numeric_type? ...)
|
24
|
+
PATTERN
|
25
|
+
|
26
|
+
def on_send(node)
|
27
|
+
big_decimal_with_numeric_argument?(node) do |numeric|
|
28
|
+
next if numeric.float_type? && specifies_precision?(node)
|
29
|
+
|
30
|
+
add_offense(node, location: numeric.source_range)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def autocorrect(node)
|
35
|
+
big_decimal_with_numeric_argument?(node) do |numeric|
|
36
|
+
lambda do |corrector|
|
37
|
+
corrector.wrap(numeric, "'", "'")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def specifies_precision?(node)
|
45
|
+
node.arguments.size > 1 && !node.arguments[1].hash_type?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# In Ruby 2.7, `UnboundMethod#bind_call` has been added.
|
7
|
+
#
|
8
|
+
# This cop identifies places where `bind(obj).call(args, ...)`
|
9
|
+
# can be replaced by `bind_call(obj, args, ...)`.
|
10
|
+
#
|
11
|
+
# The `bind_call(obj, args, ...)` method is faster than
|
12
|
+
# `bind(obj).call(args, ...)`.
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# # bad
|
16
|
+
# umethod.bind(obj).call(foo, bar)
|
17
|
+
# umethod.bind(obj).(foo, bar)
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# umethod.bind_call(obj, foo, bar)
|
21
|
+
#
|
22
|
+
class BindCall < Cop
|
23
|
+
include RangeHelp
|
24
|
+
extend TargetRubyVersion
|
25
|
+
|
26
|
+
minimum_target_ruby_version 2.7
|
27
|
+
|
28
|
+
MSG = 'Use `bind_call(%<bind_arg>s%<comma>s%<call_args>s)` ' \
|
29
|
+
'instead of `bind(%<bind_arg>s).call(%<call_args>s)`.'
|
30
|
+
|
31
|
+
def_node_matcher :bind_with_call_method?, <<~PATTERN
|
32
|
+
(send
|
33
|
+
$(send
|
34
|
+
(send nil? _) :bind
|
35
|
+
$(...)) :call
|
36
|
+
$...)
|
37
|
+
PATTERN
|
38
|
+
|
39
|
+
def on_send(node)
|
40
|
+
bind_with_call_method?(node) do |receiver, bind_arg, call_args_node|
|
41
|
+
range = correction_range(receiver, node)
|
42
|
+
|
43
|
+
call_args = build_call_args(call_args_node)
|
44
|
+
|
45
|
+
message = message(bind_arg.source, call_args)
|
46
|
+
|
47
|
+
add_offense(node, location: range, message: message)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def autocorrect(node)
|
52
|
+
receiver, bind_arg, call_args_node = bind_with_call_method?(node)
|
53
|
+
|
54
|
+
range = correction_range(receiver, node)
|
55
|
+
|
56
|
+
call_args = build_call_args(call_args_node)
|
57
|
+
call_args = ", #{call_args}" unless call_args.empty?
|
58
|
+
|
59
|
+
replacement_method = "bind_call(#{bind_arg.source}#{call_args})"
|
60
|
+
|
61
|
+
lambda do |corrector|
|
62
|
+
corrector.replace(range, replacement_method)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def message(bind_arg, call_args)
|
69
|
+
comma = call_args.empty? ? '' : ', '
|
70
|
+
|
71
|
+
format(MSG, bind_arg: bind_arg, comma: comma, call_args: call_args)
|
72
|
+
end
|
73
|
+
|
74
|
+
def correction_range(receiver, node)
|
75
|
+
location_of_bind = receiver.loc.selector.begin_pos
|
76
|
+
location_of_call = node.loc.end.end_pos
|
77
|
+
|
78
|
+
range_between(location_of_bind, location_of_call)
|
79
|
+
end
|
80
|
+
|
81
|
+
def build_call_args(call_args_node)
|
82
|
+
call_args_node.map(&:source).join(', ')
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|