rubocop-performance 1.5.2 → 1.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +5 -1
  4. data/config/default.yml +96 -13
  5. data/lib/rubocop/cop/mixin/regexp_metacharacter.rb +76 -0
  6. data/lib/rubocop/cop/mixin/sort_block.rb +28 -0
  7. data/lib/rubocop/cop/performance/ancestors_include.rb +48 -0
  8. data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +45 -0
  9. data/lib/rubocop/cop/performance/bind_call.rb +77 -0
  10. data/lib/rubocop/cop/performance/caller.rb +5 -4
  11. data/lib/rubocop/cop/performance/case_when_splat.rb +18 -11
  12. data/lib/rubocop/cop/performance/casecmp.rb +17 -23
  13. data/lib/rubocop/cop/performance/chain_array_allocation.rb +5 -11
  14. data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +140 -0
  15. data/lib/rubocop/cop/performance/compare_with_block.rb +12 -23
  16. data/lib/rubocop/cop/performance/count.rb +14 -17
  17. data/lib/rubocop/cop/performance/delete_prefix.rb +87 -0
  18. data/lib/rubocop/cop/performance/delete_suffix.rb +87 -0
  19. data/lib/rubocop/cop/performance/detect.rb +64 -32
  20. data/lib/rubocop/cop/performance/double_start_end_with.rb +18 -26
  21. data/lib/rubocop/cop/performance/end_with.rb +38 -25
  22. data/lib/rubocop/cop/performance/fixed_size.rb +2 -2
  23. data/lib/rubocop/cop/performance/flat_map.rb +21 -23
  24. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +14 -15
  25. data/lib/rubocop/cop/performance/io_readlines.rb +116 -0
  26. data/lib/rubocop/cop/performance/open_struct.rb +3 -3
  27. data/lib/rubocop/cop/performance/range_include.rb +15 -12
  28. data/lib/rubocop/cop/performance/redundant_block_call.rb +14 -9
  29. data/lib/rubocop/cop/performance/redundant_match.rb +13 -8
  30. data/lib/rubocop/cop/performance/redundant_merge.rb +36 -23
  31. data/lib/rubocop/cop/performance/redundant_sort_block.rb +43 -0
  32. data/lib/rubocop/cop/performance/redundant_string_chars.rb +133 -0
  33. data/lib/rubocop/cop/performance/regexp_match.rb +32 -32
  34. data/lib/rubocop/cop/performance/reverse_each.rb +10 -5
  35. data/lib/rubocop/cop/performance/reverse_first.rb +72 -0
  36. data/lib/rubocop/cop/performance/size.rb +41 -43
  37. data/lib/rubocop/cop/performance/sort_reverse.rb +45 -0
  38. data/lib/rubocop/cop/performance/squeeze.rb +66 -0
  39. data/lib/rubocop/cop/performance/start_with.rb +38 -28
  40. data/lib/rubocop/cop/performance/string_include.rb +55 -0
  41. data/lib/rubocop/cop/performance/string_replacement.rb +25 -36
  42. data/lib/rubocop/cop/performance/sum.rb +134 -0
  43. data/lib/rubocop/cop/performance/times_map.rb +12 -19
  44. data/lib/rubocop/cop/performance/unfreeze_string.rb +4 -8
  45. data/lib/rubocop/cop/performance/uri_default_parser.rb +7 -13
  46. data/lib/rubocop/cop/performance_cops.rb +17 -0
  47. data/lib/rubocop/performance/inject.rb +1 -1
  48. data/lib/rubocop/performance/version.rb +1 -1
  49. metadata +41 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 330b6db292d0709ef76d23cae8b7e84b24413d315b356a10a8a8596859b366ce
4
- data.tar.gz: 32e9f148b06d630da9233b4fab33ea142eb0aa1006cd7cc311aafadabfe3c8fa
3
+ metadata.gz: f1f4841e8542a1f77f21d30ffe7c26019b9f2fb7308a985780506f1920146045
4
+ data.tar.gz: ed95379cd20af662ce6ce9ce4e64721ca156a6d7b62e511774df9d6abe3c9bae
5
5
  SHA512:
