rubocop-performance 1.11.5 → 1.13.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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/config/default.yml +21 -8
  4. data/lib/rubocop/cop/performance/ancestors_include.rb +4 -0
  5. data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +3 -1
  6. data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +23 -12
  7. data/lib/rubocop/cop/performance/block_given_with_explicit_block.rb +2 -1
  8. data/lib/rubocop/cop/performance/case_when_splat.rb +7 -5
  9. data/lib/rubocop/cop/performance/casecmp.rb +4 -2
  10. data/lib/rubocop/cop/performance/concurrent_monotonic_time.rb +42 -0
  11. data/lib/rubocop/cop/performance/count.rb +22 -13
  12. data/lib/rubocop/cop/performance/delete_prefix.rb +3 -4
  13. data/lib/rubocop/cop/performance/delete_suffix.rb +3 -4
  14. data/lib/rubocop/cop/performance/detect.rb +6 -5
  15. data/lib/rubocop/cop/performance/double_start_end_with.rb +21 -0
  16. data/lib/rubocop/cop/performance/end_with.rb +5 -0
  17. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +3 -0
  18. data/lib/rubocop/cop/performance/map_compact.rb +20 -6
  19. data/lib/rubocop/cop/performance/open_struct.rb +4 -0
  20. data/lib/rubocop/cop/performance/range_include.rb +3 -2
  21. data/lib/rubocop/cop/performance/redundant_block_call.rb +1 -0
  22. data/lib/rubocop/cop/performance/redundant_equality_comparison_block.rb +3 -5
  23. data/lib/rubocop/cop/performance/redundant_merge.rb +4 -3
  24. data/lib/rubocop/cop/performance/redundant_string_chars.rb +15 -7
  25. data/lib/rubocop/cop/performance/start_with.rb +5 -0
  26. data/lib/rubocop/cop/performance/string_identifier_argument.rb +62 -0
  27. data/lib/rubocop/cop/performance/string_include.rb +2 -1
  28. data/lib/rubocop/cop/performance/sum.rb +47 -23
  29. data/lib/rubocop/cop/performance/times_map.rb +12 -0
  30. data/lib/rubocop/cop/performance/unfreeze_string.rb +5 -5
  31. data/lib/rubocop/cop/performance_cops.rb +2 -0
  32. data/lib/rubocop/performance/version.rb +1 -1
  33. metadata +7 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4fffd38c1ed7da6f4ff4f67b3f1d7d08bc60e8871dc22d4ac266de6facc04fcf
4
- data.tar.gz: e0de1a9ec946e37b2654390d4b46a0b9f0e1afbc37fec464c6fd78001ad5e509
3
+ metadata.gz: ee80f72f854adad1ef3a54295f5b732578373d11d19eae5b7ea0386bd2fa0f25
4
+ data.tar.gz: da36ad6040d986c56430965521ee1e597d3bc0919fa9c77ec1e34e5ca2cf43d6
5
5
  SHA512:
6
- metadata.gz: 4225a879e2e0421438be5d17bb38d982901709c809ed7ccda3a0095affaf77a707c5fb5657ee61b123546860f1e9f0bcf783ed706b8b17fba9114588a16b4653
7
- data.tar.gz: 3af998bbda7046ae5a55f06bb1ecc941dff48901d94a791fd81963cc1d76adf39524600c7ff4633c3dc06f925a2d8ea7f57716f51de2079e971c10b6194e40bb
6
+ metadata.gz: 255bba802b4023ec8be39476cc06c7614a4f7cbce11260a608f4c5c479f6e3d9b41ad6a286f8d42cb805ae65995d3bb29f46a746080dde0fa121f3a269cfaeb9
7
+ data.tar.gz: 24bffb35ce7441354bb42920d3ec49a07fa005edd4847bdd81066d22beda71dbb186e5e82205c6ad181372be1963e431751466560345068d995400f237297fb8
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-21 Bozhidar Batsov
1
+ Copyright (c) 2012-22 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/config/default.yml CHANGED
@@ -17,7 +17,7 @@ Performance/ArraySemiInfiniteRangeSlice:
17
17
  VersionAdded: '1.9'
18
18
 
19
19
  Performance/BigDecimalWithNumericArgument:
20
- Description: 'Convert numeric argument to string before passing to BigDecimal.'
20
+ Description: 'Convert numeric literal to string and pass it to `BigDecimal`.'
21
21
  Enabled: 'pending'
22
22
  VersionAdded: '1.7'
23
23
 
@@ -43,10 +43,9 @@ Performance/CaseWhenSplat:
43
43
  Reordering `when` conditions with a splat to the end
44
44
  of the `when` branches can improve performance.
45
45
  Enabled: false
46
- AutoCorrect: false
47
46
  SafeAutoCorrect: false
48
47
  VersionAdded: '0.34'
49
- VersionChanged: '0.59'
48
+ VersionChanged: '1.13'
50
49
 
51
50
  Performance/Casecmp:
52
51
  Description: >-
@@ -76,6 +75,12 @@ Performance/CompareWithBlock:
76
75
  Enabled: true
77
76
  VersionAdded: '0.46'
78
77
 
78
+ Performance/ConcurrentMonotonicTime:
79
+ Description: 'Use `Process.clock_gettime(Process::CLOCK_MONOTONIC)` instead of `Concurrent.monotonic_time`.'
80
+ Reference: 'https://github.com/rails/rails/pull/43502'
81
+ Enabled: pending
82
+ VersionAdded: '1.12'
83
+
79
84
  Performance/ConstantRegexp:
80
85
  Description: 'Finds regular expressions with dynamic components that are all constants.'
