rubocop-performance 1.6.0 → 1.8.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.
Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +1 -1
  3. data/config/default.yml +77 -10
  4. data/lib/rubocop/cop/mixin/regexp_metacharacter.rb +39 -4
  5. data/lib/rubocop/cop/mixin/sort_block.rb +28 -0
  6. data/lib/rubocop/cop/performance/ancestors_include.rb +48 -0
  7. data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +45 -0
  8. data/lib/rubocop/cop/performance/bind_call.rb +8 -18
  9. data/lib/rubocop/cop/performance/caller.rb +3 -2
  10. data/lib/rubocop/cop/performance/case_when_splat.rb +18 -11
  11. data/lib/rubocop/cop/performance/casecmp.rb +12 -20
  12. data/lib/rubocop/cop/performance/chain_array_allocation.rb +4 -10
  13. data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +140 -0
  14. data/lib/rubocop/cop/performance/compare_with_block.rb +10 -21
  15. data/lib/rubocop/cop/performance/count.rb +13 -16
  16. data/lib/rubocop/cop/performance/delete_prefix.rb +43 -28
  17. data/lib/rubocop/cop/performance/delete_suffix.rb +43 -28
  18. data/lib/rubocop/cop/performance/detect.rb +63 -31
  19. data/lib/rubocop/cop/performance/double_start_end_with.rb +16 -24
  20. data/lib/rubocop/cop/performance/end_with.rb +29 -17
  21. data/lib/rubocop/cop/performance/fixed_size.rb +1 -1
  22. data/lib/rubocop/cop/performance/flat_map.rb +20 -22
  23. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +13 -14
  24. data/lib/rubocop/cop/performance/io_readlines.rb +116 -0
  25. data/lib/rubocop/cop/performance/open_struct.rb +2 -2
  26. data/lib/rubocop/cop/performance/range_include.rb +14 -11
  27. data/lib/rubocop/cop/performance/redundant_block_call.rb +11 -6
  28. data/lib/rubocop/cop/performance/redundant_match.rb +11 -6
  29. data/lib/rubocop/cop/performance/redundant_merge.rb +18 -17
  30. data/lib/rubocop/cop/performance/redundant_sort_block.rb +43 -0
  31. data/lib/rubocop/cop/performance/redundant_string_chars.rb +133 -0
  32. data/lib/rubocop/cop/performance/regexp_match.rb +20 -20
  33. data/lib/rubocop/cop/performance/reverse_each.rb +9 -5
  34. data/lib/rubocop/cop/performance/reverse_first.rb +72 -0
  35. data/lib/rubocop/cop/performance/size.rb +41 -43
  36. data/lib/rubocop/cop/performance/sort_reverse.rb +45 -0
  37. data/lib/rubocop/cop/performance/squeeze.rb +66 -0
  38. data/lib/rubocop/cop/performance/start_with.rb +29 -17
  39. data/lib/rubocop/cop/performance/string_include.rb +55 -0
  40. data/lib/rubocop/cop/performance/string_replacement.rb +23 -27
  41. data/lib/rubocop/cop/performance/sum.rb +134 -0
  42. data/lib/rubocop/cop/performance/times_map.rb +11 -18
  43. data/lib/rubocop/cop/performance/unfreeze_string.rb +2 -2
  44. data/lib/rubocop/cop/performance/uri_default_parser.rb +6 -12
  45. data/lib/rubocop/cop/performance_cops.rb +12 -0
  46. data/lib/rubocop/performance/version.rb +1 -1
  47. metadata +33 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 5e13aa59742e803b8445a8149c382c50fdbaab1d
4
- data.tar.gz: 55d59fd7bb03179a9055e46613a3d1f2d2888967
2
+ SHA256:
3
+ metadata.gz: f1f4841e8542a1f77f21d30ffe7c26019b9f2fb7308a985780506f1920146045
4
+ data.tar.gz: ed95379cd20af662ce6ce9ce4e64721ca156a6d7b62e511774df9d6abe3c9bae
5
5
  SHA512:
6
- metadata.gz: 01a28951033ec2b751828d6df2a80f63a5a2d9e9d9576a2228c12b645087b2a631c3676757bd5037e3eecc22d89a7ef28765194545393210a9068da92931d065
7
- data.tar.gz: 79b46ec046a4c1bade7e1e9dd07a5fd892f25280565adb6a91d8dddcce3931722cecf93fb72d4d0e32b7d40441829d26883973b11b5139389b6cebf91bdca435
6
+ metadata.gz: 10a1e89448abd75234807bbdd9be67b10e4b9a837d2d2d711adeb0a0d105b921d722dfcac0c46e30f55f278afc2960f888407959543097d81df4f1aa3667fac6
7
+ data.tar.gz: 668784bfe08775a1e8917806ef9f34d7dcb3f7581a9080ea14a44a54b43cf7e6516db4f5c4e015718824c32a8b045f43b429c8e1d3864960f41ead8684fbb5f5
data/README.md CHANGED
@@ -74,7 +74,7 @@ Performance/Size:
74
74
 
