rubocop-performance 1.6.0 → 1.8.1

Sign up to get free protection for your applications and to get access to all the features.
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