81
86
  Enabled: pending
@@ -131,7 +136,7 @@ Performance/DoubleStartEndWith:
131
136
  VersionAdded: '0.36'
132
137
  VersionChanged: '0.48'
133
138
  # Used to check for `starts_with?` and `ends_with?`.
134
- # These methods are defined by `ActiveSupport`.
139
+ # These methods are defined by Active Support.
135
140
  IncludeActiveSupportAliases: false
136
141
 
137
142
  Performance/EndWith:
@@ -306,12 +311,17 @@ Performance/StartWith:
306
311
  VersionAdded: '0.36'
307
312
  VersionChanged: '1.10'
308
313
 
314
+ Performance/StringIdentifierArgument:
315
+ Description: 'Use symbol identifier argument instead of string identifier argument.'
316
+ Enabled: pending
317
+ VersionAdded: '1.13'
318
+
309
319
  Performance/StringInclude:
310
320
  Description: 'Use `String#include?` instead of a regex match with literal-only pattern.'
311
321
  Enabled: 'pending'
312
- AutoCorrect: false
313
322
  SafeAutoCorrect: false
314
323
  VersionAdded: '1.7'
324
+ VersionChanged: '1.12'
315
325
 
316
326
  Performance/StringReplacement:
317
327
  Description: >-
@@ -324,17 +334,20 @@ Performance/StringReplacement:
324
334
 
325
335
  Performance/Sum:
326
336
  Description: 'Use `sum` instead of a custom array summation.'
337
+ SafeAutoCorrect: false
327
338
  Reference: 'https://blog.bigbinary.com/2016/11/02/ruby-2-4-introduces-enumerable-sum.html'
328
339
  Enabled: 'pending'
329
340
  VersionAdded: '1.8'
341
+ VersionChanged: '1.13'
342
+ OnlySumOrWithInitialValue: false
330
343
 
331
344
  Performance/TimesMap:
332
345
  Description: 'Checks for .times.map calls.'
333
- AutoCorrect: false
334
346
  Enabled: true
347
+ # See https://github.com/rubocop/rubocop/issues/4658
348
+ SafeAutoCorrect: false
335
349
  VersionAdded: '0.36'
336
- VersionChanged: '0.50'
337
- SafeAutoCorrect: false # see https://github.com/rubocop/rubocop/issues/4658
350
+ VersionChanged: '1.13'
338
351
 
339
352
  Performance/UnfreezeString:
340
353
  Description: 'Use unary plus to get an unfrozen string literal.'
@@ -6,6 +6,10 @@ module RuboCop
6
6
  # This cop is used to identify usages of `ancestors.include?` and
7
7
  # change them to use `<=` instead.
8
8
  #
9
+ # @safety
10
+ # This cop is unsafe because it can't tell whether the receiver is a class or an object.
11
+ # e.g. the false positive was for `Nokogiri::XML::Node#ancestors`.
12
+ #
9
13
  # @example
10
14
  # # bad
11
15
  # A.ancestors.include?(B)
@@ -7,7 +7,9 @@ module RuboCop
7
7
  # can be replaced by `Array#take` and `Array#drop`.
8
8
  # This cop was created due to a mistake in microbenchmark and hence is disabled by default.
9
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.
10
+ #
11
+ # @safety
12
+ # This cop is unsafe for string slices because strings do not have `#take` and `#drop` methods.
11
13
  #
12
14
  # @example
13
15
  # # bad
@@ -10,36 +10,47 @@ module RuboCop
10
10
  # @example
11
11
  # # bad
12
12
  # BigDecimal(1, 2)
13
+ # 4.to_d(6)
13
14
  # BigDecimal(1.2, 3, exception: true)
15
+ # 4.5.to_d(6, exception: true)
14
16
  #
15
17
  # # good
16
18
  # BigDecimal('1', 2)
19
+ # BigDecimal('4', 6)
17
20
  # BigDecimal('1.2', 3, exception: true)
21
+ # BigDecimal('4.5', 6, exception: true)
18
22
  #
19
23
  class BigDecimalWithNumericArgument < Base
20
24
  extend AutoCorrector
21
25
 
22
- MSG = 'Convert numeric argument to string before passing to `BigDecimal`.'
23
- RESTRICT_ON_SEND = %i[BigDecimal].freeze
26
+ MSG = 'Convert numeric literal to string and pass it to `BigDecimal`.'
27
+ RESTRICT_ON_SEND = %i[BigDecimal to_d].freeze
24
28
 
25
29
  def_node_matcher :big_decimal_with_numeric_argument?, <<~PATTERN
26
30
  (send nil? :BigDecimal $numeric_type? ...)
27
31
  PATTERN
28
32
 
33
+ def_node_matcher :to_d?, <<~PATTERN
34
+ (send [!nil? $numeric_type?] :to_d ...)
35
+ PATTERN
36
+
29
37
  def on_send(node)
30
- return unless (numeric = big_decimal_with_numeric_argument?(node))
31
- return if numeric.float_type? && specifies_precision?(node)
38
+ if (numeric = big_decimal_with_numeric_argument?(node))
39
+ add_offense(numeric.source_range) do |corrector|
40
+ corrector.wrap(numeric, "'", "'")
41
+ end
42
+ elsif (numeric_to_d = to_d?(node))
43
+ add_offense(numeric_to_d.source_range) do |corrector|
44
+ big_decimal_args = node
45
+ .arguments
46
+ .map(&:source)
47
+ .unshift("'#{numeric_to_d.source}'")
48
+ .join(', ')
32
49
 
