rubocop-performance 0.0.0 → 0.0.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/config/default.yml +24 -0
- data/lib/rubocop/cop/performance/case_when_splat.rb +11 -7
- data/lib/rubocop/cop/performance/casecmp.rb +33 -42
- data/lib/rubocop/cop/performance/chain_array_allocation.rb +77 -0
- data/lib/rubocop/cop/performance/compare_with_block.rb +3 -0
- data/lib/rubocop/cop/performance/regexp_match.rb +2 -1
- data/lib/rubocop/cop/performance/sample.rb +5 -2
- data/lib/rubocop/cop/performance/size.rb +8 -2
- data/lib/rubocop/cop/performance/start_with.rb +1 -1
- data/lib/rubocop/cop/performance/string_replacement.rb +2 -1
- data/lib/rubocop/cop/performance_cops.rb +1 -0
- data/lib/rubocop/performance/version.rb +1 -1
- metadata +10 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 43633d075fac26295caf28e79d71f56cdbacd6d7853aead53b3ca35e4f29f99b
|
4
|
+
data.tar.gz: 60886d6ea540f6ed25014424376b2e0cf0f489a2291e298f1f4cbf6084f3e580
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b7b30f444965b49aa1597f9f1e483802b3c2f2f786c71fd577f6af18dbdf268e0100793982aa2d8979b86c7a9f302b0b211694e5f8e04c6b2eeaae85c7dd0591
|
7
|
+
data.tar.gz: ac1707a5f94ae008d1baf8c90b2ff269a6bfb3da8e27ed4adb1fa968b69e6dfdbc7ae8a074d18c4fa5d6e7c6b3d5de814f5b5dfbd38832338e80d107b854ad33
|
data/config/default.yml
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# This is the default configuration file.
|
2
|
+
|
3
|
+
Performance/CaseWhenSplat:
|
4
|
+
Description: >-
|
5
|
+
Reordering `when` conditions with a splat to the end
|
6
|
+
of the `when` branches can improve performance.
|
7
|
+
Enabled: false
|
8
|
+
AutoCorrect: false
|
9
|
+
|
10
|
+
Performance/ChainArrayAllocation:
|
11
|
+
Description: >-
|
12
|
+
Instead of chaining array methods that allocate new arrays, mutate an
|
13
|
+
existing array.
|
14
|
+
Reference: 'https://twitter.com/schneems/status/1034123879978029057'
|
15
|
+
Enabled: false
|
16
|
+
|
17
|
+
Performance/DoubleStartEndWith:
|
18
|
+
# Used to check for `starts_with?` and `ends_with?`.
|
19
|
+
# These methods are defined by `ActiveSupport`.
|
20
|
+
IncludeActiveSupportAliases: false
|
21
|
+
|
22
|
+
Performance/RedundantMerge:
|
23
|
+
# Max number of key-value pairs to consider an offense
|
24
|
+
MaxKeyValuePairs: 2
|
@@ -3,16 +3,19 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Performance
|
6
|
-
#
|
7
|
-
# of the
|
6
|
+
# Reordering `when` conditions with a splat to the end
|
7
|
+
# of the `when` branches can improve performance.
|
8
8
|
#
|
9
9
|
# Ruby has to allocate memory for the splat expansion every time
|
10
10
|
# that the `case` `when` statement is run. Since Ruby does not support
|
11
11
|
# fall through inside of `case` `when`, like some other languages do,
|
12
|
-
# the order of the `when` branches
|
12
|
+
# the order of the `when` branches should not matter. By placing any
|
13
13
|
# splat expansions at the end of the list of `when` branches we will
|
14
14
|
# reduce the number of times that memory has to be allocated for
|
15
|
-
# the expansion.
|
15
|
+
# the expansion. The exception to this is if multiple of your `when`
|
16
|
+
# conditions can be true for any given condition. A likely scenario for
|
17
|
+
# this defining a higher level when condition to override a condition
|
18
|
+
# that is inside of the splat expansion.
|
16
19
|
#
|
17
20
|
# This is not a guaranteed performance improvement. If the data being
|
18
21
|
# processed by the `case` condition is normalized in a manner that favors
|
@@ -54,9 +57,10 @@ module RuboCop
|
|
54
57
|
include Alignment
|
55
58
|
include RangeHelp
|
56
59
|
|
57
|
-
MSG = '
|
58
|
-
|
59
|
-
ARRAY_MSG = '
|
60
|
+
MSG = 'Reordering `when` conditions with a splat to the end ' \
|
61
|
+
'of the `when` branches can improve performance.'.freeze
|
62
|
+
ARRAY_MSG = 'Pass the contents of array literals ' \
|
63
|
+
'directly to `when` conditions.'.freeze
|
60
64
|
|
61
65
|
def on_case(case_node)
|
62
66
|
when_conditions = case_node.when_branches.flat_map(&:conditions)
|
@@ -18,7 +18,7 @@ module RuboCop
|
|
18
18
|
# str.casecmp('ABC').zero?
|
19
19
|
# 'abc'.casecmp(str).zero?
|
20
20
|
class Casecmp < Cop
|
21
|
-
MSG = 'Use `
|
21
|
+
MSG = 'Use `%<good>s` instead of `%<bad>s`.'.freeze
|
22
22
|
CASE_METHODS = %i[downcase upcase].freeze
|
23
23
|
|
24
24
|
def_node_matcher :downcase_eq, <<-PATTERN
|
@@ -43,16 +43,29 @@ module RuboCop
|
|
43
43
|
PATTERN
|
44
44
|
|
45
45
|
def on_send(node)
|
46
|
-
return
|
46
|
+
return unless downcase_eq(node) || eq_downcase(node)
|
47
|
+
return unless (parts = take_method_apart(node))
|
47
48
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
_, _, arg, variable = parts
|
50
|
+
good_method = build_good_method(arg, variable)
|
51
|
+
|
52
|
+
add_offense(
|
53
|
+
node,
|
54
|
+
message: format(MSG, good: good_method, bad: node.source)
|
55
|
+
)
|
53
56
|
end
|
54
57
|
|
55
58
|
def autocorrect(node)
|
59
|
+
return unless (parts = take_method_apart(node))
|
60
|
+
|
61
|
+
receiver, method, arg, variable = parts
|
62
|
+
|
63
|
+
correction(node, receiver, method, arg, variable)
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def take_method_apart(node)
|
56
69
|
if downcase_downcase(node)
|
57
70
|
receiver, method, rhs = *node
|
58
71
|
arg, = *rhs
|
@@ -65,52 +78,30 @@ module RuboCop
|
|
65
78
|
end
|
66
79
|
|
67
80
|
variable, = *receiver
|
68
|
-
correction(node, receiver, method, arg, variable)
|
69
|
-
end
|
70
|
-
|
71
|
-
private
|
72
|
-
|
73
|
-
def inefficient_comparison(node)
|
74
|
-
loc = node.loc
|
75
81
|
|
76
|
-
|
77
|
-
*_, method = *other
|
78
|
-
range, is_other_part = downcase_eq_range(method, loc, send_downcase)
|
79
|
-
|
80
|
-
yield range, is_other_part, case_method, eq_method
|
81
|
-
return
|
82
|
-
end
|
83
|
-
|
84
|
-
eq_downcase(node) do |eq_method, send_downcase, case_method|
|
85
|
-
range = loc.selector.join(send_downcase.loc.selector)
|
86
|
-
yield range, false, eq_method, case_method
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def downcase_eq_range(method, loc, send_downcase)
|
91
|
-
if CASE_METHODS.include?(method)
|
92
|
-
[loc.expression, true]
|
93
|
-
else
|
94
|
-
[loc.selector.join(send_downcase.loc.selector), false]
|
95
|
-
end
|
82
|
+
[receiver, method, arg, variable]
|
96
83
|
end
|
97
84
|
|
98
85
|
def correction(node, _receiver, method, arg, variable)
|
99
86
|
lambda do |corrector|
|
100
87
|
corrector.insert_before(node.loc.expression, '!') if method == :!=
|
101
88
|
|
102
|
-
|
103
|
-
# if arg already includes one or more sets of parens, don't add more
|
104
|
-
# or if method call already used parens, again, don't add more
|
105
|
-
replacement = if arg.send_type? || !parentheses?(arg)
|
106
|
-
"#{variable.source}.casecmp(#{arg.source}).zero?"
|
107
|
-
else
|
108
|
-
"#{variable.source}.casecmp#{arg.source}.zero?"
|
109
|
-
end
|
89
|
+
replacement = build_good_method(arg, variable)
|
110
90
|
|
111
91
|
corrector.replace(node.loc.expression, replacement)
|
112
92
|
end
|
113
93
|
end
|
94
|
+
|
95
|
+
def build_good_method(arg, variable)
|
96
|
+
# We want resulting call to be parenthesized
|
97
|
+
# if arg already includes one or more sets of parens, don't add more
|
98
|
+
# or if method call already used parens, again, don't add more
|
99
|
+
if arg.send_type? || !parentheses?(arg)
|
100
|
+
"#{variable.source}.casecmp(#{arg.source}).zero?"
|
101
|
+
else
|
102
|
+
"#{variable.source}.casecmp#{arg.source}.zero?"
|
103
|
+
end
|
104
|
+
end
|
114
105
|
end
|
115
106
|
end
|
116
107
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop is used to identify usages of
|
7
|
+
# @example
|
8
|
+
# # bad
|
9
|
+
# array = ["a", "b", "c"]
|
10
|
+
# array.compact.flatten.map { |x| x.downcase }
|
11
|
+
#
|
12
|
+
# Each of these methods (`compact`, `flatten`, `map`) will generate a
|
13
|
+
# new intermediate array that is promptly thrown away. Instead it is
|
14
|
+
# faster to mutate when we know it's safe.
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# # good.
|
18
|
+
# array = ["a", "b", "c"]
|
19
|
+
# array.compact!
|
20
|
+
# array.flatten!
|
21
|
+
# array.map! { |x| x.downcase }
|
22
|
+
# array
|
23
|
+
class ChainArrayAllocation < Cop
|
24
|
+
include RangeHelp
|
25
|
+
|
26
|
+
# These methods return a new array but only sometimes. They must be
|
27
|
+
# called with an argument. For example:
|
28
|
+
#
|
29
|
+
# [1,2].first # => 1
|
30
|
+
# [1,2].first(1) # => [1]
|
31
|
+
#
|
32
|
+
RETURN_NEW_ARRAY_WHEN_ARGS = ':first :last :pop :sample :shift '.freeze
|
33
|
+
|
34
|
+
# These methods return a new array only when called without a block.
|
35
|
+
RETURNS_NEW_ARRAY_WHEN_NO_BLOCK = ':zip :product '.freeze
|
36
|
+
|
37
|
+
# These methods ALWAYS return a new array
|
38
|
+
# after they're called it's safe to mutate the the resulting array
|
39
|
+
ALWAYS_RETURNS_NEW_ARRAY = ':* :+ :- :collect :compact :drop '\
|
40
|
+
':drop_while :flatten :map :reject ' \
|
41
|
+
':reverse :rotate :select :shuffle :sort ' \
|
42
|
+
':take :take_while :transpose :uniq ' \
|
43
|
+
':values_at :| '.freeze
|
44
|
+
|
45
|
+
# These methods have a mutation alternative. For example :collect
|
46
|
+
# can be called as :collect!
|
47
|
+
HAS_MUTATION_ALTERNATIVE = ':collect :compact :flatten :map :reject '\
|
48
|
+
':reverse :rotate :select :shuffle :sort '\
|
49
|
+
':uniq '.freeze
|
50
|
+
MSG = 'Use `%<method>s...%<second_method>s!` instead of `%<method>s' \
|
51
|
+
'...%<second_method>s`.'.freeze
|
52
|
+
|
53
|
+
def_node_matcher :flat_map_candidate?, <<-PATTERN
|
54
|
+
{
|
55
|
+
(send (send _ ${#{RETURN_NEW_ARRAY_WHEN_ARGS}} {int lvar ivar cvar gvar}) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
|
56
|
+
(send (block (send _ ${#{ALWAYS_RETURNS_NEW_ARRAY} }) ...) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
|
57
|
+
(send (send _ ${#{ALWAYS_RETURNS_NEW_ARRAY + RETURNS_NEW_ARRAY_WHEN_NO_BLOCK}} ...) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
|
58
|
+
}
|
59
|
+
PATTERN
|
60
|
+
|
61
|
+
def on_send(node)
|
62
|
+
flat_map_candidate?(node) do |fm, sm, _|
|
63
|
+
range = range_between(
|
64
|
+
node.loc.dot.begin_pos,
|
65
|
+
node.source_range.end_pos
|
66
|
+
)
|
67
|
+
add_offense(
|
68
|
+
node,
|
69
|
+
location: range,
|
70
|
+
message: format(MSG, method: fm, second_method: sm)
|
71
|
+
)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -48,6 +48,7 @@ module RuboCop
|
|
48
48
|
compare?(node) do |send, var_a, var_b, body|
|
49
49
|
replaceable_body?(body, var_a, var_b) do |method, args_a, args_b|
|
50
50
|
return unless slow_compare?(method, args_a, args_b)
|
51
|
+
|
51
52
|
range = compare_range(send, node)
|
52
53
|
|
53
54
|
add_offense(
|
@@ -78,8 +79,10 @@ module RuboCop
|
|
78
79
|
|
79
80
|
def slow_compare?(method, args_a, args_b)
|
80
81
|
return false unless args_a == args_b
|
82
|
+
|
81
83
|
if method == :[]
|
82
84
|
return false unless args_a.size == 1
|
85
|
+
|
83
86
|
key = args_a.first
|
84
87
|
return false unless %i[sym str int].include?(key.type)
|
85
88
|
else
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Performance
|
6
|
-
# In Ruby 2.4, `String#match?`, `Regexp#match
|
6
|
+
# In Ruby 2.4, `String#match?`, `Regexp#match?`, and `Symbol#match?`
|
7
7
|
# have been added. The methods are faster than `match`.
|
8
8
|
# Because the methods avoid creating a `MatchData` object or saving
|
9
9
|
# backref.
|
@@ -101,6 +101,7 @@ module RuboCop
|
|
101
101
|
|
102
102
|
def match_with_lvasgn?(node)
|
103
103
|
return false unless node.match_with_lvasgn_type?
|
104
|
+
|
104
105
|
regexp, _rhs = *node
|
105
106
|
regexp.to_regexp.named_captures.empty?
|
106
107
|
end
|
@@ -3,8 +3,9 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Performance
|
6
|
-
# This cop is used to identify usages of `shuffle.first`,
|
7
|
-
# and `shuffle[]` and change them to use
|
6
|
+
# This cop is used to identify usages of `shuffle.first`,
|
7
|
+
# `shuffle.last`, and `shuffle[]` and change them to use
|
8
|
+
# `sample` instead.
|
8
9
|
#
|
9
10
|
# @example
|
10
11
|
# # bad
|
@@ -88,12 +89,14 @@ module RuboCop
|
|
88
89
|
|
89
90
|
def sample_size_for_two_args(first, second)
|
90
91
|
return :unknown unless first.int_type? && first.to_a.first.zero?
|
92
|
+
|
91
93
|
second.int_type? ? second.to_a.first : :unknown
|
92
94
|
end
|
93
95
|
|
94
96
|
def range_size(range_node)
|
95
97
|
vals = range_node.to_a
|
96
98
|
return :unknown unless vals.all?(&:int_type?)
|
99
|
+
|
97
100
|
low, high = vals.map { |val| val.children[0] }
|
98
101
|
return :unknown unless low.zero? && high >= 0
|
99
102
|
|
@@ -55,15 +55,21 @@ module RuboCop
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def array?(node)
|
58
|
+
return true if node.array_type?
|
59
|
+
return false unless node.send_type?
|
60
|
+
|
58
61
|
_, constant = *node.receiver
|
59
62
|
|
60
|
-
|
63
|
+
constant == :Array || node.method_name == :to_a
|
61
64
|
end
|
62
65
|
|
63
66
|
def hash?(node)
|
67
|
+
return true if node.hash_type?
|
68
|
+
return false unless node.send_type?
|
69
|
+
|
64
70
|
_, constant = *node.receiver
|
65
71
|
|
66
|
-
|
72
|
+
constant == :Hash || node.method_name == :to_h
|
67
73
|
end
|
68
74
|
end
|
69
75
|
end
|
@@ -26,7 +26,7 @@ module RuboCop
|
|
26
26
|
|
27
27
|
def literal_at_start?(regex_str)
|
28
28
|
# is this regexp 'literal' in the sense of only matching literal
|
29
|
-
# chars, rather than using metachars like
|
29
|
+
# chars, rather than using metachars like `.` and `*` and so on?
|
30
30
|
# also, is it anchored at the start of the string?
|
31
31
|
# (tricky: \s, \d, and so on are metacharacters, but other characters
|
32
32
|
# escaped with a slash are just literals. LITERAL_REGEX takes all
|
@@ -22,7 +22,7 @@ module RuboCop
|
|
22
22
|
include RangeHelp
|
23
23
|
|
24
24
|
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'.freeze
|
25
|
-
DETERMINISTIC_REGEX = /\A(?:#{LITERAL_REGEX})+\Z
|
25
|
+
DETERMINISTIC_REGEX = /\A(?:#{LITERAL_REGEX})+\Z/.freeze
|
26
26
|
DELETE = 'delete'.freeze
|
27
27
|
TR = 'tr'.freeze
|
28
28
|
BANG = '!'.freeze
|
@@ -87,6 +87,7 @@ module RuboCop
|
|
87
87
|
unless first_param.str_type?
|
88
88
|
return true if options
|
89
89
|
return true unless first_source =~ DETERMINISTIC_REGEX
|
90
|
+
|
90
91
|
# This must be done after checking DETERMINISTIC_REGEX
|
91
92
|
# Otherwise things like \s will trip us up
|
92
93
|
first_source = interpret_string_escapes(first_source)
|
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: 0.0.
|
4
|
+
version: 0.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bozhidar Batsov
|
@@ -10,22 +10,22 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2018-
|
13
|
+
date: 2018-10-31 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rubocop
|
17
17
|
requirement: !ruby/object:Gem::Requirement
|
18
18
|
requirements:
|
19
|
-
- - "
|
19
|
+
- - ">="
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
21
|
+
version: 0.58.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:
|
28
|
+
version: 0.58.0
|
29
29
|
- !ruby/object:Gem::Dependency
|
30
30
|
name: simplecov
|
31
31
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,10 +52,12 @@ extra_rdoc_files:
|
|
52
52
|
files:
|
53
53
|
- LICENSE.txt
|
54
54
|
- README.md
|
55
|
+
- config/default.yml
|
55
56
|
- lib/rubocop-performance.rb
|
56
57
|
- lib/rubocop/cop/performance/caller.rb
|
57
58
|
- lib/rubocop/cop/performance/case_when_splat.rb
|
58
59
|
- lib/rubocop/cop/performance/casecmp.rb
|
60
|
+
- lib/rubocop/cop/performance/chain_array_allocation.rb
|
59
61
|
- lib/rubocop/cop/performance/compare_with_block.rb
|
60
62
|
- lib/rubocop/cop/performance/count.rb
|
61
63
|
- lib/rubocop/cop/performance/detect.rb
|
@@ -99,7 +101,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
99
101
|
requirements:
|
100
102
|
- - ">="
|
101
103
|
- !ruby/object:Gem::Version
|
102
|
-
version: 2.
|
104
|
+
version: 2.2.0
|
103
105
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
104
106
|
requirements:
|
105
107
|
- - ">="
|
@@ -107,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
109
|
version: '0'
|
108
110
|
requirements: []
|
109
111
|
rubyforge_project:
|
110
|
-
rubygems_version: 2.7.
|
112
|
+
rubygems_version: 2.7.6
|
111
113
|
signing_key:
|
112
114
|
specification_version: 4
|
113
115
|
summary: Automatic performance checking tool for Ruby code.
|