6
- metadata.gz: fddb07ffb4b9ab812d7fcc454f36b3d70fe016783b9cbd863bd0a1391266a39177831b62afb2e35fd4cabc76c94a9b97108177f03ad809a09b69af58365d10dc
7
- data.tar.gz: 1f2375b3dc17a9053a3219a04c45b7f5dee9a99ce472d2e38383c50f463dd8c8c716b6bf558eb6f4245abca24b2d89391ea6fa7fe131dbd0bc7b9eaa2fb994cf
6
+ metadata.gz: 10a1e89448abd75234807bbdd9be67b10e4b9a837d2d2d711adeb0a0d105b921d722dfcac0c46e30f55f278afc2960f888407959543097d81df4f1aa3667fac6
7
+ data.tar.gz: 668784bfe08775a1e8917806ef9f34d7dcb3f7581a9080ea14a44a54b43cf7e6516db4f5c4e015718824c32a8b045f43b429c8e1d3864960f41ead8684fbb5f5
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-18 Bozhidar Batsov
1
+ Copyright (c) 2012-20 Bozhidar Batsov
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
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:
@@ -31,6 +49,13 @@ Performance/ChainArrayAllocation:
31
49
  Enabled: false
32
50
  VersionAdded: '0.59'
33
51
 
52
+ Performance/CollectionLiteralInLoop:
53
+ Description: 'Extract Array and Hash literals outside of loops into local variables or constants.'
54
+ Enabled: true
55
+ VersionAdded: '1.8'
56
+ # Min number of elements to consider an offense
57
+ MinSize: 1
58
+
34
59
  Performance/CompareWithBlock:
35
60
  Description: 'Use `sort_by(&:foo)` instead of `sort { |a, b| a.foo <=> b.foo }`.'
36
61
  Enabled: true
@@ -38,21 +63,31 @@ Performance/CompareWithBlock:
38
63
 
39
64
  Performance/Count:
40
65
  Description: >-
41
- Use `count` instead of `select...size`, `reject...size`,
42
- `select...count`, `reject...count`, `select...length`,
43
- and `reject...length`.
66
+ Use `count` instead of `{select,find_all,filter,reject}...{size,count,length}`.
44
67
  # This cop has known compatibility issues with `ActiveRecord` and other
45
68
  # frameworks. ActiveRecord's `count` ignores the block that is passed to it.
46
69
  # For more information, see the documentation in the cop itself.
47
70
  SafeAutoCorrect: false
48
71
  Enabled: true
49
72
  VersionAdded: '0.31'
50
- VersionChanged: '1.5'
73
+ VersionChanged: '1.8'
74
+
75
+ Performance/DeletePrefix:
76
+ Description: 'Use `delete_prefix` instead of `gsub`.'
77
+ Enabled: true
78
+ SafeMultiline: true
79
+ VersionAdded: '1.6'
80
+
81
+ Performance/DeleteSuffix:
82
+ Description: 'Use `delete_suffix` instead of `gsub`.'
83
+ Enabled: true
84
+ SafeMultiline: true
85
+ VersionAdded: '1.6'
51
86
 
52
87
  Performance/Detect:
53
88
  Description: >-
54
- Use `detect` instead of `select.first`, `find_all.first`,
55
- `select.last`, and `find_all.last`.
89
+ Use `detect` instead of `select.first`, `find_all.first`, `filter.first`,
90
+ `select.last`, `find_all.last`, and `filter.last`.
56
91
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code'
57
92
  # This cop has known compatibility issues with `ActiveRecord` and other
58
93
  # frameworks. `ActiveRecord` does not implement a `detect` method and `find`
@@ -61,7 +96,7 @@ Performance/Detect:
61
96
  SafeAutoCorrect: false
62
97
  Enabled: true
63
98
  VersionAdded: '0.30'
64
- VersionChanged: '1.5'
99
+ VersionChanged: '1.8'
65
100
 
66
101
  Performance/DoubleStartEndWith:
67
102
  Description: >-