33
- add_offense(numeric.source_range) do |corrector|
34
- corrector.wrap(numeric, "'", "'")
50
+ corrector.replace(node, "BigDecimal(#{big_decimal_args})")
51
+ end
35
52
  end
36
53
  end
37
-
38
- private
39
-
40
- def specifies_precision?(node)
41
- node.arguments.size > 1 && !node.arguments[1].hash_type?
42
- end
43
54
  end
44
55
  end
45
56
  end
@@ -38,8 +38,9 @@ module RuboCop
38
38
 
39
39
  block_arg = def_node.arguments.find(&:blockarg_type?)
40
40
  return unless block_arg
41
+ return unless (block_arg_name = block_arg.loc.name)
41
42
 
42
- block_arg_name = block_arg.loc.name.source.to_sym
43
+ block_arg_name = block_arg_name.source.to_sym
43
44
  return if reassigns_block_arg?(def_node, block_arg_name)
44
45
 
45
46
  add_offense(node) do |corrector|
@@ -17,11 +17,13 @@ module RuboCop
17
17
  # this defining a higher level when condition to override a condition
18
18
  # that is inside of the splat expansion.
19
19
  #
20
- # This is not a guaranteed performance improvement. If the data being
21
- # processed by the `case` condition is normalized in a manner that favors
22
- # hitting a condition in the splat expansion, it is possible that
23
- # moving the splat condition to the end will use more memory,
24
- # and run slightly slower.
20
+ # @safety
21
+ # This cop is not unsafe auto-correction because it is not a guaranteed
22
+ # performance improvement. If the data being processed by the `case` condition is
23
+ # normalized in a manner that favors hitting a condition in the splat expansion,
24
+ # it is possible that moving the splat condition to the end will use more memory,
25
+ # and run slightly slower.
26
+ # See for more details: https://github.com/rubocop/rubocop/pull/6163
25
27
  #
26
28
  # @example
27
29
  # # bad
@@ -5,8 +5,10 @@ module RuboCop
5
5
  module Performance
6
6
  # This cop identifies places where a case-insensitive string comparison
7
7
  # can better be implemented using `casecmp`.
8
- # This cop is unsafe because `String#casecmp` and `String#casecmp?` behave
9
- # differently when using Non-ASCII characters.
8
+ #
9
+ # @safety
10
+ # This cop is unsafe because `String#casecmp` and `String#casecmp?` behave
11
+ # differently when using Non-ASCII characters.
10
12
  #
11
13
  # @example
12
14
  # # bad
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop identifies places where `Concurrent.monotonic_time`
7
+ # can be replaced by `Process.clock_gettime(Process::CLOCK_MONOTONIC)`.
8
+ #
9
+ # @example
10
+ #
11
+ # # bad
12
+ # Concurrent.monotonic_time
13
+ #
14
+ # # good
15
+ # Process.clock_gettime(Process::CLOCK_MONOTONIC)
16
+ #
17
+ class ConcurrentMonotonicTime < Base
18
+ extend AutoCorrector
19
+
20
+ MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
21
+ RESTRICT_ON_SEND = %i[monotonic_time].freeze
22
+
23
+ def_node_matcher :concurrent_monotonic_time?, <<~PATTERN
24
+ (send
25
+ (const {nil? cbase} :Concurrent) :monotonic_time ...)
26
+ PATTERN
27
+
28
+ def on_send(node)
29
+ return unless concurrent_monotonic_time?(node)
30
+
31
+ optional_unit_parameter = ", #{node.first_argument.source}" if node.first_argument
32
+ prefer = "Process.clock_gettime(Process::CLOCK_MONOTONIC#{optional_unit_parameter})"
33
+ message = format(MSG, prefer: prefer, current: node.source)
34
+
35
+ add_offense(node, message: message) do |corrector|
36
+ corrector.replace(node, prefer)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -7,6 +7,28 @@ module RuboCop
7
7
  # follow calls to `select`, `find_all`, `filter` or `reject`. Querying logic can instead be
8
8
  # passed to the `count` call.
9
9
  #
10
+ # @safety
11
+ # This cop is unsafe because it has known compatibility issues with `ActiveRecord` and other
12
+ # frameworks. ActiveRecord's `count` ignores the block that is passed to it.
13
+ # `ActiveRecord` will ignore the block that is passed to `count`.
14
+ # Other methods, such as `select`, will convert the association to an
15
+ # array and then run the block on the array. A simple work around to
16
+ # make `count` work with a block is to call `to_a.count {...}`.
17
+ #
18
+ # For example:
19
+ #
20
+ # [source,ruby]
21
+ # ----
22
+ # `Model.where(id: [1, 2, 3]).select { |m| m.method == true }.size`
23
+ # ----
24
+ #
25
+ # becomes:
26
+ #
27
+ # [source,ruby]
28
+ # ----
29
+ # `Model.where(id: [1, 2, 3]).to_a.count { |m| m.method == true }`
30
+ # ----
31
+ #
10
32
  # @example
11
33
  # # bad
12
34
  # [1, 2, 3].select { |e| e > 2 }.size
@@ -24,19 +46,6 @@ module RuboCop
24
46
  # [1, 2, 3].count { |e| e < 2 && e.even? }
25
47
  # Model.select('field AS field_one').count
26
48
  # Model.select(:value).count
27
- #
28
- # `ActiveRecord` compatibility:
29
- # `ActiveRecord` will ignore the block that is passed to `count`.
30
- # Other methods, such as `select`, will convert the association to an
31
- # array and then run the block on the array. A simple work around to
32
- # make `count` work with a block is to call `to_a.count {...}`.
33
- #
34
- # Example:
35
- # `Model.where(id: [1, 2, 3]).select { |m| m.method == true }.size`
36
- #
37
- # becomes:
38
- #
39
- # `Model.where(id: [1, 2, 3]).to_a.count { |m| m.method == true }`
40
49
  class Count < Base
