rubocop-performance 1.5.2 → 1.8.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.
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 +30 -27
  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 +129 -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 +27 -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: 00b98a3d2af1bcfd781dfab24e4a8deb118d29fdaedeca8b2d404ff03eab3529
4
+ data.tar.gz: cc5508a731d9c932d2c141892aefbe15f635d18bdd95ae6ad5b4998c931fcaf2
5
5
  SHA512:
6
- metadata.gz: fddb07ffb4b9ab812d7fcc454f36b3d70fe016783b9cbd863bd0a1391266a39177831b62afb2e35fd4cabc76c94a9b97108177f03ad809a09b69af58365d10dc
7
- data.tar.gz: 1f2375b3dc17a9053a3219a04c45b7f5dee9a99ce472d2e38383c50f463dd8c8c716b6bf558eb6f4245abca24b2d89391ea6fa7fe131dbd0bc7b9eaa2fb994cf
6
+ metadata.gz: 806f21556b5e791a6a00866714c4feec42726b20d4c82db7c4cd89ec576624eaac3007f4a68fca4fd633fbc3ed7a41801fdb0dfc64f767ee241582a4185bbb8b
7
+ data.tar.gz: 1e922a3d51a62df60cdb26f99960fec91e1fa558ceea751bcce7ec529f7d4db6e1644d0dfd24848cb75e384e08fd6a3f2afaaa8f531a1118a09d35d6fa1b0e21
@@ -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).
@@ -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