rubocop-performance 1.7.1 → 1.9.2

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -0
  3. data/config/default.yml +46 -7
  4. data/lib/rubocop/cop/mixin/regexp_metacharacter.rb +4 -4
  5. data/lib/rubocop/cop/performance/ancestors_include.rb +15 -13
  6. data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +77 -0
  7. data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +8 -12
  8. data/lib/rubocop/cop/performance/bind_call.rb +9 -18
  9. data/lib/rubocop/cop/performance/block_given_with_explicit_block.rb +52 -0
  10. data/lib/rubocop/cop/performance/caller.rb +14 -15
  11. data/lib/rubocop/cop/performance/case_when_splat.rb +18 -11
  12. data/lib/rubocop/cop/performance/casecmp.rb +13 -20
  13. data/lib/rubocop/cop/performance/chain_array_allocation.rb +24 -28
  14. data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +140 -0
  15. data/lib/rubocop/cop/performance/compare_with_block.rb +10 -21
  16. data/lib/rubocop/cop/performance/constant_regexp.rb +68 -0
  17. data/lib/rubocop/cop/performance/count.rb +14 -16
  18. data/lib/rubocop/cop/performance/delete_prefix.rb +14 -22
  19. data/lib/rubocop/cop/performance/delete_suffix.rb +14 -22
  20. data/lib/rubocop/cop/performance/detect.rb +65 -32
  21. data/lib/rubocop/cop/performance/double_start_end_with.rb +16 -24
  22. data/lib/rubocop/cop/performance/end_with.rb +9 -13
  23. data/lib/rubocop/cop/performance/fixed_size.rb +2 -1
  24. data/lib/rubocop/cop/performance/flat_map.rb +21 -22
  25. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +15 -14
  26. data/lib/rubocop/cop/performance/io_readlines.rb +27 -42
  27. data/lib/rubocop/cop/performance/method_object_as_block.rb +32 -0
  28. data/lib/rubocop/cop/performance/open_struct.rb +3 -2
  29. data/lib/rubocop/cop/performance/range_include.rb +8 -6
  30. data/lib/rubocop/cop/performance/redundant_block_call.rb +15 -10
  31. data/lib/rubocop/cop/performance/redundant_match.rb +12 -6
  32. data/lib/rubocop/cop/performance/redundant_merge.rb +19 -17
  33. data/lib/rubocop/cop/performance/redundant_sort_block.rb +6 -16
  34. data/lib/rubocop/cop/performance/redundant_string_chars.rb +10 -18
  35. data/lib/rubocop/cop/performance/regexp_match.rb +20 -20
  36. data/lib/rubocop/cop/performance/reverse_each.rb +13 -12
  37. data/lib/rubocop/cop/performance/reverse_first.rb +5 -10
  38. data/lib/rubocop/cop/performance/size.rb +7 -6
  39. data/lib/rubocop/cop/performance/sort_reverse.rb +6 -15
  40. data/lib/rubocop/cop/performance/squeeze.rb +8 -11
  41. data/lib/rubocop/cop/performance/start_with.rb +9 -13
  42. data/lib/rubocop/cop/performance/string_include.rb +11 -14
  43. data/lib/rubocop/cop/performance/string_replacement.rb +24 -27
  44. data/lib/rubocop/cop/performance/sum.rb +236 -0
  45. data/lib/rubocop/cop/performance/times_map.rb +12 -18
  46. data/lib/rubocop/cop/performance/unfreeze_string.rb +20 -2
  47. data/lib/rubocop/cop/performance/uri_default_parser.rb +7 -12
  48. data/lib/rubocop/cop/performance_cops.rb +6 -0
  49. data/lib/rubocop/performance/version.rb +6 -1
  50. metadata +25 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 34605a937c1a3e040428bee06e322220659c105b6e835423d9e0e73869fd83ee
4
- data.tar.gz: e3fa0103bd2518cec45334bc162e144963eef9e3d09ab9fd6bd7b2d35b654e84
3
+ metadata.gz: 30b8495304f86e7ef0e9a7f11e12a30ab7986fb4b12cb1921870f31a1b37a42f
4
+ data.tar.gz: 1863b8766efff5dd99b2e4f2dbb4e16191f223d12dbfd51826a837f1f75eef08
5
5
  SHA512:
6
- metadata.gz: 5fe72ab5a0370aa8e0cc434080a27a38d8979084c53c50cd18e8cbc0896572ddc9d011130d56283f9d588268253ec0e2e44fc32b81483e1458689c60c17e835b
7
- data.tar.gz: a60d627b0d1ded2fb6103a78fb4414fc1e1f1ff13ab9f4e82c399289c9a40c3c6e0385ee2509074175bd724755f768dd4625bb9791af993032a1f840a0533ef6
6
+ metadata.gz: a6071b0e37b9a9683f1992b88b36e0911004e0ec8fc4b41f78ba569ce396d96840acb0eff937d519d132917dec1b0a5af9fe4d1563d743e293ee78239a59b13f
7
+ data.tar.gz: 74c457f9d7f7ef240a2fa483100ff1120fc4edba7bb64e999f78a85e106621cba4ddef1a2dae7f375f9541d021a0fa08f054edf83ebd1646423d214b4677e212
data/README.md CHANGED
@@ -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).
@@ -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-hq/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: >-
@@ -49,23 +64,33 @@ Performance/ChainArrayAllocation:
49
64
  Enabled: false
50
65
  VersionAdded: '0.59'
51
66
 
67
+ Performance/CollectionLiteralInLoop:
68
+ Description: 'Extract Array and Hash literals outside of loops into local variables or constants.'
69
+ Enabled: 'pending'
70
+ VersionAdded: '1.8'
71
+ # Min number of elements to consider an offense
72
+ MinSize: 1
73
+
52
74
  Performance/CompareWithBlock:
53
75
  Description: 'Use `sort_by(&:foo)` instead of `sort { |a, b| a.foo <=> b.foo }`.'
54
76
  Enabled: true
55
77
  VersionAdded: '0.46'
56
78
 
79
+ Performance/ConstantRegexp:
80
+ Description: 'Finds regular expressions with dynamic components that are all constants.'
81
+ Enabled: pending
82
+ VersionAdded: '1.9'
83
+
57
84
  Performance/Count:
58
85
  Description: >-
59
- Use `count` instead of `select...size`, `reject...size`,
60
- `select...count`, `reject...count`, `select...length`,
61
- and `reject...length`.
86
+ Use `count` instead of `{select,find_all,filter,reject}...{size,count,length}`.
62
87
  # This cop has known compatibility issues with `ActiveRecord` and other
63
88
  # frameworks. ActiveRecord's `count` ignores the block that is passed to it.
64
89
  # For more information, see the documentation in the cop itself.
65
90
  SafeAutoCorrect: false
66
91
  Enabled: true
67
92
  VersionAdded: '0.31'
68
- VersionChanged: '1.5'
93
+ VersionChanged: '1.8'
69
94
 
70
95
  Performance/DeletePrefix:
71
96
  Description: 'Use `delete_prefix` instead of `gsub`.'
@@ -81,8 +106,8 @@ Performance/DeleteSuffix:
81
106
 
82
107
  Performance/Detect:
83
108
  Description: >-
84
- Use `detect` instead of `select.first`, `find_all.first`,
85
- `select.last`, and `find_all.last`.
109
+ Use `detect` instead of `select.first`, `find_all.first`, `filter.first`,
110
+ `select.last`, `find_all.last`, and `filter.last`.
86
111
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code'
87
112
  # This cop has known compatibility issues with `ActiveRecord` and other
88
113
  # frameworks. `ActiveRecord` does not implement a `detect` method and `find`
@@ -91,7 +116,7 @@ Performance/Detect:
91
116
  SafeAutoCorrect: false
92
117
  Enabled: true
93
118
  VersionAdded: '0.30'
94
- VersionChanged: '1.5'
119
+ VersionChanged: '1.8'
95
120
 
96
121
  Performance/DoubleStartEndWith:
97
122
  Description: >-
@@ -149,6 +174,12 @@ Performance/IoReadlines:
149
174
  Enabled: false
150
175
  VersionAdded: '1.7'
151
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
+
152
183
  Performance/OpenStruct:
153
184
  Description: 'Use `Struct` instead of `OpenStruct`.'
154
185
  Enabled: false
@@ -261,6 +292,12 @@ Performance/StringReplacement:
261
292
  Enabled: true
262
293
  VersionAdded: '0.33'
263
294
 
295
+ Performance/Sum:
296
+ Description: 'Use `sum` instead of a custom array summation.'
297
+ Reference: 'https://blog.bigbinary.com/2016/11/02/ruby-2-4-introduces-enumerable-sum.html'
298
+ Enabled: 'pending'
299
+ VersionAdded: '1.8'
300
+
264
301
  Performance/TimesMap:
265
302
  Description: 'Checks for .times.map calls.'
266
303
  AutoCorrect: false
@@ -272,7 +309,9 @@ Performance/TimesMap:
272
309
  Performance/UnfreezeString:
273
310
  Description: 'Use unary plus to get an unfrozen string literal.'
274
311
  Enabled: true
312
+ SafeAutoCorrect: false
275
313
  VersionAdded: '0.50'
314
+ VersionChanged: '1.9'
276
315
 
277
316
  Performance/UriDefaultParser:
278
317
  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)
@@ -13,33 +13,35 @@ module RuboCop
13
13
  # # good
14
14
  # A <= B
15
15
  #