75
75
  ## Documentation
76
76
 
77
- You can read a lot more about RuboCop Performance in its [official docs](https://docs.rubocop.org/projects/performance/).
77
+ You can read a lot more about RuboCop Performance in its [official docs](https://docs.rubocop.org/rubocop-performance/).
78
78
 
79
79
  ## Contributing
80
80
 
@@ -1,5 +1,17 @@
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
+
3
15
  Performance/BindCall:
4
16
  Description: 'Use `bind_call(obj, args, ...)` instead of `bind(obj).call(args, ...)`.'
5
17
  Enabled: true
@@ -37,6 +49,13 @@ Performance/ChainArrayAllocation:
37
49
  Enabled: false
38
50
  VersionAdded: '0.59'
39
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
+
40
59
  Performance/CompareWithBlock:
41
60
  Description: 'Use `sort_by(&:foo)` instead of `sort { |a, b| a.foo <=> b.foo }`.'
42
61
  Enabled: true
@@ -44,31 +63,31 @@ Performance/CompareWithBlock:
44
63
 
45
64
  Performance/Count:
46
65
  Description: >-
47
- Use `count` instead of `select...size`, `reject...size`,
48
- `select...count`, `reject...count`, `select...length`,
49
- and `reject...length`.
66
+ Use `count` instead of `{select,find_all,filter,reject}...{size,count,length}`.
50
67
  # This cop has known compatibility issues with `ActiveRecord` and other
51
68
  # frameworks. ActiveRecord's `count` ignores the block that is passed to it.
52
69
  # For more information, see the documentation in the cop itself.
53
70
  SafeAutoCorrect: false
54
71
  Enabled: true
55
72
  VersionAdded: '0.31'
56
- VersionChanged: '1.5'
73
+ VersionChanged: '1.8'
57
74
 
58
75
  Performance/DeletePrefix:
59
76
  Description: 'Use `delete_prefix` instead of `gsub`.'
60
77
  Enabled: true
78
+ SafeMultiline: true
61
79
  VersionAdded: '1.6'
62
80
 
63
81
  Performance/DeleteSuffix:
64
82
  Description: 'Use `delete_suffix` instead of `gsub`.'
65
83
  Enabled: true
84
+ SafeMultiline: true
66
85
  VersionAdded: '1.6'
67
86
 
68
87
  Performance/Detect:
69
88
  Description: >-
70
- Use `detect` instead of `select.first`, `find_all.first`,
71
- `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`.
72
91
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code'
73
92
  # This cop has known compatibility issues with `ActiveRecord` and other
74
93
  # frameworks. `ActiveRecord` does not implement a `detect` method and `find`
@@ -77,7 +96,7 @@ Performance/Detect:
77
96
  SafeAutoCorrect: false
78
97
  Enabled: true
79
98
  VersionAdded: '0.30'
80
- VersionChanged: '1.5'
99
+ VersionChanged: '1.8'
81
100
 
82
101
  Performance/DoubleStartEndWith:
83
102
  Description: >-
@@ -99,8 +118,9 @@ Performance/EndWith:
99
118
  SafeAutoCorrect: false
100
119
  AutoCorrect: false
101
120
  Enabled: true
121
+ SafeMultiline: true
102
122
  VersionAdded: '0.36'
103
- VersionChanged: '0.44'
123
+ VersionChanged: '1.6'
104
124
 
105
125
  Performance/FixedSize:
106
126
  Description: 'Do not compute the size of statically sized objects except in constants.'
@@ -128,6 +148,12 @@ Performance/InefficientHashSearch:
128
148
  VersionAdded: '0.56'
129
149
  Safe: false
130
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
+
131
157
  Performance/OpenStruct:
132
158
  Description: 'Use `Struct` instead of `OpenStruct`.'
133
159
  Enabled: false
@@ -135,10 +161,11 @@ Performance/OpenStruct:
135
161
  Safe: false
136
162
 
137
163
  Performance/RangeInclude:
138
- Description: 'Use `Range#cover?` instead of `Range#include?`.'
164
+ Description: 'Use `Range#cover?` instead of `Range#include?` (or `Range#member?`).'
139
165
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#cover-vs-include-code'
140
166
  Enabled: true
141
167
  VersionAdded: '0.36'
168
+ VersionChanged: '1.7'
142
169
  Safe: false
143
170
 
144
171
  Performance/RedundantBlockCall:
@@ -162,6 +189,16 @@ Performance/RedundantMerge:
162
189
  # Max number of key-value pairs to consider an offense
163
190
  MaxKeyValuePairs: 2
164
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
+
165
202
  Performance/RegexpMatch:
166
203
  Description: >-
167
204
  Use `match?` instead of `Regexp#match`, `String#match`, `Symbol#match`,
@@ -176,6 +213,11 @@ Performance/ReverseEach:
176
213
  Enabled: true
177
214
  VersionAdded: '0.30'
178
215
 
216
+ Performance/ReverseFirst:
217
+ Description: 'Use `last(n).reverse` instead of `reverse.first(n)`.'
218
+ Enabled: 'pending'
219
+ VersionAdded: '1.7'
220
+
179
221
  Performance/Size:
180
222
  Description: >-
181
223
  Use `size` instead of `count` for counting
@@ -184,6 +226,17 @@ Performance/Size:
184
226
  Enabled: true
185
227
  VersionAdded: '0.30'
186
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
+
187
240
  Performance/StartWith:
188
241
  Description: 'Use `start_with?` instead of a regex match anchored to the beginning of a string.'
189
242
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringmatch-vs-stringstart_withstringend_with-code-start-code-end'
@@ -193,8 +246,16 @@ Performance/StartWith:
193
246
  SafeAutoCorrect: false
194
247
  AutoCorrect: false
195
248
  Enabled: true
249
+ SafeMultiline: true
196
250
  VersionAdded: '0.36'
197
- 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'
198
259
 
199
260
  Performance/StringReplacement:
200
261
  Description: >-
@@ -205,6 +266,12 @@ Performance/StringReplacement:
205
266
  Enabled: true
206
267
  VersionAdded: '0.33'
207
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
+
208
275
  Performance/TimesMap:
209
276
  Description: 'Checks for .times.map calls.'
210
277
  AutoCorrect: false
@@ -4,21 +4,52 @@ module RuboCop
4
4
  module Cop
5
5
  # Common functionality for handling regexp metacharacters.
6
6
  module RegexpMetacharacter
7
- def literal_at_start?(regex_str)
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)
8
32
  # is this regexp 'literal' in the sense of only matching literal
9
33
  # chars, rather than using metachars like `.` and `*` and so on?
10
34
  # also, is it anchored at the start of the string?
11
35
  # (tricky: \s, \d, and so on are metacharacters, but other characters
12
36
  # escaped with a slash are just literals. LITERAL_REGEX takes all
13
37
  # that into account.)
14
- regex_str =~ /\A(\\A|\^)(?:#{Util::LITERAL_REGEX})+\z/
38
+ /\A\^(?:#{Util::LITERAL_REGEX})+\z/.match?(regex_str)
15
39
  end
16
40
 
17
- def literal_at_end?(regex_str)
41
+ def literal_at_end_with_backslash_z?(regex_str)
18
42
  # is this regexp 'literal' in the sense of only matching literal
19
43
  # chars, rather than using metachars like . and * and so on?
20
44
  # also, is it anchored at the end of the string?
21
- regex_str =~ /\A(?:#{Util::LITERAL_REGEX})+(\\z|\$)\z/
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)
22
53
  end
23
54
 
24
55
  def drop_start_metacharacter(regexp_string)
@@ -36,6 +67,10 @@ module RuboCop
36
67
  regexp_string.chop # drop `$` anchor
37
68
  end
38
69
  end
70
+
71
+ def safe_multiline?
72
+ cop_config.fetch('SafeMultiline', true)
73
+ end
39
74
  end
40
75
  end
41
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
@@ -19,8 +19,9 @@ module RuboCop
19
19
  # # good
20
20
  # umethod.bind_call(obj, foo, bar)
21
21
  #
22
- class BindCall < Cop
22
+ class BindCall < Base
23
23
  include RangeHelp
24
+ extend AutoCorrector
24
25
  extend TargetRubyVersion
25
26
 
26
27
  minimum_target_ruby_version 2.7
@@ -37,28 +38,17 @@ module RuboCop
37
38
  PATTERN
38
39
 
39
40
  def on_send(node)
40
- bind_with_call_method?(node) do |receiver, bind_arg, call_args_node|
41
- range = correction_range(receiver, node)
42
-
43
- call_args = build_call_args(call_args_node)
44
-
45
- message = message(bind_arg.source, call_args)
46
-
47
- add_offense(node, location: range, message: message)
48
- end
49
- end
50
-
51
- def autocorrect(node)
52
- receiver, bind_arg, call_args_node = bind_with_call_method?(node)
41
+ return unless (receiver, bind_arg, call_args_node = bind_with_call_method?(node))
53
42
 
54
43
  range = correction_range(receiver, node)
55
-
56
44
  call_args = build_call_args(call_args_node)
57
- call_args = ", #{call_args}" unless call_args.empty?
45
+ message = message(bind_arg.source, call_args)
46
+
47
+ add_offense(range, message: message) do |corrector|
48
+ call_args = ", #{call_args}" unless call_args.empty?
58
49
 
59
- replacement_method = "bind_call(#{bind_arg.source}#{call_args})"
50
+ replacement_method = "bind_call(#{bind_arg.source}#{call_args})"
60
51
 
61
- lambda do |corrector|
62
52
  corrector.replace(range, replacement_method)
63
53
  end
64
54
  end