41
50
  include RangeHelp
42
51
  extend AutoCorrector
@@ -7,7 +7,6 @@ module RuboCop
7
7
  #
8
8
  # This cop identifies places where `gsub(/\Aprefix/, '')` and `sub(/\Aprefix/, '')`
9
9
  # can be replaced by `delete_prefix('prefix')`.
10
- # It is marked as unsafe by default because `Pathname` has `sub` but not `delete_prefix`.
11
10
  #
12
11
  # This cop has `SafeMultiline` configuration option that `true` by default because
13
12
  # `^prefix` is unsafe as it will behave incompatible with `delete_prefix`
@@ -15,6 +14,9 @@ module RuboCop
15
14
  #
16
15
  # The `delete_prefix('prefix')` method is faster than `gsub(/\Aprefix/, '')`.
17
16
  #
17
+ # @safety
18
+ # This cop is unsafe because `Pathname` has `sub` but not `delete_prefix`.
19
+ #
18
20
  # @example
19
21
  #
20
22
  # # bad
@@ -47,9 +49,6 @@ module RuboCop
47
49
  class DeletePrefix < Base
48
50
  include RegexpMetacharacter
49
51
  extend AutoCorrector
50
- extend TargetRubyVersion
51
-
52
- minimum_target_ruby_version 2.5
53
52
 
54
53
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
55
54
  RESTRICT_ON_SEND = %i[gsub gsub! sub sub!].freeze
@@ -7,7 +7,6 @@ module RuboCop
7
7
  #
8
8
  # This cop identifies places where `gsub(/suffix\z/, '')` and `sub(/suffix\z/, '')`
9
9
  # can be replaced by `delete_suffix('suffix')`.
10
- # It is marked as unsafe by default because `Pathname` has `sub` but not `delete_suffix`.
11
10
  #
12
11
  # This cop has `SafeMultiline` configuration option that `true` by default because
13
12
  # `suffix$` is unsafe as it will behave incompatible with `delete_suffix?`
@@ -15,6 +14,9 @@ module RuboCop
15
14
  #
16
15
  # The `delete_suffix('suffix')` method is faster than `gsub(/suffix\z/, '')`.
17
16
  #
17
+ # @safety
18
+ # This cop is unsafe because `Pathname` has `sub` but not `delete_suffix`.
19
+ #
18
20
  # @example
19
21
  #
20
22
  # # bad
@@ -47,9 +49,6 @@ module RuboCop
47
49
  class DeleteSuffix < Base
48
50
  include RegexpMetacharacter
49
51
  extend AutoCorrector
50
- extend TargetRubyVersion
51
-
52
- minimum_target_ruby_version 2.5
53
52
 
54
53
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
55
54
  RESTRICT_ON_SEND = %i[gsub gsub! sub sub!].freeze
@@ -7,6 +7,11 @@ module RuboCop
7
7
  # chained to `select`, `find_all` or `filter` and change them to use
8
8
  # `detect` instead.
9
9
  #
10
+ # @safety
11
+ # This cop is unsafe because is has known compatibility issues with `ActiveRecord` and other
12
+ # frameworks. `ActiveRecord` does not implement a `detect` method and `find` has its own
13
+ # meaning. Correcting `ActiveRecord` methods with this cop should be considered unsafe.
14
+ #
10
15
  # @example
11
16
  # # bad
12
17
  # [].select { |item| true }.first
@@ -22,10 +27,6 @@ module RuboCop
22
27
  # [].detect { |item| true }
23
28
  # [].reverse.detect { |item| true }
24
29
  #
25
- # `ActiveRecord` compatibility:
26
- # `ActiveRecord` does not implement a `detect` method and `find` has its
27
- # own meaning. Correcting ActiveRecord methods with this cop should be
28
- # considered unsafe.
29
30
  class Detect < Base
30
31
  extend AutoCorrector
31
32
 
@@ -90,7 +91,7 @@ module RuboCop
90
91
  end
91
92
 
92
93
  def replacement(method, index)
93
- if method == :last || method == :[] && index == -1
94
+ if method == :last || (method == :[] && index == -1)
94
95
  "reverse.#{preferred_method}"
95
96
  else
96
97
  preferred_method
@@ -7,6 +7,9 @@ module RuboCop
7
7
  # separated by `||`. In some cases such calls can be replaced
8
8
  # with an single `#start_with?`/`#end_with?` call.
9
9
  #
10
+ # `IncludeActiveSupportAliases` configuration option is used to check for
11
+ # `starts_with?` and `ends_with?`. These methods are defined by Active Support.
12
+ #
10
13
  # @example
11
14
  # # bad
12
15
  # str.start_with?("a") || str.start_with?(Some::CONST)
@@ -17,6 +20,24 @@ module RuboCop
17
20
  # str.start_with?("a", Some::CONST)
18
21
  # str.start_with?("a", "b", "c")
19
22
  # str.end_with?(var1, var2)