16
- class AncestorsInclude < Cop
16
+ class AncestorsInclude < Base
17
17
  include RangeHelp
18
+ extend AutoCorrector
18
19
 
19
20
  MSG = 'Use `<=` instead of `ancestors.include?`.'
21
+ RESTRICT_ON_SEND = %i[include?].freeze
20
22
 
21
23
  def_node_matcher :ancestors_include_candidate?, <<~PATTERN
22
24
  (send (send $_subclass :ancestors) :include? $_superclass)
23
25
  PATTERN
24
26
 
25
27
  def on_send(node)
26
- return unless ancestors_include_candidate?(node)
28
+ return unless (subclass, superclass = ancestors_include_candidate?(node))
29
+ return if subclass && !subclass.const_type?
27
30
 
28
- location_of_ancestors = node.children[0].loc.selector.begin_pos
29
- end_location = node.loc.selector.end_pos
30
- range = range_between(location_of_ancestors, end_location)
31
+ add_offense(range(node)) do |corrector|
32
+ subclass_source = subclass ? subclass.source : 'self'
31
33
 
32
- add_offense(node, location: range)
34
+ corrector.replace(node, "#{subclass_source} <= #{superclass.source}")
35
+ end
33
36
  end
34
37
 
35
- def autocorrect(node)
36
- ancestors_include_candidate?(node) do |subclass, superclass|
37
- lambda do |corrector|
38
- subclass_source = subclass ? subclass.source : 'self'
38
+ private
39
39
 
40
- corrector.replace(node, "#{subclass_source} <= #{superclass.source}")
41
- end
42
- end
40
+ def range(node)
41
+ location_of_ancestors = node.children[0].loc.selector.begin_pos
42
+ end_location = node.loc.selector.end_pos
43
+
44
+ range_between(location_of_ancestors, end_location)
43
45
  end
44
46
  end
45
47
  end
@@ -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-hq/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
@@ -16,26 +16,22 @@ module RuboCop
16
16
  # BigDecimal('1', 2)
17
17
  # BigDecimal('1.2', 3, exception: true)
18
18
  #
19
- class BigDecimalWithNumericArgument < Cop
19
+ class BigDecimalWithNumericArgument < Base
20
+ extend AutoCorrector
21
+
20
22
  MSG = 'Convert numeric argument to string before passing to `BigDecimal`.'
23
+ RESTRICT_ON_SEND = %i[BigDecimal].freeze
21
24
 
22
25
  def_node_matcher :big_decimal_with_numeric_argument?, <<~PATTERN
23
26
  (send nil? :BigDecimal $numeric_type? ...)
24
27
  PATTERN
25
28
 
26
29
  def on_send(node)
27
- big_decimal_with_numeric_argument?(node) do |numeric|
28
- next if numeric.float_type? && specifies_precision?(node)
29
-
30
- add_offense(node, location: numeric.source_range)
31
- end
32
- end
30
+ return unless (numeric = big_decimal_with_numeric_argument?(node))
31
+ return if numeric.float_type? && specifies_precision?(node)
33
32
 
34
- def autocorrect(node)
35
- big_decimal_with_numeric_argument?(node) do |numeric|
36
- lambda do |corrector|
37
- corrector.wrap(numeric, "'", "'")
38
- end
33
+ add_offense(numeric.source_range) do |corrector|
34
+ corrector.wrap(numeric, "'", "'")
39
35
  end
40
36
  end
41
37
 
@@ -19,14 +19,16 @@ 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
27
28
 
28
29
  MSG = 'Use `bind_call(%<bind_arg>s%<comma>s%<call_args>s)` ' \
29
30
  'instead of `bind(%<bind_arg>s).call(%<call_args>s)`.'
31
+ RESTRICT_ON_SEND = %i[call].freeze
30
32
 
31
33
  def_node_matcher :bind_with_call_method?, <<~PATTERN
32
34
  (send
@@ -37,28 +39,17 @@ module RuboCop
37
39
  PATTERN
38
40
 
39
41
  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)
42
+ return unless (receiver, bind_arg, call_args_node = bind_with_call_method?(node))
53
43
 
54
44
  range = correction_range(receiver, node)
55
-
56
45
  call_args = build_call_args(call_args_node)
57
- call_args = ", #{call_args}" unless call_args.empty?
46
+ message = message(bind_arg.source, call_args)
47
+
48
+ add_offense(range, message: message) do |corrector|
49
+ call_args = ", #{call_args}" unless call_args.empty?
58
50
 
59
- replacement_method = "bind_call(#{bind_arg.source}#{call_args})"
51
+ replacement_method = "bind_call(#{bind_arg.source}#{call_args})"
60
52
 
61
- lambda do |corrector|
62
53
  corrector.replace(range, replacement_method)
63
54
  end
64
55
  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