@@ -83,11 +118,12 @@ Performance/EndWith:
83
118
  SafeAutoCorrect: false
84
119
  AutoCorrect: false
85
120
  Enabled: true
121
+ SafeMultiline: true
86
122
  VersionAdded: '0.36'
87
- VersionChanged: '0.44'
123
+ VersionChanged: '1.6'
88
124
 
89
125
  Performance/FixedSize:
90
- Description: 'Do not compute the size of statically sized objects except in constants'
126
+ Description: 'Do not compute the size of statically sized objects except in constants.'
91
127
  Enabled: true
92
128
  VersionAdded: '0.35'
93
129
 
@@ -95,7 +131,7 @@ Performance/FlatMap:
95
131
  Description: >-
96
132
  Use `Enumerable#flat_map`
97
133
  instead of `Enumerable#map...Array#flatten(1)`
98
- or `Enumberable#collect..Array#flatten(1)`
134
+ or `Enumberable#collect..Array#flatten(1)`.
99
135
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code'
100
136
  Enabled: true
101
137
  VersionAdded: '0.30'
@@ -106,12 +142,18 @@ Performance/FlatMap:
106
142
  # `flatten` without any parameters can flatten multiple levels.
107
143
 
108
144
  Performance/InefficientHashSearch:
109
- Description: 'Use `key?` or `value?` instead of `keys.include?` or `values.include?`'
145
+ Description: 'Use `key?` or `value?` instead of `keys.include?` or `values.include?`.'
110
146
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#hashkey-instead-of-hashkeysinclude-code'
111
147
  Enabled: true
112
148
  VersionAdded: '0.56'
113
149
  Safe: false
114
150
 
151
+ Performance/IoReadlines:
152
+ Description: 'Use `IO.each_line` (`IO#each_line`) instead of `IO.readlines` (`IO#readlines`).'
153
+ Reference: 'https://docs.gitlab.com/ee/development/performance.html#reading-from-files-and-other-data-sources'
154
+ Enabled: false
155
+ VersionAdded: '1.7'
156
+
115
157
  Performance/OpenStruct:
116
158
  Description: 'Use `Struct` instead of `OpenStruct`.'
117
159
  Enabled: false
@@ -119,10 +161,11 @@ Performance/OpenStruct:
119
161
  Safe: false
120
162
 
121
163
  Performance/RangeInclude:
122
- Description: 'Use `Range#cover?` instead of `Range#include?`.'
164
+ Description: 'Use `Range#cover?` instead of `Range#include?` (or `Range#member?`).'
123
165
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#cover-vs-include-code'
124
166
  Enabled: true
125
167
  VersionAdded: '0.36'
168
+ VersionChanged: '1.7'
126
169
  Safe: false
127
170
 
128
171
  Performance/RedundantBlockCall:
@@ -146,6 +189,16 @@ Performance/RedundantMerge:
146
189
  # Max number of key-value pairs to consider an offense
147
190
  MaxKeyValuePairs: 2
148
191
 
192
+ Performance/RedundantSortBlock:
193
+ Description: 'Use `sort` instead of `sort { |a, b| a <=> b }`.'
194
+ Enabled: 'pending'
195
+ VersionAdded: '1.7'
196
+
197
+ Performance/RedundantStringChars:
198
+ Description: 'Checks for redundant `String#chars`.'
199
+ Enabled: 'pending'
200
+ VersionAdded: '1.7'
201
+
149
202
  Performance/RegexpMatch:
150
203
  Description: >-
151
204
  Use `match?` instead of `Regexp#match`, `String#match`, `Symbol#match`,
@@ -160,6 +213,11 @@ Performance/ReverseEach:
160
213
  Enabled: true
161
214
  VersionAdded: '0.30'
162
215
 
216
+ Performance/ReverseFirst:
217
+ Description: 'Use `last(n).reverse` instead of `reverse.first(n)`.'
218
+ Enabled: 'pending'
219
+ VersionAdded: '1.7'
220
+
163
221
  Performance/Size:
164
222
  Description: >-
165
223
  Use `size` instead of `count` for counting