23
+ #
24
+ # @example IncludeActiveSupportAliases: false (default)
25
+ # # good
26
+ # str.starts_with?("a", "b") || str.starts_with?("c")
27
+ # str.ends_with?(var1) || str.ends_with?(var2)
28
+ #
29
+ # str.starts_with?("a", "b", "c")
30
+ # str.ends_with?(var1, var2)
31
+ #
32
+ # @example IncludeActiveSupportAliases: true
33
+ # # bad
34
+ # str.starts_with?("a", "b") || str.starts_with?("c")
35
+ # str.ends_with?(var1) || str.ends_with?(var2)
36
+ #
37
+ # # good
38
+ # str.starts_with?("a", "b", "c")
39
+ # str.ends_with?(var1, var2)
40
+ #
20
41
  class DoubleStartEndWith < Base
21
42
  extend AutoCorrector
22
43
 
@@ -9,6 +9,11 @@ module RuboCop
9
9
  # `end$` is unsafe as it will behave incompatible with `end_with?`
10
10
  # for receiver is multiline string.
11
11
  #
12
+ # @safety
13
+ # This will change to a new method call which isn't guaranteed to be on the
14
+ # object. Switching these methods has to be done with knowledge of the types
15
+ # of the variables which rubocop doesn't have.
16
+ #
12
17
  # @example
13
18
  # # bad
14
19
  # 'abc'.match?(/bc\Z/)
@@ -15,6 +15,9 @@ module RuboCop
15
15
  # both perform an O(n) search through all of the values, calling `values`
16
16
  # allocates a new array while using `value?` does not.
17
17
  #
18
+ # @safety
19
+ # This cop is unsafe because it can't tell whether the receiver is a hash object.
20
+ #
18
21
  # @example
19
22
  # # bad
20
23
  # { a: 1, b: 2 }.keys.include?(:a)
@@ -6,8 +6,10 @@ module RuboCop
6
6
  # In Ruby 2.7, `Enumerable#filter_map` has been added.
7
7
  #
8
8
  # This cop identifies places where `map { ... }.compact` can be replaced by `filter_map`.
9
- # It is marked as unsafe auto-correction by default because `map { ... }.compact`
10
- # that is not compatible with `filter_map`.
9
+ #
10
+ # @safety
11
+ # This cop's autocorrection is unsafe because `map { ... }.compact` that is not
12
+ # compatible with `filter_map`.
11
13
  #
12
14
  # [source,ruby]
13
15
  # ----
@@ -56,19 +58,19 @@ module RuboCop
56
58
 
57
59
  add_offense(range) do |corrector|
58
60
  corrector.replace(map_node.loc.selector, 'filter_map')
59
- remove_compact_method(corrector, node)
61
+ remove_compact_method(corrector, node, node.parent)
60
62
  end
61
63
  end
62
64
 
63
65
  private
64
66
 
65
- def remove_compact_method(corrector, compact_node)
66
- chained_method = compact_node.parent
67
+ def remove_compact_method(corrector, compact_node, chained_method)
67
68
  compact_method_range = compact_node.loc.selector
68
69
 
69
70
  if compact_node.multiline? && chained_method&.loc.respond_to?(:selector) && chained_method.dot? &&
71
+ !map_method_and_compact_method_on_same_line?(compact_node) &&
70
72
  !invoke_method_after_map_compact_on_same_line?(compact_node, chained_method)
71
- compact_method_range = range_by_whole_lines(compact_method_range, include_final_newline: true)
73
+ compact_method_range = compact_method_with_final_newline_range(compact_method_range)
72
74
  else
73
75
  corrector.remove(compact_node.loc.dot)
74
76
  end
@@ -76,9 +78,21 @@ module RuboCop
76
78
  corrector.remove(compact_method_range)
77
79
  end
78
80
 
81
+ def map_method_and_compact_method_on_same_line?(compact_node)
82
+ return false unless compact_node.children.first.respond_to?(:send_node)
83
+
84
+ map_node = compact_node.children.first.send_node
85
+
86
+ compact_node.loc.selector.line == map_node.loc.selector.line
87
+ end
88
+
79
89
  def invoke_method_after_map_compact_on_same_line?(compact_node, chained_method)
80
90
  compact_node.loc.selector.line == chained_method.loc.selector.line
81
91
  end
92
+
93
+ def compact_method_with_final_newline_range(compact_method_range)
94
+ range_by_whole_lines(compact_method_range, include_final_newline: true)
95
+ end
82
96
  end
83
97
  end
84
98
  end
@@ -11,6 +11,10 @@ module RuboCop
11
11
  # especially in case of single-threaded
12
12
  # applications with multiple `OpenStruct` instantiations.
13
13
  #
14
+ # @safety
15
+ # This cop is unsafe because `OpenStruct.new` and `Struct.new`
16
+ # are not equivalent.
17
+ #
14
18
  # @example
15
19
  # # bad
16
20
  # class MyClass
@@ -9,8 +9,9 @@ module RuboCop
9
9
  # end points of the `Range`. In a great majority of cases, this is what
10
10
  # is wanted.
11
11
  #
12
- # This cop is `Safe: false` by default because `Range#include?` (or `Range#member?`) and
13
- # `Range#cover?` are not equivalent behaviour.
12
+ # @safety
13
+ # This cop is unsafe because `Range#include?` (or `Range#member?`) and `Range#cover?`
14
+ # are not equivalent behaviour.
14
15
  #
15
16
  # @example
16
17
  # # bad
@@ -55,6 +55,7 @@ module RuboCop
55
55
  end
56
56
  end
57
57
  end
58
+ alias on_defs on_def
58
59
 
59
60
  private
60
61
 
@@ -9,8 +9,9 @@ module RuboCop
9
9
  # By default, `Object#===` behaves the same as `Object#==`, but this
10
10
  # behavior is appropriately overridden in subclass. For example,
11
11
  # `Range#===` returns `true` when argument is within the range.
