rubocop-performance 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/config/default.yml +0 -25
- data/lib/rubocop/cop/performance_cops.rb +0 -4
- data/lib/rubocop/performance/version.rb +1 -1
- metadata +5 -9
- data/lib/rubocop/cop/performance/lstrip_rstrip.rb +0 -46
- data/lib/rubocop/cop/performance/redundant_sort_by.rb +0 -50
- data/lib/rubocop/cop/performance/sample.rb +0 -145
- data/lib/rubocop/cop/performance/unneeded_sort.rb +0 -165
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c816f114aca2b788180d1ee5869c0a56431f9d98ff5c4d02d664ca7f90fd3e6
|
4
|
+
data.tar.gz: 6c7fc284ea2f60d098e53f0b9518b0ca1676e1b4659d0b30e230b044ad089316
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e2b576ba2e2d34fccfd31dbda2f34d85c9e359ed0c34195393fac93d6918070dbd64fb07d3e9a60f3f7074e09a969c5e78caaf15a231d614fc9e3d5100926c64
|
7
|
+
data.tar.gz: 8c9a0db760475847abcd14a41a403c294e3bbbe75a8fa7070b0047ff043a2ea840beff2a550500727aae98ec2262ebaaa2fba57b160aa1aec0b9afc0f54212b1
|
data/README.md
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# RuboCop Performance
|
2
2
|
|
3
|
+
[](https://badge.fury.io/rb/rubocop-performance)
|
3
4
|
[](https://circleci.com/gh/rubocop-hq/rubocop-performance)
|
4
5
|
|
5
6
|
Performance optimization analysis for your projects, as an extension to [RuboCop](https://github.com/rubocop-hq/rubocop).
|
data/config/default.yml
CHANGED
@@ -113,11 +113,6 @@ Performance/InefficientHashSearch:
|
|
113
113
|
VersionAdded: '0.56'
|
114
114
|
Safe: false
|
115
115
|
|
116
|
-
Performance/LstripRstrip:
|
117
|
-
Description: 'Use `strip` instead of `lstrip.rstrip`.'
|
118
|
-
Enabled: true
|
119
|
-
VersionAdded: '0.36'
|
120
|
-
|
121
116
|
Performance/OpenStruct:
|
122
117
|
Description: 'Use `Struct` instead of `OpenStruct`.'
|
123
118
|
Enabled: false
|
@@ -152,11 +147,6 @@ Performance/RedundantMerge:
|
|
152
147
|
# Max number of key-value pairs to consider an offense
|
153
148
|
MaxKeyValuePairs: 2
|
154
149
|
|
155
|
-
Performance/RedundantSortBy:
|
156
|
-
Description: 'Use `sort` instead of `sort_by { |x| x }`.'
|
157
|
-
Enabled: true
|
158
|
-
VersionAdded: '0.36'
|
159
|
-
|
160
150
|
Performance/RegexpMatch:
|
161
151
|
Description: >-
|
162
152
|
Use `match?` instead of `Regexp#match`, `String#match`, `Symbol#match`,
|
@@ -171,14 +161,6 @@ Performance/ReverseEach:
|
|
171
161
|
Enabled: true
|
172
162
|
VersionAdded: '0.30'
|
173
163
|
|
174
|
-
Performance/Sample:
|
175
|
-
Description: >-
|
176
|
-
Use `sample` instead of `shuffle.first`,
|
177
|
-
`shuffle.last`, and `shuffle[Integer]`.
|
178
|
-
Reference: 'https://github.com/JuanitoFatas/fast-ruby#arrayshufflefirst-vs-arraysample-code'
|
179
|
-
Enabled: true
|
180
|
-
VersionAdded: '0.30'
|
181
|
-
|
182
164
|
Performance/Size:
|
183
165
|
Description: >-
|
184
166
|
Use `size` instead of `count` for counting
|
@@ -221,13 +203,6 @@ Performance/UnfreezeString:
|
|
221
203
|
Enabled: true
|
222
204
|
VersionAdded: '0.50'
|
223
205
|
|
224
|
-
Performance/UnneededSort:
|
225
|
-
Description: >-
|
226
|
-
Use `min` instead of `sort.first`,
|
227
|
-
`max_by` instead of `sort_by...last`, etc.
|
228
|
-
Enabled: true
|
229
|
-
VersionAdded: '0.55'
|
230
|
-
|
231
206
|
Performance/UriDefaultParser:
|
232
207
|
Description: 'Use `URI::DEFAULT_PARSER` instead of `URI::Parser.new`.'
|
233
208
|
Enabled: true
|
@@ -19,21 +19,17 @@ require_relative 'performance/end_with'
|
|
19
19
|
require_relative 'performance/fixed_size'
|
20
20
|
require_relative 'performance/flat_map'
|
21
21
|
require_relative 'performance/inefficient_hash_search'
|
22
|
-
require_relative 'performance/lstrip_rstrip'
|
23
22
|
require_relative 'performance/open_struct'
|
24
23
|
require_relative 'performance/range_include'
|
25
24
|
require_relative 'performance/redundant_block_call'
|
26
25
|
require_relative 'performance/redundant_match'
|
27
26
|
require_relative 'performance/redundant_merge'
|
28
|
-
require_relative 'performance/redundant_sort_by'
|
29
27
|
require_relative 'performance/regexp_match'
|
30
28
|
require_relative 'performance/reverse_each'
|
31
|
-
require_relative 'performance/sample'
|
32
29
|
require_relative 'performance/size'
|
33
30
|
require_relative 'performance/start_with'
|
34
31
|
require_relative 'performance/string_replacement'
|
35
32
|
require_relative 'performance/times_map'
|
36
33
|
require_relative 'performance/unfreeze_string'
|
37
|
-
require_relative 'performance/unneeded_sort'
|
38
34
|
require_relative 'performance/uri_default_parser'
|
39
35
|
require_relative 'performance/chain_array_allocation'
|
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.1.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: 2019-
|
13
|
+
date: 2019-04-08 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rubocop
|
@@ -18,14 +18,14 @@ dependencies:
|
|
18
18
|
requirements:
|
19
19
|
- - ">="
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 0.
|
21
|
+
version: 0.67.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
requirements:
|
26
26
|
- - ">="
|
27
27
|
- !ruby/object:Gem::Version
|
28
|
-
version: 0.
|
28
|
+
version: 0.67.0
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: simplecov
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,22 +66,18 @@ files:
|
|
66
66
|
- lib/rubocop/cop/performance/fixed_size.rb
|
67
67
|
- lib/rubocop/cop/performance/flat_map.rb
|
68
68
|
- lib/rubocop/cop/performance/inefficient_hash_search.rb
|
69
|
-
- lib/rubocop/cop/performance/lstrip_rstrip.rb
|
70
69
|
- lib/rubocop/cop/performance/open_struct.rb
|
71
70
|
- lib/rubocop/cop/performance/range_include.rb
|
72
71
|
- lib/rubocop/cop/performance/redundant_block_call.rb
|
73
72
|
- lib/rubocop/cop/performance/redundant_match.rb
|
74
73
|
- lib/rubocop/cop/performance/redundant_merge.rb
|
75
|
-
- lib/rubocop/cop/performance/redundant_sort_by.rb
|
76
74
|
- lib/rubocop/cop/performance/regexp_match.rb
|
77
75
|
- lib/rubocop/cop/performance/reverse_each.rb
|
78
|
-
- lib/rubocop/cop/performance/sample.rb
|
79
76
|
- lib/rubocop/cop/performance/size.rb
|
80
77
|
- lib/rubocop/cop/performance/start_with.rb
|
81
78
|
- lib/rubocop/cop/performance/string_replacement.rb
|
82
79
|
- lib/rubocop/cop/performance/times_map.rb
|
83
80
|
- lib/rubocop/cop/performance/unfreeze_string.rb
|
84
|
-
- lib/rubocop/cop/performance/unneeded_sort.rb
|
85
81
|
- lib/rubocop/cop/performance/uri_default_parser.rb
|
86
82
|
- lib/rubocop/cop/performance_cops.rb
|
87
83
|
- lib/rubocop/performance.rb
|
@@ -104,7 +100,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
104
100
|
requirements:
|
105
101
|
- - ">="
|
106
102
|
- !ruby/object:Gem::Version
|
107
|
-
version: 2.2.
|
103
|
+
version: 2.2.2
|
108
104
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
105
|
requirements:
|
110
106
|
- - ">="
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Performance
|
6
|
-
# This cop identifies places where `lstrip.rstrip` can be replaced by
|
7
|
-
# `strip`.
|
8
|
-
#
|
9
|
-
# @example
|
10
|
-
# # bad
|
11
|
-
# 'abc'.lstrip.rstrip
|
12
|
-
# 'abc'.rstrip.lstrip
|
13
|
-
#
|
14
|
-
# # good
|
15
|
-
# 'abc'.strip
|
16
|
-
class LstripRstrip < Cop
|
17
|
-
include RangeHelp
|
18
|
-
|
19
|
-
MSG = 'Use `strip` instead of `%<methods>s`.'.freeze
|
20
|
-
|
21
|
-
def_node_matcher :lstrip_rstrip, <<-PATTERN
|
22
|
-
{(send $(send _ $:rstrip) $:lstrip)
|
23
|
-
(send $(send _ $:lstrip) $:rstrip)}
|
24
|
-
PATTERN
|
25
|
-
|
26
|
-
def on_send(node)
|
27
|
-
lstrip_rstrip(node) do |first_send, method_one, method_two|
|
28
|
-
range = range_between(first_send.loc.selector.begin_pos,
|
29
|
-
node.source_range.end_pos)
|
30
|
-
add_offense(node,
|
31
|
-
location: range,
|
32
|
-
message: format(MSG,
|
33
|
-
methods: "#{method_one}.#{method_two}"))
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def autocorrect(node)
|
38
|
-
range = range_between(node.receiver.loc.selector.begin_pos,
|
39
|
-
node.source_range.end_pos)
|
40
|
-
|
41
|
-
->(corrector) { corrector.replace(range, 'strip') }
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Performance
|
6
|
-
# This cop identifies places where `sort_by { ... }` can be replaced by
|
7
|
-
# `sort`.
|
8
|
-
#
|
9
|
-
# @example
|
10
|
-
# # bad
|
11
|
-
# array.sort_by { |x| x }
|
12
|
-
# array.sort_by do |var|
|
13
|
-
# var
|
14
|
-
# end
|
15
|
-
#
|
16
|
-
# # good
|
17
|
-
# array.sort
|
18
|
-
class RedundantSortBy < Cop
|
19
|
-
include RangeHelp
|
20
|
-
|
21
|
-
MSG = 'Use `sort` instead of `sort_by { |%<var>s| %<var>s }`.'.freeze
|
22
|
-
|
23
|
-
def_node_matcher :redundant_sort_by, <<-PATTERN
|
24
|
-
(block $(send _ :sort_by) (args (arg $_x)) (lvar _x))
|
25
|
-
PATTERN
|
26
|
-
|
27
|
-
def on_block(node)
|
28
|
-
redundant_sort_by(node) do |send, var_name|
|
29
|
-
range = sort_by_range(send, node)
|
30
|
-
|
31
|
-
add_offense(node,
|
32
|
-
location: range,
|
33
|
-
message: format(MSG, var: var_name))
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def autocorrect(node)
|
38
|
-
send, = *node
|
39
|
-
->(corrector) { corrector.replace(sort_by_range(send, node), 'sort') }
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def sort_by_range(send, node)
|
45
|
-
range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
@@ -1,145 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Performance
|
6
|
-
# This cop is used to identify usages of `shuffle.first`,
|
7
|
-
# `shuffle.last`, and `shuffle[]` and change them to use
|
8
|
-
# `sample` instead.
|
9
|
-
#
|
10
|
-
# @example
|
11
|
-
# # bad
|
12
|
-
# [1, 2, 3].shuffle.first
|
13
|
-
# [1, 2, 3].shuffle.first(2)
|
14
|
-
# [1, 2, 3].shuffle.last
|
15
|
-
# [2, 1, 3].shuffle.at(0)
|
16
|
-
# [2, 1, 3].shuffle.slice(0)
|
17
|
-
# [1, 2, 3].shuffle[2]
|
18
|
-
# [1, 2, 3].shuffle[0, 2] # sample(2) will do the same
|
19
|
-
# [1, 2, 3].shuffle[0..2] # sample(3) will do the same
|
20
|
-
# [1, 2, 3].shuffle(random: Random.new).first
|
21
|
-
#
|
22
|
-
# # good
|
23
|
-
# [1, 2, 3].shuffle
|
24
|
-
# [1, 2, 3].sample
|
25
|
-
# [1, 2, 3].sample(3)
|
26
|
-
# [1, 2, 3].shuffle[1, 3] # sample(3) might return a longer Array
|
27
|
-
# [1, 2, 3].shuffle[1..3] # sample(3) might return a longer Array
|
28
|
-
# [1, 2, 3].shuffle[foo, bar]
|
29
|
-
# [1, 2, 3].shuffle(random: Random.new)
|
30
|
-
class Sample < Cop
|
31
|
-
MSG = 'Use `%<correct>s` instead of `%<incorrect>s`.'.freeze
|
32
|
-
|
33
|
-
def_node_matcher :sample_candidate?, <<-PATTERN
|
34
|
-
(send $(send _ :shuffle $...) ${:first :last :[] :at :slice} $...)
|
35
|
-
PATTERN
|
36
|
-
|
37
|
-
def on_send(node)
|
38
|
-
sample_candidate?(node) do |shuffle, shuffle_arg, method, method_args|
|
39
|
-
return unless offensive?(method, method_args)
|
40
|
-
|
41
|
-
range = source_range(shuffle, node)
|
42
|
-
message = message(shuffle_arg, method, method_args, range)
|
43
|
-
add_offense(node, location: range, message: message)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def autocorrect(node)
|
48
|
-
shuffle_node, shuffle_arg, method, method_args =
|
49
|
-
sample_candidate?(node)
|
50
|
-
|
51
|
-
lambda do |corrector|
|
52
|
-
corrector.replace(source_range(shuffle_node, node),
|
53
|
-
correction(shuffle_arg, method, method_args))
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
def offensive?(method, method_args)
|
60
|
-
case method
|
61
|
-
when :first, :last
|
62
|
-
true
|
63
|
-
when :[], :at, :slice
|
64
|
-
sample_size(method_args) != :unknown
|
65
|
-
else
|
66
|
-
false
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def sample_size(method_args)
|
71
|
-
case method_args.size
|
72
|
-
when 1
|
73
|
-
sample_size_for_one_arg(method_args.first)
|
74
|
-
when 2
|
75
|
-
sample_size_for_two_args(*method_args)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def sample_size_for_one_arg(arg)
|
80
|
-
case arg.type
|
81
|
-
when :erange, :irange
|
82
|
-
range_size(arg)
|
83
|
-
when :int
|
84
|
-
[0, -1].include?(arg.to_a.first) ? nil : :unknown
|
85
|
-
else
|
86
|
-
:unknown
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def sample_size_for_two_args(first, second)
|
91
|
-
return :unknown unless first.int_type? && first.to_a.first.zero?
|
92
|
-
|
93
|
-
second.int_type? ? second.to_a.first : :unknown
|
94
|
-
end
|
95
|
-
|
96
|
-
def range_size(range_node)
|
97
|
-
vals = range_node.to_a
|
98
|
-
return :unknown unless vals.all?(&:int_type?)
|
99
|
-
|
100
|
-
low, high = vals.map { |val| val.children[0] }
|
101
|
-
return :unknown unless low.zero? && high >= 0
|
102
|
-
|
103
|
-
case range_node.type
|
104
|
-
when :erange
|
105
|
-
(low...high).size
|
106
|
-
when :irange
|
107
|
-
(low..high).size
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
def source_range(shuffle_node, node)
|
112
|
-
Parser::Source::Range.new(shuffle_node.source_range.source_buffer,
|
113
|
-
shuffle_node.loc.selector.begin_pos,
|
114
|
-
node.source_range.end_pos)
|
115
|
-
end
|
116
|
-
|
117
|
-
def message(shuffle_arg, method, method_args, range)
|
118
|
-
format(MSG,
|
119
|
-
correct: correction(shuffle_arg, method, method_args),
|
120
|
-
incorrect: range.source)
|
121
|
-
end
|
122
|
-
|
123
|
-
def correction(shuffle_arg, method, method_args)
|
124
|
-
shuffle_arg = extract_source(shuffle_arg)
|
125
|
-
sample_arg = sample_arg(method, method_args)
|
126
|
-
args = [sample_arg, shuffle_arg].compact.join(', ')
|
127
|
-
args.empty? ? 'sample' : "sample(#{args})"
|
128
|
-
end
|
129
|
-
|
130
|
-
def sample_arg(method, method_args)
|
131
|
-
case method
|
132
|
-
when :first, :last
|
133
|
-
extract_source(method_args)
|
134
|
-
when :[], :slice
|
135
|
-
sample_size(method_args)
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
def extract_source(args)
|
140
|
-
args.empty? ? nil : args.first.source
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
@@ -1,165 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module RuboCop
|
4
|
-
module Cop
|
5
|
-
module Performance
|
6
|
-
# This cop is used to identify instances of sorting and then
|
7
|
-
# taking only the first or last element. The same behavior can
|
8
|
-
# be accomplished without a relatively expensive sort by using
|
9
|
-
# `Enumerable#min` instead of sorting and taking the first
|
10
|
-
# element and `Enumerable#max` instead of sorting and taking the
|
11
|
-
# last element. Similarly, `Enumerable#min_by` and
|
12
|
-
# `Enumerable#max_by` can replace `Enumerable#sort_by` calls
|
13
|
-
# after which only the first or last element is used.
|
14
|
-
#
|
15
|
-
# @example
|
16
|
-
# # bad
|
17
|
-
# [2, 1, 3].sort.first
|
18
|
-
# [2, 1, 3].sort[0]
|
19
|
-
# [2, 1, 3].sort.at(0)
|
20
|
-
# [2, 1, 3].sort.slice(0)
|
21
|
-
#
|
22
|
-
# # good
|
23
|
-
# [2, 1, 3].min
|
24
|
-
#
|
25
|
-
# # bad
|
26
|
-
# [2, 1, 3].sort.last
|
27
|
-
# [2, 1, 3].sort[-1]
|
28
|
-
# [2, 1, 3].sort.at(-1)
|
29
|
-
# [2, 1, 3].sort.slice(-1)
|
30
|
-
#
|
31
|
-
# # good
|
32
|
-
# [2, 1, 3].max
|
33
|
-
#
|
34
|
-
# # bad
|
35
|
-
# arr.sort_by(&:foo).first
|
36
|
-
# arr.sort_by(&:foo)[0]
|
37
|
-
# arr.sort_by(&:foo).at(0)
|
38
|
-
# arr.sort_by(&:foo).slice(0)
|
39
|
-
#
|
40
|
-
# # good
|
41
|
-
# arr.min_by(&:foo)
|
42
|
-
#
|
43
|
-
# # bad
|
44
|
-
# arr.sort_by(&:foo).last
|
45
|
-
# arr.sort_by(&:foo)[-1]
|
46
|
-
# arr.sort_by(&:foo).at(-1)
|
47
|
-
# arr.sort_by(&:foo).slice(-1)
|
48
|
-
#
|
49
|
-
# # good
|
50
|
-
# arr.max_by(&:foo)
|
51
|
-
#
|
52
|
-
class UnneededSort < Cop
|
53
|
-
include RangeHelp
|
54
|
-
|
55
|
-
MSG = 'Use `%<suggestion>s` instead of '\
|
56
|
-
'`%<sorter>s...%<accessor_source>s`.'.freeze
|
57
|
-
|
58
|
-
def_node_matcher :unneeded_sort?, <<-MATCHER
|
59
|
-
{
|
60
|
-
(send $(send _ $:sort ...) ${:last :first})
|
61
|
-
(send $(send _ $:sort ...) ${:[] :at :slice} {(int 0) (int -1)})
|
62
|
-
|
63
|
-
(send $(send _ $:sort_by _) ${:last :first})
|
64
|
-
(send $(send _ $:sort_by _) ${:[] :at :slice} {(int 0) (int -1)})
|
65
|
-
|
66
|
-
(send (block $(send _ ${:sort_by :sort}) ...) ${:last :first})
|
67
|
-
(send
|
68
|
-
(block $(send _ ${:sort_by :sort}) ...)
|
69
|
-
${:[] :at :slice} {(int 0) (int -1)}
|
70
|
-
)
|
71
|
-
}
|
72
|
-
MATCHER
|
73
|
-
|
74
|
-
def on_send(node)
|
75
|
-
unneeded_sort?(node) do |sort_node, sorter, accessor|
|
76
|
-
range = range_between(
|
77
|
-
sort_node.loc.selector.begin_pos,
|
78
|
-
node.loc.expression.end_pos
|
79
|
-
)
|
80
|
-
|
81
|
-
add_offense(node,
|
82
|
-
location: range,
|
83
|
-
message: message(node,
|
84
|
-
sorter,
|
85
|
-
accessor))
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def autocorrect(node)
|
90
|
-
sort_node, sorter, accessor = unneeded_sort?(node)
|
91
|
-
|
92
|
-
lambda do |corrector|
|
93
|
-
# Remove accessor, e.g. `first` or `[-1]`.
|
94
|
-
corrector.remove(
|
95
|
-
range_between(
|
96
|
-
accessor_start(node),
|
97
|
-
node.loc.expression.end_pos
|
98
|
-
)
|
99
|
-
)
|
100
|
-
|
101
|
-
# Replace "sort" or "sort_by" with the appropriate min/max method.
|
102
|
-
corrector.replace(
|
103
|
-
sort_node.loc.selector,
|
104
|
-
suggestion(sorter, accessor, arg_value(node))
|
105
|
-
)
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
private
|
110
|
-
|
111
|
-
def message(node, sorter, accessor)
|
112
|
-
accessor_source = range_between(
|
113
|
-
node.loc.selector.begin_pos,
|
114
|
-
node.loc.expression.end_pos
|
115
|
-
).source
|
116
|
-
|
117
|
-
format(MSG,
|
118
|
-
suggestion: suggestion(sorter,
|
119
|
-
accessor,
|
120
|
-
arg_value(node)),
|
121
|
-
sorter: sorter,
|
122
|
-
accessor_source: accessor_source)
|
123
|
-
end
|
124
|
-
|
125
|
-
def suggestion(sorter, accessor, arg)
|
126
|
-
base(accessor, arg) + suffix(sorter)
|
127
|
-
end
|
128
|
-
|
129
|
-
def base(accessor, arg)
|
130
|
-
if accessor == :first || (arg && arg.zero?)
|
131
|
-
'min'
|
132
|
-
elsif accessor == :last || arg == -1
|
133
|
-
'max'
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def suffix(sorter)
|
138
|
-
if sorter == :sort
|
139
|
-
''
|
140
|
-
elsif sorter == :sort_by
|
141
|
-
'_by'
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
def arg_node(node)
|
146
|
-
node.arguments.first
|
147
|
-
end
|
148
|
-
|
149
|
-
def arg_value(node)
|
150
|
-
arg_node(node).nil? ? nil : arg_node(node).node_parts.first
|
151
|
-
end
|
152
|
-
|
153
|
-
# This gets the start of the accessor whether it has a dot
|
154
|
-
# (e.g. `.first`) or doesn't (e.g. `[0]`)
|
155
|
-
def accessor_start(node)
|
156
|
-
if node.loc.dot
|
157
|
-
node.loc.dot.begin_pos
|
158
|
-
else
|
159
|
-
node.loc.selector.begin_pos
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|