@@ -168,6 +226,17 @@ Performance/Size:
168
226
  Enabled: true
169
227
  VersionAdded: '0.30'
170
228
 
229
+ Performance/SortReverse:
230
+ Description: 'Use `sort.reverse` instead of `sort { |a, b| b <=> a }`.'
231
+ Enabled: 'pending'
232
+ VersionAdded: '1.7'
233
+
234
+ Performance/Squeeze:
235
+ Description: "Use `squeeze('a')` instead of `gsub(/a+/, 'a')`."
236
+ Reference: 'https://github.com/JuanitoFatas/fast-ruby#remove-extra-spaces-or-other-contiguous-characters-code'
237
+ Enabled: 'pending'
238
+ VersionAdded: '1.7'
239
+
171
240
  Performance/StartWith:
172
241
  Description: 'Use `start_with?` instead of a regex match anchored to the beginning of a string.'
173
242
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end'
@@ -177,8 +246,16 @@ Performance/StartWith:
177
246
  SafeAutoCorrect: false
178
247
  AutoCorrect: false
179
248
  Enabled: true
249
+ SafeMultiline: true
180
250
  VersionAdded: '0.36'
181
- VersionChanged: '0.44'
251
+ VersionChanged: '1.6'
252
+
253
+ Performance/StringInclude:
254
+ Description: 'Use `String#include?` instead of a regex match with literal-only pattern.'
255
+ Enabled: 'pending'
256
+ AutoCorrect: false
257
+ SafeAutoCorrect: false
258
+ VersionAdded: '1.7'
182
259
 
183
260
  Performance/StringReplacement:
184
261
  Description: >-
@@ -189,6 +266,12 @@ Performance/StringReplacement:
189
266
  Enabled: true
190
267
  VersionAdded: '0.33'
191
268
 
269
+ Performance/Sum:
270
+ Description: 'Use `sum` instead of a custom array summation.'
271
+ Reference: 'https://blog.bigbinary.com/2016/11/02/ruby-2-4-introduces-enumerable-sum.html'
272
+ Enabled: 'pending'
273
+ VersionAdded: '1.8'
274
+
192
275
  Performance/TimesMap:
193
276
  Description: 'Checks for .times.map calls.'
194
277
  AutoCorrect: false
@@ -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,48 @@
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 < Base
17
+ include RangeHelp
18
+ extend AutoCorrector
19
+
20
+ MSG = 'Use `<=` instead of `ancestors.include?`.'
21
+
22
+ def_node_matcher :ancestors_include_candidate?, <<~PATTERN
23
+ (send (send $_subclass :ancestors) :include? $_superclass)
24
+ PATTERN
25
+
26
+ def on_send(node)
27
+ return unless (subclass, superclass = ancestors_include_candidate?(node))
28
+ return if subclass && !subclass.const_type?
29
+
30
+ add_offense(range(node)) do |corrector|
31
+ subclass_source = subclass ? subclass.source : 'self'
32
+
33
+ corrector.replace(node, "#{subclass_source} <= #{superclass.source}")
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def range(node)
40
+ location_of_ancestors = node.children[0].loc.selector.begin_pos
41
+ end_location = node.loc.selector.end_pos
42
+
43
+ range_between(location_of_ancestors, end_location)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,45 @@
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 < Base
20
+ extend AutoCorrector
21
+
22
+ MSG = 'Convert numeric argument to string before passing to `BigDecimal`.'
23
+
24
+ def_node_matcher :big_decimal_with_numeric_argument?, <<~PATTERN
25
+ (send nil? :BigDecimal $numeric_type? ...)
26
+ PATTERN
27
+
28
+ def on_send(node)
29
+ return unless (numeric = big_decimal_with_numeric_argument?(node))
30
+ return if numeric.float_type? && specifies_precision?(node)
31
+
32
+ add_offense(numeric.source_range) do |corrector|
33
+ corrector.wrap(numeric, "'", "'")
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def specifies_precision?(node)
40
+ node.arguments.size > 1 && !node.arguments[1].hash_type?
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end