12
- # Therefore, It is marked as unsafe by default because `===` and `==`
13
- # do not always behave the same.
12
+ #
13
+ # @safety
14
+ # This cop is unsafe because `===` and `==` do not always behave the same.
14
15
  #
15
16
  # @example
16
17
  # # bad
@@ -24,9 +25,6 @@ module RuboCop
24
25
  #
25
26
  class RedundantEqualityComparisonBlock < Base
26
27
  extend AutoCorrector
27
- extend TargetRubyVersion
28
-
29
- minimum_target_ruby_version 2.5
30
28
 
31
29
  MSG = 'Use `%<prefer>s` instead of block.'
32
30
 
@@ -8,8 +8,9 @@ module RuboCop
8
8
  # You can set the maximum number of key-value pairs to consider
9
9
  # an offense with `MaxKeyValuePairs`.
10
10
  #
11
- # This cop is marked as unsafe because RuboCop cannot determine if the
12
- # receiver of `merge!` is actually a hash or not.
11
+ # @safety
12
+ # This cop is unsafe because RuboCop cannot determine if the
13
+ # receiver of `merge!` is actually a hash or not.
13
14
  #
14
15
  # @example
15
16
  # # bad
@@ -91,7 +92,7 @@ module RuboCop
91
92
  end
92
93
 
93
94
  def non_redundant_pairs?(receiver, pairs)
94
- pairs.size > 1 && !receiver.pure? || pairs.size > max_key_value_pairs
95
+ (pairs.size > 1 && !receiver.pure?) || pairs.size > max_key_value_pairs
95
96
  end
96
97
 
97
98
  def kwsplat_used?(pairs)
@@ -82,14 +82,10 @@ module RuboCop
82
82
 
83
83
  def build_good_method(method, args)
84
84
  case method
85
- when :[], :slice
85
+ when :slice
86
86
  "[#{build_call_args(args)}].chars"
87
- when :first
88
- if args.any?
89
- "[0...#{args.first.source}].chars"
90
- else
91
- '[0]'
92
- end
87
+ when :[], :first
88
+ build_good_method_for_brackets_or_first_method(method, args)
93
89
  when :take
94
90
  "[0...#{args.first.source}].chars"
95
91
  else
@@ -97,6 +93,18 @@ module RuboCop
97
93
  end
98
94
  end
99
95
 
96
+ def build_good_method_for_brackets_or_first_method(method, args)
97
+ first_arg = args.first
98
+
99
+ if first_arg&.range_type?
100
+ "[#{build_call_args(args)}].chars"
101
+ elsif method == :first && args.any?
102
+ "[0...#{args.first.source}].chars"
103
+ else
104
+ first_arg ? "[#{first_arg.source}]" : '[0]'
105
+ end
106
+ end
107
+
100
108
  def build_bad_method(method, args)
101
109
  case method
102
110
  when :[]
@@ -9,6 +9,11 @@ module RuboCop
9
9
  # `^start` is unsafe as it will behave incompatible with `start_with?`
10
10
  # for receiver is multiline string.
11
11
  #
12
+ # @safety
13
+ # This will change to a new method call which isn't guaranteed to be on the
14
+ # object. Switching these methods has to be done with knowledge of the types
15
+ # of the variables which rubocop doesn't have.
16
+ #
12
17
  # @example
13
18
  # # bad
14
19
  # 'abc'.match?(/\Aab/)
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop identifies places where string identifier argument can be replaced
7
+ # by symbol identifier argument.
8
+ # It prevents the redundancy of the internal string-to-symbol conversion.
9
+ #
10
+ # This cop targets methods that take identifier (e.g. method name) argument
11
+ # and the following examples are parts of it.
12
+ #
13
+ # @example
14
+ #
15
+ # # bad
16
+ # send('do_something')
17
+ # attr_accessor 'do_something'
18
+ # instance_variable_get('@ivar')
19
+ #
20
+ # # good
21
+ # send(:do_something)
22
+ # attr_accessor :do_something
23
+ # instance_variable_get(:@ivar)
24
+ #
25
+ class StringIdentifierArgument < Base
26
+ extend AutoCorrector
27
+
28
+ MSG = 'Use `%<symbol_arg>s` instead of `%<string_arg>s`.'
29
+
30
+ # NOTE: `attr` method is not included in this list as it can cause false positives in Nokogiri API.
31
+ # And `attr` may not be used because `Style/Attr` registers an offense.
32
+ # https://github.com/rubocop/rubocop-performance/issues/278
33
+ RESTRICT_ON_SEND = %i[
34
+ alias_method attr_accessor attr_reader attr_writer autoload autoload?
35
+ class_variable_defined? const_defined? const_get const_set const_source_location
36
+ define_method instance_method method_defined? private_class_method? private_method_defined?
37
+ protected_method_defined? public_class_method public_instance_method public_method_defined?
38
+ remove_class_variable remove_method undef_method class_variable_get class_variable_set
39
+ deprecate_constant module_function private private_constant protected public public_constant
40
+ remove_const ruby2_keywords
41
+ define_singleton_method instance_variable_defined instance_variable_get instance_variable_set
42
+ method public_method public_send remove_instance_variable respond_to? send singleton_method
43
+ __send__
44
+ ].freeze
45
+
46
+ def on_send(node)
47
+ return unless (first_argument = node.first_argument)
48
+ return unless first_argument.str_type?
49
+ return if first_argument.value.include?(' ')
50
+
51
+ replacement = first_argument.value.to_sym.inspect
52
+
53
+ message = format(MSG, symbol_arg: replacement, string_arg: first_argument.source)
54
+
55
+ add_offense(first_argument, message: message) do |corrector|
56
+ corrector.replace(first_argument, replacement)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -6,7 +6,8 @@ module RuboCop
6
6
  # This cop identifies unnecessary use of a regex where
