rubocop-performance 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/rubocop-performance.svg)](https://badge.fury.io/rb/rubocop-performance)
|
3
4
|
[![CircleCI](https://circleci.com/gh/rubocop-hq/rubocop-performance.svg?style=svg)](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
|