rubocop-performance 1.8.0 → 1.10.0

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 +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +10 -2
  4. data/config/default.yml +47 -6
  5. data/lib/rubocop/cop/mixin/regexp_metacharacter.rb +4 -4
  6. data/lib/rubocop/cop/performance/ancestors_include.rb +1 -0
  7. data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +77 -0
  8. data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +1 -0
  9. data/lib/rubocop/cop/performance/bind_call.rb +3 -2
  10. data/lib/rubocop/cop/performance/block_given_with_explicit_block.rb +52 -0
  11. data/lib/rubocop/cop/performance/caller.rb +13 -15
  12. data/lib/rubocop/cop/performance/casecmp.rb +1 -0
  13. data/lib/rubocop/cop/performance/chain_array_allocation.rb +20 -18
  14. data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +1 -1
  15. data/lib/rubocop/cop/performance/constant_regexp.rb +73 -0
  16. data/lib/rubocop/cop/performance/count.rb +1 -0
  17. data/lib/rubocop/cop/performance/delete_prefix.rb +1 -0
  18. data/lib/rubocop/cop/performance/delete_suffix.rb +1 -0
  19. data/lib/rubocop/cop/performance/detect.rb +47 -17
  20. data/lib/rubocop/cop/performance/end_with.rb +1 -0
  21. data/lib/rubocop/cop/performance/fixed_size.rb +1 -0
  22. data/lib/rubocop/cop/performance/flat_map.rb +1 -0
  23. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +2 -0
  24. data/lib/rubocop/cop/performance/io_readlines.rb +3 -7
  25. data/lib/rubocop/cop/performance/method_object_as_block.rb +32 -0
  26. data/lib/rubocop/cop/performance/open_struct.rb +1 -0
  27. data/lib/rubocop/cop/performance/range_include.rb +1 -0
  28. data/lib/rubocop/cop/performance/redundant_block_call.rb +4 -4
  29. data/lib/rubocop/cop/performance/redundant_equality_comparison_block.rb +72 -0
  30. data/lib/rubocop/cop/performance/redundant_match.rb +1 -0
  31. data/lib/rubocop/cop/performance/redundant_merge.rb +1 -0
  32. data/lib/rubocop/cop/performance/redundant_split_regexp_argument.rb +67 -0
  33. data/lib/rubocop/cop/performance/redundant_string_chars.rb +2 -6
  34. data/lib/rubocop/cop/performance/reverse_each.rb +7 -10
  35. data/lib/rubocop/cop/performance/reverse_first.rb +1 -0
  36. data/lib/rubocop/cop/performance/size.rb +1 -0
  37. data/lib/rubocop/cop/performance/squeeze.rb +2 -1
  38. data/lib/rubocop/cop/performance/start_with.rb +1 -0
  39. data/lib/rubocop/cop/performance/string_include.rb +2 -1
  40. data/lib/rubocop/cop/performance/string_replacement.rb +1 -0
  41. data/lib/rubocop/cop/performance/sum.rb +131 -18
  42. data/lib/rubocop/cop/performance/times_map.rb +1 -0
  43. data/lib/rubocop/cop/performance/unfreeze_string.rb +19 -1
  44. data/lib/rubocop/cop/performance/uri_default_parser.rb +1 -0
  45. data/lib/rubocop/cop/performance_cops.rb +6 -0
  46. data/lib/rubocop/performance/version.rb +6 -1
  47. metadata +29 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 00b98a3d2af1bcfd781dfab24e4a8deb118d29fdaedeca8b2d404ff03eab3529
4
- data.tar.gz: cc5508a731d9c932d2c141892aefbe15f635d18bdd95ae6ad5b4998c931fcaf2
3
+ metadata.gz: c936c552e05f2364a4256d5f0975973ee3a332270bc1af524db3420e9fc70ba5
4
+ data.tar.gz: 3cd0086460c2db1c5926caef0737a1d9ae4038808b58b7c91f97e349cfe8f1b6
5
5
  SHA512:
6
- metadata.gz: 806f21556b5e791a6a00866714c4feec42726b20d4c82db7c4cd89ec576624eaac3007f4a68fca4fd633fbc3ed7a41801fdb0dfc64f767ee241582a4185bbb8b
7
- data.tar.gz: 1e922a3d51a62df60cdb26f99960fec91e1fa558ceea751bcce7ec529f7d4db6e1644d0dfd24848cb75e384e08fd6a3f2afaaa8f531a1118a09d35d6fa1b0e21
6
+ metadata.gz: cdc03f8c28e3ea1f63a8477fe064cf1df766f6334fc017d5f8bc7a830f189ad11c1f5a65a33008469b4fe8438522f64b56e6b026fdadb208abf18181d2a6d261
7
+ data.tar.gz: 04034d2353dc721670ac494d40fae7383ba3800923feea6140c8ea9d86ee080028bc8fb05e46deeb475f1365705114d6a3848520e01248076f38f6e4ef18282f
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-20 Bozhidar Batsov
1
+ Copyright (c) 2012-21 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
@@ -1,9 +1,9 @@
1
1
  # RuboCop Performance
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/rubocop-performance.svg)](https://badge.fury.io/rb/rubocop-performance)
4
- [![CircleCI](https://circleci.com/gh/rubocop-hq/rubocop-performance.svg?style=svg)](https://circleci.com/gh/rubocop-hq/rubocop-performance)
4
+ [![CircleCI](https://circleci.com/gh/rubocop/rubocop-performance.svg?style=svg)](https://circleci.com/gh/rubocop/rubocop-performance)
5
5
 
6
- Performance optimization analysis for your projects, as an extension to [RuboCop](https://github.com/rubocop-hq/rubocop).
6
+ Performance optimization analysis for your projects, as an extension to [RuboCop](https://github.com/rubocop/rubocop).
7
7
 
8
8
  ## Installation
9
9
 
@@ -76,6 +76,14 @@ Performance/Size:
76
76
 
77
77
  You can read a lot more about RuboCop Performance in its [official docs](https://docs.rubocop.org/rubocop-performance/).
78
78
 
79
+ ## Compatibility
80
+
81
+ RuboCop Performance complies with the RuboCop core compatibility.
82
+
83
+ See the [compatibility documentation](https://docs.rubocop.org/rubocop/compatibility.html) for further details.
84
+
85
+ **Note:** Performance cops are all MRI focused and are highly dependent of the version of MRI you're using.
86
+
79
87
  ## Contributing
80
88
 
81
89
  Checkout the [contribution guidelines](CONTRIBUTING.md).
data/config/default.yml CHANGED
@@ -7,6 +7,15 @@ Performance/AncestorsInclude:
7
7
  Safe: false
8
8
  VersionAdded: '1.7'
9
9
 
10
+ Performance/ArraySemiInfiniteRangeSlice:
11
+ Description: 'Identifies places where slicing arrays with semi-infinite ranges can be replaced by `Array#take` and `Array#drop`.'
12
+ # This cop was created due to a mistake in microbenchmark.
13
+ # Refer https://github.com/rubocop/rubocop-performance/pull/175#issuecomment-731892717
14
+ Enabled: false
15
+ # Unsafe for string slices because strings do not have `#take` and `#drop` methods.
16
+ Safe: false
17
+ VersionAdded: '1.9'
18
+
10
19
  Performance/BigDecimalWithNumericArgument:
11
20
  Description: 'Convert numeric argument to string before passing to BigDecimal.'
12
21
  Enabled: 'pending'
@@ -17,11 +26,17 @@ Performance/BindCall:
17
26
  Enabled: true
18
27
  VersionAdded: '1.6'
19
28
 
29
+ Performance/BlockGivenWithExplicitBlock:
30
+ Description: 'Check block argument explicitly instead of using `block_given?`.'
31
+ Enabled: pending
32
+ VersionAdded: '1.9'
33
+
20
34
  Performance/Caller:
21
35
  Description: >-
22
36
  Use `caller(n..n)` instead of `caller`.
23
37
  Enabled: true
24
38
  VersionAdded: '0.49'
39
+ VersionChanged: '1.9'
25
40
 
26
41
  Performance/CaseWhenSplat:
27
42
  Description: >-
@@ -51,7 +66,7 @@ Performance/ChainArrayAllocation:
51
66
 
52
67
  Performance/CollectionLiteralInLoop:
53
68
  Description: 'Extract Array and Hash literals outside of loops into local variables or constants.'
54
- Enabled: true
69
+ Enabled: 'pending'
55
70
  VersionAdded: '1.8'
56
71
  # Min number of elements to consider an offense
57
72
  MinSize: 1
@@ -61,6 +76,12 @@ Performance/CompareWithBlock:
61
76
  Enabled: true
62
77
  VersionAdded: '0.46'
63
78
 
79
+ Performance/ConstantRegexp:
80
+ Description: 'Finds regular expressions with dynamic components that are all constants.'
81
+ Enabled: pending
82
+ VersionAdded: '1.9'
83
+ VersionChanged: '1.10'
84
+
64
85
  Performance/Count:
65
86
  Description: >-
66
87
  Use `count` instead of `{select,find_all,filter,reject}...{size,count,length}`.
@@ -116,11 +137,10 @@ Performance/EndWith:
116
137
  # object. Switching these methods has to be done with knowledge of the types
117
138
  # of the variables which rubocop doesn't have.
118
139
  SafeAutoCorrect: false
119
- AutoCorrect: false
120
140
  Enabled: true
121
141
  SafeMultiline: true
122
142
  VersionAdded: '0.36'
123
- VersionChanged: '1.6'
143
+ VersionChanged: '1.10'
124
144
 
125
145
  Performance/FixedSize:
126
146
  Description: 'Do not compute the size of statically sized objects except in constants.'
@@ -154,6 +174,12 @@ Performance/IoReadlines:
154
174
  Enabled: false
155
175
  VersionAdded: '1.7'
156
176
 
177
+ Performance/MethodObjectAsBlock:
178
+ Description: 'Use block explicitly instead of block-passing a method object.'
179
+ Reference: 'https://github.com/JuanitoFatas/fast-ruby#normal-way-to-apply-method-vs-method-code'
180
+ Enabled: pending
181
+ VersionAdded: '1.9'
182
+
157
183
  Performance/OpenStruct:
158
184
  Description: 'Use `Struct` instead of `OpenStruct`.'
159
185
  Enabled: false
@@ -174,6 +200,15 @@ Performance/RedundantBlockCall:
174
200
  Enabled: true
175
201
  VersionAdded: '0.36'
176
202
 
203
+ Performance/RedundantEqualityComparisonBlock:
204
+ Description: >-
205
+ Checks for uses `Enumerable#all?`, `Enumerable#any?`, `Enumerable#one?`,
206
+ or `Enumerable#none?` are compared with `===` or similar methods in block.
207
+ Reference: 'https://github.com/rails/rails/pull/41363'
208
+ Enabled: pending
209
+ Safe: false
210
+ VersionAdded: '1.10'
211
+
177
212
  Performance/RedundantMatch:
178
213
  Description: >-
179
214
  Use `=~` instead of `String#match` or `Regexp#match` in a context where the
@@ -194,6 +229,11 @@ Performance/RedundantSortBlock:
194
229
  Enabled: 'pending'
195
230
  VersionAdded: '1.7'
196
231
 
232
+ Performance/RedundantSplitRegexpArgument:
233
+ Description: 'This cop identifies places where `split` argument can be replaced from a deterministic regexp to a string.'
234
+ Enabled: pending
235
+ VersionAdded: '1.10'
236
+
197
237
  Performance/RedundantStringChars:
198
238
  Description: 'Checks for redundant `String#chars`.'
199
239
  Enabled: 'pending'
@@ -244,11 +284,10 @@ Performance/StartWith:
244
284
  # object. Switching these methods has to be done with knowledge of the types
245
285
  # of the variables which rubocop doesn't have.
246
286
  SafeAutoCorrect: false
247
- AutoCorrect: false
248
287
  Enabled: true
249
288
  SafeMultiline: true
250
289
  VersionAdded: '0.36'
251
- VersionChanged: '1.6'
290
+ VersionChanged: '1.10'
252
291
 
253
292
  Performance/StringInclude:
254
293
  Description: 'Use `String#include?` instead of a regex match with literal-only pattern.'
@@ -278,12 +317,14 @@ Performance/TimesMap:
278
317
  Enabled: true
279
318
  VersionAdded: '0.36'
280
319
  VersionChanged: '0.50'
281
- SafeAutoCorrect: false # see https://github.com/rubocop-hq/rubocop/issues/4658
320
+ SafeAutoCorrect: false # see https://github.com/rubocop/rubocop/issues/4658
282
321
 
283
322
  Performance/UnfreezeString:
284
323
  Description: 'Use unary plus to get an unfrozen string literal.'
285
324
  Enabled: true
325
+ SafeAutoCorrect: false
286
326
  VersionAdded: '0.50'
327
+ VersionChanged: '1.9'
287
328
 
288
329
  Performance/UriDefaultParser:
289
330
  Description: 'Use `URI::DEFAULT_PARSER` instead of `URI::Parser.new`.'
@@ -25,7 +25,7 @@ module RuboCop
25
25
  # (tricky: \s, \d, and so on are metacharacters, but other characters
26
26
  # escaped with a slash are just literals. LITERAL_REGEX takes all
27
27
  # that into account.)
28
- /\A\\A(?:#{Util::LITERAL_REGEX})+\z/.match?(regex_str)
28
+ /\A\\A(?:#{Util::LITERAL_REGEX})+\z/o.match?(regex_str)
29
29
  end
30
30
 
31
31
  def literal_at_start_with_caret?(regex_str)
@@ -35,21 +35,21 @@ module RuboCop
35
35
  # (tricky: \s, \d, and so on are metacharacters, but other characters
36
36
  # escaped with a slash are just literals. LITERAL_REGEX takes all
37
37
  # that into account.)
38
- /\A\^(?:#{Util::LITERAL_REGEX})+\z/.match?(regex_str)
38
+ /\A\^(?:#{Util::LITERAL_REGEX})+\z/o.match?(regex_str)
39
39
  end
40
40
 
41
41
  def literal_at_end_with_backslash_z?(regex_str)
42
42
  # is this regexp 'literal' in the sense of only matching literal
43
43
  # chars, rather than using metachars like . and * and so on?
44
44
  # also, is it anchored at the end of the string?
45
- /\A(?:#{Util::LITERAL_REGEX})+\\z\z/.match?(regex_str)
45
+ /\A(?:#{Util::LITERAL_REGEX})+\\z\z/o.match?(regex_str)
46
46
  end
47
47
 
48
48
  def literal_at_end_with_dollar?(regex_str)
49
49
  # is this regexp 'literal' in the sense of only matching literal
50
50
  # chars, rather than using metachars like . and * and so on?
51
51
  # also, is it anchored at the end of the string?
52
- /\A(?:#{Util::LITERAL_REGEX})+\$\z/.match?(regex_str)
52
+ /\A(?:#{Util::LITERAL_REGEX})+\$\z/o.match?(regex_str)
53
53
  end
54
54
 
55
55
  def drop_start_metacharacter(regexp_string)
@@ -18,6 +18,7 @@ module RuboCop
18
18
  extend AutoCorrector
19
19
 
20
20
  MSG = 'Use `<=` instead of `ancestors.include?`.'
21
+ RESTRICT_ON_SEND = %i[include?].freeze
21
22
 
22
23
  def_node_matcher :ancestors_include_candidate?, <<~PATTERN
23
24
  (send (send $_subclass :ancestors) :include? $_superclass)
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop identifies places where slicing arrays with semi-infinite ranges
7
+ # can be replaced by `Array#take` and `Array#drop`.
8
+ # This cop was created due to a mistake in microbenchmark and hence is disabled by default.
9
+ # Refer https://github.com/rubocop/rubocop-performance/pull/175#issuecomment-731892717
10
+ # This cop is also unsafe for string slices because strings do not have `#take` and `#drop` methods.
11
+ #
12
+ # @example
13
+ # # bad
14
+ # # array[..2]
15
+ # # array[...2]
16
+ # # array[2..]
17
+ # # array[2...]
18
+ # # array.slice(..2)
19
+ #
20
+ # # good
21
+ # array.take(3)
22
+ # array.take(2)
23
+ # array.drop(2)
24
+ # array.drop(2)
25
+ # array.take(3)
26
+ #
27
+ class ArraySemiInfiniteRangeSlice < Base
28
+ include RangeHelp
29
+ extend AutoCorrector
30
+ extend TargetRubyVersion
31
+
32
+ minimum_target_ruby_version 2.7
33
+
34
+ MSG = 'Use `%<prefer>s` instead of `%<current>s` with semi-infinite range.'
35
+
36
+ SLICE_METHODS = Set[:[], :slice].freeze
37
+ RESTRICT_ON_SEND = SLICE_METHODS
38
+
39
+ def_node_matcher :endless_range_slice?, <<~PATTERN
40
+ (send $_ $%SLICE_METHODS $#endless_range?)
41
+ PATTERN
42
+
43
+ def_node_matcher :endless_range?, <<~PATTERN
44
+ {
45
+ ({irange erange} nil? (int positive?))
46
+ ({irange erange} (int positive?) nil?)
47
+ }
48
+ PATTERN
49
+
50
+ def on_send(node)
51
+ endless_range_slice?(node) do |receiver, method_name, range_node|
52
+ prefer = range_node.begin ? :drop : :take
53
+ message = format(MSG, prefer: prefer, current: method_name)
54
+
55
+ add_offense(node, message: message) do |corrector|
56
+ corrector.replace(node, correction(receiver, range_node))
57
+ end
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def correction(receiver, range_node)
64
+ method_call = if range_node.begin
65
+ "drop(#{range_node.begin.value})"
66
+ elsif range_node.irange_type?
67
+ "take(#{range_node.end.value + 1})"
68
+ else
69
+ "take(#{range_node.end.value})"
70
+ end
71
+
72
+ "#{receiver.source}.#{method_call}"
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -20,6 +20,7 @@ module RuboCop
20
20
  extend AutoCorrector
21
21
 
22
22
  MSG = 'Convert numeric argument to string before passing to `BigDecimal`.'
23
+ RESTRICT_ON_SEND = %i[BigDecimal].freeze
23
24
 
24
25
  def_node_matcher :big_decimal_with_numeric_argument?, <<~PATTERN
25
26
  (send nil? :BigDecimal $numeric_type? ...)
@@ -28,11 +28,12 @@ module RuboCop
28
28
 
29
29
  MSG = 'Use `bind_call(%<bind_arg>s%<comma>s%<call_args>s)` ' \
30
30
  'instead of `bind(%<bind_arg>s).call(%<call_args>s)`.'
31
+ RESTRICT_ON_SEND = %i[call].freeze
31
32
 
32
33
  def_node_matcher :bind_with_call_method?, <<~PATTERN
33
34
  (send
34
35
  $(send
35
- (send nil? _) :bind
36
+ _ :bind
36
37
  $(...)) :call
37
38
  $...)
38
39
  PATTERN
@@ -63,7 +64,7 @@ module RuboCop
63
64
 
64
65
  def correction_range(receiver, node)
65
66
  location_of_bind = receiver.loc.selector.begin_pos
66
- location_of_call = node.loc.end.end_pos
67
+ location_of_call = node.source_range.end.end_pos
67
68
 
68
69
  range_between(location_of_bind, location_of_call)
69
70
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop identifies unnecessary use of a `block_given?` where explicit check
7
+ # of block argument would suffice.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # def method(&block)
12
+ # do_something if block_given?
13
+ # end
14
+ #
15
+ # # good
16
+ # def method(&block)
17
+ # do_something if block
18
+ # end
19
+ #
20
+ # # good - block is reassigned
21
+ # def method(&block)
22
+ # block ||= -> { do_something }
23
+ # warn "Using default ..." unless block_given?
24
+ # # ...
25
+ # end
26
+ #
27
+ class BlockGivenWithExplicitBlock < Base
28
+ extend AutoCorrector
29
+
30
+ RESTRICT_ON_SEND = %i[block_given?].freeze
31
+ MSG = 'Check block argument explicitly instead of using `block_given?`.'
32
+
33
+ def_node_matcher :reassigns_block_arg?, '`(lvasgn %1 ...)'
34
+
35
+ def on_send(node)
36
+ def_node = node.each_ancestor(:def, :defs).first
37
+ return unless def_node
38
+
39
+ block_arg = def_node.arguments.find(&:blockarg_type?)
40
+ return unless block_arg
41
+
42
+ block_arg_name = block_arg.loc.name.source.to_sym
43
+ return if reassigns_block_arg?(def_node, block_arg_name)
44
+
45
+ add_offense(node) do |corrector|
46
+ corrector.replace(node, block_arg_name)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -19,10 +19,10 @@ module RuboCop
19
19
  # caller_locations(2..2).first
20
20
  # caller_locations(1..1).first
21
21
  class Caller < Base
22
- MSG_BRACE = 'Use `%<method>s(%<n>d..%<n>d).first`' \
23
- ' instead of `%<method>s[%<m>d]`.'
24
- MSG_FIRST = 'Use `%<method>s(%<n>d..%<n>d).first`' \
25
- ' instead of `%<method>s.first`.'
22
+ extend AutoCorrector
23
+
24
+ MSG = 'Use `%<preferred_method>s` instead of `%<current_method>s`.'
25
+ RESTRICT_ON_SEND = %i[first []].freeze
26
26
 
27
27
  def_node_matcher :slow_caller?, <<~PATTERN
28
28
  {
@@ -41,26 +41,24 @@ module RuboCop
41
41
  def on_send(node)
42
42
  return unless caller_with_scope_method?(node)
43
43
 
44
- message = message(node)
45
- add_offense(node, message: message)
46
- end
47
-
48
- private
49
-
50
- def message(node)
51
44
  method_name = node.receiver.method_name
52
45
  caller_arg = node.receiver.first_argument
53
46
  n = caller_arg ? int_value(caller_arg) : 1
54
-
55
47
  if node.method?(:[])
56
48
  m = int_value(node.first_argument)
57
49
  n += m
58
- format(MSG_BRACE, n: n, m: m, method: method_name)
59
- else
60
- format(MSG_FIRST, n: n, method: method_name)
50
+ end
51
+
52
+ preferred_method = "#{method_name}(#{n}..#{n}).first"
53
+
54
+ message = format(MSG, preferred_method: preferred_method, current_method: node.source)
55
+ add_offense(node, message: message) do |corrector|
56
+ corrector.replace(node, preferred_method)
61
57
  end
62
58
  end
63
59
 
60
+ private
61
+
64
62
  def int_value(node)
65
63
  node.children[0]
66
64
  end
@@ -23,6 +23,7 @@ module RuboCop
23
23
  extend AutoCorrector
24
24
 
25
25
  MSG = 'Use `%<good>s` instead of `%<bad>s`.'
26
+ RESTRICT_ON_SEND = %i[== eql? !=].freeze
26
27
  CASE_METHODS = %i[downcase upcase].freeze
27
28
 
28
29
  def_node_matcher :downcase_eq, <<~PATTERN