7
7
  # `String#include?` would suffice.
8
8
  #
9
- # This cop's offenses are not safe to auto-correct if a receiver is nil.
9
+ # @safety
10
+ # This cop's offenses are not safe to auto-correct if a receiver is nil.
10
11
  #
11
12
  # @example
12
13
  # # bad
@@ -6,35 +6,46 @@ module RuboCop
6
6
  # This cop identifies places where custom code finding the sum of elements
7
7
  # in some Enumerable object can be replaced by `Enumerable#sum` method.
8
8
  #
9
- # This cop can change auto-correction scope depending on the value of
10
- # `SafeAutoCorrect`.
11
- # Its auto-correction is marked as safe by default (`SafeAutoCorrect: true`)
12
- # to prevent `TypeError` in auto-correced code when initial value is not
13
- # specified as shown below:
9
+ # @safety
10
+ # Auto-corrections are unproblematic wherever an initial value is provided explicitly:
14
11
  #
15
- # [source,ruby]
16
- # ----
17
- # ['a', 'b'].sum # => (String can't be coerced into Integer)
18
- # ----
12
+ # [source,ruby]
13
+ # ----
14
+ # [1, 2, 3].reduce(4, :+) # => 10
15
+ # [1, 2, 3].sum(4) # => 10
19
16
  #
20
- # Therefore if initial value is not specified, unsafe auto-corrected will not occur.
17
+ # [].reduce(4, :+) # => 4
18
+ # [].sum(4) # => 4
19
+ # ----
21
20
  #
22
- # If you always want to enable auto-correction, you can set `SafeAutoCorrect: false`.
21
+ # This also holds true for non-numeric types which implement a `:+` method:
23
22
  #
24
- # [source,yaml]
25
- # ----
26
- # Performance/Sum:
27
- # SafeAutoCorrect: false
28
- # ----
23
+ # [source,ruby]
24
+ # ----
25
+ # ['l', 'o'].reduce('Hel', :+) # => "Hello"
26
+ # ['l', 'o'].sum('Hel') # => "Hello"
27
+ # ----
29
28
  #
30
- # Please note that the auto-correction command line option will be changed from
31
- # `rubocop -a` to `rubocop -A`, which includes unsafe auto-correction.
29
+ # When no initial value is provided though, `Enumerable#reduce` will pick the first enumerated value
30
+ # as initial value and successively add all following values to it, whereas
31
+ # `Enumerable#sum` will set an initial value of `0` (`Integer`) which can lead to a `TypeError`:
32
32
  #
33
- # @example
33
+ # [source,ruby]
34
+ # ----
35
+ # [].reduce(:+) # => nil
36
+ # [1, 2, 3].reduce(:+) # => 6
37
+ # ['H', 'e', 'l', 'l', 'o'].reduce(:+) # => "Hello"
38
+ #
39
+ # [].sum # => 0
40
+ # [1, 2, 3].sum # => 6
41
+ # ['H', 'e', 'l', 'l', 'o'].sum # => in `+': String can't be coerced into Integer (TypeError)
42
+ # ----
43
+ #
44
+ # @example OnlySumOrWithInitialValue: false (default)
34
45
  # # bad
35
- # [1, 2, 3].inject(:+) # These bad cases with no initial value are unsafe and
36
- # [1, 2, 3].inject(&:+) # will not be auto-correced by default. If you want to
37
- # [1, 2, 3].reduce { |acc, elem| acc + elem } # auto-corrected, you can set `SafeAutoCorrect: false`.
46
+ # [1, 2, 3].inject(:+) # Auto-corrections for cases without initial value are unsafe
47
+ # [1, 2, 3].inject(&:+) # and will only be performed when using the `-A` option.
48
+ # [1, 2, 3].reduce { |acc, elem| acc + elem } # They can be prohibited completely using `SafeAutoCorrect: true`.
38
49
  # [1, 2, 3].reduce(10, :+)
39
50
  # [1, 2, 3].map { |elem| elem ** 2 }.sum
40
51
  # [1, 2, 3].collect(&:count).sum(10)
@@ -45,6 +56,17 @@ module RuboCop
45
56
  # [1, 2, 3].sum { |elem| elem ** 2 }
46
57
  # [1, 2, 3].sum(10, &:count)
47
58
  #
59
+ # @example OnlySumOrWithInitialValue: true
60
+ # # bad
61
+ # [1, 2, 3].reduce(10, :+)
62
+ # [1, 2, 3].map { |elem| elem ** 2 }.sum
63
+ # [1, 2, 3].collect(&:count).sum(10)
64
+ #
65
+ # # good
66
+ # [1, 2, 3].sum(10)
67
+ # [1, 2, 3].sum { |elem| elem ** 2 }
68
+ # [1, 2, 3].sum(10, &:count)
69
+ #
48
70
  class Sum < Base
49
71
  include RangeHelp
50
72
  extend AutoCorrector
@@ -103,6 +125,8 @@ module RuboCop
103
125
 
104
126
  def handle_sum_candidate(node)
105
127
  sum_candidate?(node) do |method, init, operation|
128
+ next if cop_config['OnlySumOrWithInitialValue'] && init.empty?
129
+
106
130
  range = sum_method_range(node)
107
131
  message = build_method_message(node, method, init, operation)
108
132
 
@@ -156,7 +180,7 @@ module RuboCop
156
180
  end
157
181
 
