rubocop-performance 1.8.1 → 1.10.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 (46) 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/constant_regexp.rb +73 -0
  15. data/lib/rubocop/cop/performance/count.rb +1 -0
  16. data/lib/rubocop/cop/performance/delete_prefix.rb +1 -0
  17. data/lib/rubocop/cop/performance/delete_suffix.rb +1 -0
  18. data/lib/rubocop/cop/performance/detect.rb +3 -2
  19. data/lib/rubocop/cop/performance/end_with.rb +1 -0
  20. data/lib/rubocop/cop/performance/fixed_size.rb +1 -0
  21. data/lib/rubocop/cop/performance/flat_map.rb +1 -0
  22. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +2 -0
  23. data/lib/rubocop/cop/performance/io_readlines.rb +3 -7
  24. data/lib/rubocop/cop/performance/method_object_as_block.rb +32 -0
  25. data/lib/rubocop/cop/performance/open_struct.rb +1 -0
  26. data/lib/rubocop/cop/performance/range_include.rb +1 -0
  27. data/lib/rubocop/cop/performance/redundant_block_call.rb +4 -4
  28. data/lib/rubocop/cop/performance/redundant_equality_comparison_block.rb +80 -0
  29. data/lib/rubocop/cop/performance/redundant_match.rb +1 -0
  30. data/lib/rubocop/cop/performance/redundant_merge.rb +1 -0
  31. data/lib/rubocop/cop/performance/redundant_split_regexp_argument.rb +64 -0
  32. data/lib/rubocop/cop/performance/redundant_string_chars.rb +2 -6
  33. data/lib/rubocop/cop/performance/reverse_each.rb +7 -10
  34. data/lib/rubocop/cop/performance/reverse_first.rb +1 -0
  35. data/lib/rubocop/cop/performance/size.rb +1 -0
  36. data/lib/rubocop/cop/performance/squeeze.rb +2 -1
  37. data/lib/rubocop/cop/performance/start_with.rb +1 -0
  38. data/lib/rubocop/cop/performance/string_include.rb +2 -1
  39. data/lib/rubocop/cop/performance/string_replacement.rb +1 -0
  40. data/lib/rubocop/cop/performance/sum.rb +123 -15
  41. data/lib/rubocop/cop/performance/times_map.rb +1 -0
  42. data/lib/rubocop/cop/performance/unfreeze_string.rb +19 -1
  43. data/lib/rubocop/cop/performance/uri_default_parser.rb +1 -0
  44. data/lib/rubocop/cop/performance_cops.rb +6 -0
  45. data/lib/rubocop/performance/version.rb +6 -1
  46. metadata +25 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1f4841e8542a1f77f21d30ffe7c26019b9f2fb7308a985780506f1920146045
4
- data.tar.gz: ed95379cd20af662ce6ce9ce4e64721ca156a6d7b62e511774df9d6abe3c9bae
3
+ metadata.gz: 026fa40bed33c9e7867599f1024ddf6c1ab430b00beae5f5ec52d9b32f5adef9
4
+ data.tar.gz: d0997dd1a5255e217f5faf2adc4f27b64a43266ff24264aa80307b474cbfa936
5
5
  SHA512:
6
- metadata.gz: 10a1e89448abd75234807bbdd9be67b10e4b9a837d2d2d711adeb0a0d105b921d722dfcac0c46e30f55f278afc2960f888407959543097d81df4f1aa3667fac6
7
- data.tar.gz: 668784bfe08775a1e8917806ef9f34d7dcb3f7581a9080ea14a44a54b43cf7e6516db4f5c4e015718824c32a8b045f43b429c8e1d3864960f41ead8684fbb5f5
6
+ metadata.gz: 7a09ace7478bf3d87dc35897bb677c802c6b191ce0206e1b94e04826091ce8075ad9f267cd7805cd68ba3476c679e3cdb59c219fd71760ac4756c26a209c51bc
7
+ data.tar.gz: f9ccef454d4c9ff9a8d67db5276d2249d03a2882b169abfc5d4bacc28f9f09391edb15100750480c354fe9cc161463156169f216d482c9504f7d6a17da524a0c
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