158
182
  def sum_method_range(node)
159
- range_between(node.loc.selector.begin_pos, node.loc.end.end_pos)
183
+ range_between(node.loc.selector.begin_pos, node.loc.expression.end_pos)
160
184
  end
161
185
 
162
186
  def sum_map_range(map, sum)
@@ -7,6 +7,18 @@ module RuboCop
7
7
  # In most cases such calls can be replaced
8
8
  # with an explicit array creation.
9
9
  #
10
+ # @safety
11
+ # This cop's autocorrection is unsafe because `Integer#times` does nothing if receiver is 0
12
+ # or less. However, `Array.new` raises an error if argument is less than 0.
13
+ #
14
+ # For example:
15
+ #
16
+ # [source,ruby]
17
+ # ----
18
+ # -1.times{} # does nothing
19
+ # Array.new(-1) # ArgumentError: negative array size
20
+ # ----
21
+ #
10
22
  # @example
11
23
  # # bad
12
24
  # 9.times.map do |i|
@@ -7,11 +7,11 @@ module RuboCop
7
7
  # literal instead of `String#dup` and `String.new`.
8
8
  # Unary plus operator is faster than `String#dup`.
9
9
  #
10
- # NOTE: `String.new` (without operator) is not exactly the same as `+''`.
11
- # These differ in encoding. `String.new.encoding` is always `ASCII-8BIT`.
12
- # However, `(+'').encoding` is the same as script encoding(e.g. `UTF-8`).
13
- # Therefore, auto-correction is unsafe.
14
- # So, if you expect `ASCII-8BIT` encoding, disable this cop.
10
+ # @safety
11
+ # This cop's autocorrection is unsafe because `String.new` (without operator) is not
12
+ # exactly the same as `+''`. These differ in encoding. `String.new.encoding` is always
13
+ # `ASCII-8BIT`. However, `(+'').encoding` is the same as script encoding(e.g. `UTF-8`).
14
+ # if you expect `ASCII-8BIT` encoding, disable this cop.
15
15
  #
16
16
  # @example
17
17
  # # bad
@@ -13,6 +13,7 @@ require_relative 'performance/case_when_splat'
13
13
  require_relative 'performance/casecmp'
14
14
  require_relative 'performance/collection_literal_in_loop'
15
15
  require_relative 'performance/compare_with_block'
16
+ require_relative 'performance/concurrent_monotonic_time'
16
17
  require_relative 'performance/constant_regexp'
17
18
  require_relative 'performance/count'
18
19
  require_relative 'performance/delete_prefix'
@@ -43,6 +44,7 @@ require_relative 'performance/size'
43
44
  require_relative 'performance/sort_reverse'
44
45
  require_relative 'performance/squeeze'
45
46
  require_relative 'performance/start_with'
47
+ require_relative 'performance/string_identifier_argument'
46
48
  require_relative 'performance/string_include'
47
49
  require_relative 'performance/string_replacement'
48
50
  require_relative 'performance/sum'
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Performance
5
5
  # This module holds the RuboCop Performance version information.
6
6
  module Version
7
- STRING = '1.11.5'
7
+ STRING = '1.13.2'
8
8
 
9
9
  def self.document_version
10
10
  STRING.match('\d+\.\d+').to_s
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-performance
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.11.5
4
+ version: 1.13.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-08-18 00:00:00.000000000 Z
13
+ date: 2022-01-16 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rubocop
@@ -74,6 +74,7 @@ files:
74
74
  - lib/rubocop/cop/performance/chain_array_allocation.rb
75
75
  - lib/rubocop/cop/performance/collection_literal_in_loop.rb
76
76
  - lib/rubocop/cop/performance/compare_with_block.rb
77
+ - lib/rubocop/cop/performance/concurrent_monotonic_time.rb
77
78
  - lib/rubocop/cop/performance/constant_regexp.rb
78
79
  - lib/rubocop/cop/performance/count.rb
79
80
  - lib/rubocop/cop/performance/delete_prefix.rb
@@ -104,6 +105,7 @@ files:
104
105
  - lib/rubocop/cop/performance/sort_reverse.rb
105
106
  - lib/rubocop/cop/performance/squeeze.rb
106
107
  - lib/rubocop/cop/performance/start_with.rb
108
+ - lib/rubocop/cop/performance/string_identifier_argument.rb
107
109
  - lib/rubocop/cop/performance/string_include.rb
108
110
  - lib/rubocop/cop/performance/string_replacement.rb
109
111
  - lib/rubocop/cop/performance/sum.rb
@@ -121,8 +123,9 @@ metadata:
121
123
  homepage_uri: https://docs.rubocop.org/rubocop-performance/
122
124
  changelog_uri: https://github.com/rubocop/rubocop-performance/blob/master/CHANGELOG.md
123
125
  source_code_uri: https://github.com/rubocop/rubocop-performance/
124
- documentation_uri: https://docs.rubocop.org/rubocop-performance/1.11/
126
+ documentation_uri: https://docs.rubocop.org/rubocop-performance/1.13/
125
127
  bug_tracker_uri: https://github.com/rubocop/rubocop-performance/issues
128
+ rubygems_mfa_required: 'true'
126
129
  post_install_message:
127
130
  rdoc_options: []
128
131
  require_paths:
@@ -138,7 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
141
  - !ruby/object:Gem::Version
139
142
  version: '0'
140
143
  requirements: []
141
- rubygems_version: 3.2.12
144
+ rubygems_version: 3.3.3
142
145
  signing_key:
143
146
  specification_version: 4
144
147
  summary: Automatic performance checking tool for Ruby code.