rubocop-performance 1.11.2 → 1.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +11 -1
  3. data/lib/rubocop/cop/performance/ancestors_include.rb +4 -0
  4. data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +8 -6
  5. data/lib/rubocop/cop/performance/case_when_splat.rb +11 -13
  6. data/lib/rubocop/cop/performance/casecmp.rb +4 -2
  7. data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +1 -1
  8. data/lib/rubocop/cop/performance/concurrent_monotonic_time.rb +42 -0
  9. data/lib/rubocop/cop/performance/count.rb +22 -13
  10. data/lib/rubocop/cop/performance/delete_prefix.rb +4 -4
  11. data/lib/rubocop/cop/performance/delete_suffix.rb +4 -4
  12. data/lib/rubocop/cop/performance/detect.rb +10 -9
  13. data/lib/rubocop/cop/performance/end_with.rb +5 -0
  14. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +3 -0
  15. data/lib/rubocop/cop/performance/map_compact.rb +13 -9
  16. data/lib/rubocop/cop/performance/open_struct.rb +4 -0
  17. data/lib/rubocop/cop/performance/range_include.rb +3 -2
  18. data/lib/rubocop/cop/performance/redundant_block_call.rb +1 -0
  19. data/lib/rubocop/cop/performance/redundant_equality_comparison_block.rb +20 -7
  20. data/lib/rubocop/cop/performance/redundant_merge.rb +4 -3
  21. data/lib/rubocop/cop/performance/redundant_string_chars.rb +7 -17
  22. data/lib/rubocop/cop/performance/regexp_match.rb +1 -2
  23. data/lib/rubocop/cop/performance/start_with.rb +5 -0
  24. data/lib/rubocop/cop/performance/string_include.rb +2 -1
  25. data/lib/rubocop/cop/performance/sum.rb +1 -1
  26. data/lib/rubocop/cop/performance/times_map.rb +12 -0
  27. data/lib/rubocop/cop/performance/unfreeze_string.rb +9 -6
  28. data/lib/rubocop/cop/performance_cops.rb +1 -0
  29. data/lib/rubocop/performance/version.rb +1 -1
  30. metadata +5 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a26b6360b5df05f3a7a8dfe957e5ecad0922105024a2e49dae8c39a566f10d88
4
- data.tar.gz: 7591fd4d805f4d6dad68b83c8fbffff8e563bfa27bd58030efa48f332c392b2f
3
+ metadata.gz: c98065b3a22d03c0e9290990288d6b8a82a87d956bdf4165d94d3cd326ae7bf5
4
+ data.tar.gz: a9ee1493235e6584ad381941423c0cac234bfc36fb4a2417433e667a16bec054
5
5
  SHA512:
6
- metadata.gz: c8fb234710212fd000d0d695da71a1b1a36687550cf3738ce987264dad85f015d2f1f1da9b0f511c2147532ff7d6d444b35efb472b399646171bbb35e0ff79ad
7
- data.tar.gz: 8447f79bb69e546ff684ece819b626fbfc68db8f7154b1b839c22167c87dbbf58821f1abe626a2b6d486ccfc81601e0844e416a312703ce0be7bc5552f1160ab
6
+ metadata.gz: 35f36bff7bfe1b19496bfea773e7d0f7892731e57b06bb523ac192324a817f401207750abab20892ea58ec54595d1bdfd454bf250a4a00baf6ef0f4ac3f7b030
7
+ data.tar.gz: 84a6ebc3e13d7c24aa6f1219443ce2c6cf6cd6c6253d4f09c78056fb3906f08bcb44db5365b03231aae55170a653e504b4a3433b4ed4a4dea1ee21b23d7a1387
data/config/default.yml CHANGED
@@ -76,6 +76,12 @@ Performance/CompareWithBlock:
76
76
  Enabled: true
77
77
  VersionAdded: '0.46'
78
78
 
79
+ Performance/ConcurrentMonotonicTime:
80
+ Description: 'Use `Process.clock_gettime(Process::CLOCK_MONOTONIC)` instead of `Concurrent.monotonic_time`.'
81
+ Reference: 'https://github.com/rails/rails/pull/43502'
82
+ Enabled: pending
83
+ VersionAdded: '1.12'
84
+
79
85
  Performance/ConstantRegexp:
80
86
  Description: 'Finds regular expressions with dynamic components that are all constants.'
81
87
  Enabled: pending
@@ -96,14 +102,18 @@ Performance/Count:
96
102
  Performance/DeletePrefix:
97
103
  Description: 'Use `delete_prefix` instead of `gsub`.'
98
104
  Enabled: true
105
+ Safe: false
99
106
  SafeMultiline: true
100
107
  VersionAdded: '1.6'
108
+ VersionChanged: '1.11'
101
109
 
102
110
  Performance/DeleteSuffix:
103
111
  Description: 'Use `delete_suffix` instead of `gsub`.'
104
112
  Enabled: true
113
+ Safe: false
105
114
  SafeMultiline: true
106
115
  VersionAdded: '1.6'
116
+ VersionChanged: '1.11'
107
117
 
108
118
  Performance/Detect:
109
119
  Description: >-
@@ -305,9 +315,9 @@ Performance/StartWith:
305
315
  Performance/StringInclude:
306
316
  Description: 'Use `String#include?` instead of a regex match with literal-only pattern.'
307
317
  Enabled: 'pending'
308
- AutoCorrect: false
309
318
  SafeAutoCorrect: false
310
319
  VersionAdded: '1.7'
320
+ VersionChanged: '1.12'
311
321
 
312
322
  Performance/StringReplacement:
313
323
  Description: >-
@@ -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,15 +7,17 @@ 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
14
- # # array[..2]
15
- # # array[...2]
16
- # # array[2..]
17
- # # array[2...]
18
- # # array.slice(..2)
16
+ # array[..2]
17
+ # array[...2]
18
+ # array[2..]
19
+ # array[2...]
20
+ # array.slice(..2)
19
21
  #
20
22
  # # good
21
23
  # array.take(3)
@@ -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
@@ -58,10 +60,8 @@ module RuboCop
58
60
  include RangeHelp
59
61
  extend AutoCorrector
60
62
 
61
- MSG = 'Reordering `when` conditions with a splat to the end ' \
62
- 'of the `when` branches can improve performance.'
63
- ARRAY_MSG = 'Pass the contents of array literals ' \
64
- 'directly to `when` conditions.'
63
+ MSG = 'Reordering `when` conditions with a splat to the end of the `when` branches can improve performance.'
64
+ ARRAY_MSG = 'Pass the contents of array literals directly to `when` conditions.'
65
65
 
66
66
  def on_case(case_node)
67
67
  when_conditions = case_node.when_branches.flat_map(&:conditions)
@@ -134,13 +134,11 @@ module RuboCop
134
134
  end
135
135
 
136
136
  def new_condition_with_then(node, new_condition)
137
- "\n#{indent_for(node)}when " \
138
- "#{new_condition} then #{node.body.source}"
137
+ "\n#{indent_for(node)}when #{new_condition} then #{node.body.source}"
139
138
  end
140
139
 
141
140
  def new_branch_without_then(node, new_condition)
142
- "\n#{indent_for(node)}when #{new_condition}" \
143
- "\n#{indent_for(node.body)}#{node.body.source}"
141
+ "\n#{indent_for(node)}when #{new_condition}\n#{indent_for(node.body)}#{node.body.source}"
144
142
  end
145
143
 
146
144
  def indent_for(node)
@@ -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
@@ -33,7 +33,7 @@ module RuboCop
33
33
  #
34
34
  class CollectionLiteralInLoop < Base
35
35
  MSG = 'Avoid immutable %<literal_class>s literals in loops. '\
36
- 'It is better to extract it into a local variable or a constant.'
36
+ 'It is better to extract it into a local variable or a constant.'
37
37
 
38
38
  POST_CONDITION_LOOP_TYPES = %i[while_post until_post].freeze
39
39
  LOOP_TYPES = (POST_CONDITION_LOOP_TYPES + %i[while until for]).freeze
@@ -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
@@ -14,6 +14,9 @@ module RuboCop
14
14
  #
15
15
  # The `delete_prefix('prefix')` method is faster than `gsub(/\Aprefix/, '')`.
16
16
  #
17
+ # @safety
18
+ # This cop is unsafe because `Pathname` has `sub` but not `delete_prefix`.
19
+ #
17
20
  # @example
18
21
  #
19
22
  # # bad
@@ -46,9 +49,6 @@ module RuboCop
46
49
  class DeletePrefix < Base
47
50
  include RegexpMetacharacter
48
51
  extend AutoCorrector
49
- extend TargetRubyVersion
50
-
51
- minimum_target_ruby_version 2.5
52
52
 
53
53
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
54
54
  RESTRICT_ON_SEND = %i[gsub gsub! sub sub!].freeze
@@ -66,7 +66,7 @@ module RuboCop
66
66
 
67
67
  def on_send(node)
68
68
  return unless (receiver, bad_method, regexp_str, replace_string = delete_prefix_candidate?(node))
69
- return unless replace_string.blank?
69
+ return unless replace_string.empty?
70
70
 
71
71
  good_method = PREFERRED_METHODS[bad_method]
72
72
 
@@ -14,6 +14,9 @@ module RuboCop
14
14
  #
15
15
  # The `delete_suffix('suffix')` method is faster than `gsub(/suffix\z/, '')`.
16
16
  #
17
+ # @safety
18
+ # This cop is unsafe because `Pathname` has `sub` but not `delete_suffix`.
19
+ #
17
20
  # @example
18
21
  #
19
22
  # # bad
@@ -46,9 +49,6 @@ module RuboCop
46
49
  class DeleteSuffix < Base
47
50
  include RegexpMetacharacter
48
51
  extend AutoCorrector
49
- extend TargetRubyVersion
50
-
51
- minimum_target_ruby_version 2.5
52
52
 
53
53
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
54
54
  RESTRICT_ON_SEND = %i[gsub gsub! sub sub!].freeze
@@ -66,7 +66,7 @@ module RuboCop
66
66
 
67
67
  def on_send(node)
68
68
  return unless (receiver, bad_method, regexp_str, replace_string = delete_suffix_candidate?(node))
69
- return unless replace_string.blank?
69
+ return unless replace_string.empty?
70
70
 
71
71
  good_method = PREFERRED_METHODS[bad_method]
72
72
 
@@ -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
@@ -31,14 +36,10 @@ module RuboCop
31
36
 
32
37
  CANDIDATE_METHODS = Set[:select, :find_all, :filter].freeze
33
38
 
34
- MSG = 'Use `%<prefer>s` instead of ' \
35
- '`%<first_method>s.%<second_method>s`.'
36
- REVERSE_MSG = 'Use `reverse.%<prefer>s` instead of ' \
37
- '`%<first_method>s.%<second_method>s`.'
38
- INDEX_MSG = 'Use `%<prefer>s` instead of ' \
39
- '`%<first_method>s[%<index>i]`.'
40
- INDEX_REVERSE_MSG = 'Use `reverse.%<prefer>s` instead of ' \
41
- '`%<first_method>s[%<index>i]`.'
39
+ MSG = 'Use `%<prefer>s` instead of `%<first_method>s.%<second_method>s`.'
40
+ REVERSE_MSG = 'Use `reverse.%<prefer>s` instead of `%<first_method>s.%<second_method>s`.'
41
+ INDEX_MSG = 'Use `%<prefer>s` instead of `%<first_method>s[%<index>i]`.'
42
+ INDEX_REVERSE_MSG = 'Use `reverse.%<prefer>s` instead of `%<first_method>s[%<index>i]`.'
42
43
  RESTRICT_ON_SEND = %i[first last []].freeze
43
44
 
44
45
  def_node_matcher :detect_candidate?, <<~PATTERN
@@ -94,7 +95,7 @@ module RuboCop
94
95
  end
95
96
 
96
97
  def replacement(method, index)
97
- if method == :last || method == :[] && index == -1
98
+ if method == :last || (method == :[] && index == -1)
98
99
  "reverse.#{preferred_method}"
99
100
  else
100
101
  preferred_method
@@ -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
  # ----
@@ -23,6 +25,7 @@ module RuboCop
23
25
  # # good
24
26
  # ary.filter_map(&:foo)
25
27
  # ary.map(&:foo).compact!
28
+ # ary.compact.map(&:foo)
26
29
  #
27
30
  class MapCompact < Base
28
31
  include RangeHelp
@@ -55,23 +58,24 @@ module RuboCop
55
58
 
56
59
  add_offense(range) do |corrector|
57
60
  corrector.replace(map_node.loc.selector, 'filter_map')
58
- corrector.remove(compact_loc.dot)
59
- corrector.remove(compact_method_range(node))
61
+ remove_compact_method(corrector, node)
60
62
  end
61
63
  end
62
64
 
63
65
  private
64
66
 
65
- def compact_method_range(compact_node)
67
+ def remove_compact_method(corrector, compact_node)
66
68
  chained_method = compact_node.parent
67
69
  compact_method_range = compact_node.loc.selector
68
70
 
69
- if compact_node.multiline? &&
70
- chained_method && !invoke_method_after_map_compact_on_same_line?(compact_node, chained_method)
71
- range_by_whole_lines(compact_method_range, include_final_newline: true)
71
+ if compact_node.multiline? && chained_method&.loc.respond_to?(:selector) && chained_method.dot? &&
72
+ !invoke_method_after_map_compact_on_same_line?(compact_node, chained_method)
73
+ compact_method_range = range_by_whole_lines(compact_method_range, include_final_newline: true)
72
74
  else
73
- compact_method_range
75
+ corrector.remove(compact_node.loc.dot)
74
76
  end
77
+
78
+ corrector.remove(compact_method_range)
75
79
  end
76
80
 
77
81
  def invoke_method_after_map_compact_on_same_line?(compact_node, chained_method)
@@ -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
 
@@ -74,12 +72,27 @@ module RuboCop
74
72
 
75
73
  def new_argument(block_argument, block_body)
76
74
  if block_argument.source == block_body.receiver.source
77
- block_body.first_argument.source
75
+ rhs = block_body.first_argument
76
+ return if use_block_argument_in_method_argument_of_operand?(block_argument, rhs)
77
+
78
+ rhs.source
78
79
  elsif block_argument.source == block_body.first_argument.source
79
- block_body.receiver.source
80
+ lhs = block_body.receiver
81
+ return if use_block_argument_in_method_argument_of_operand?(block_argument, lhs)
82
+
83
+ lhs.source
80
84
  end
81
85
  end
82
86
 
87
+ def use_block_argument_in_method_argument_of_operand?(block_argument, operand)
88
+ return false unless operand.send_type?
89
+
90
+ arguments = operand.arguments
91
+ arguments.inject(arguments.map(&:source)) do |operand_sources, argument|
92
+ operand_sources + argument.each_descendant(:lvar).map(&:source)
93
+ end.any?(block_argument.source)
94
+ end
95
+
83
96
  def offense_range(node)
84
97
  node.send_node.loc.selector.join(node.source_range.end)
85
98
  end
@@ -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)
@@ -16,35 +16,35 @@ module RuboCop
16
16
  # # bad
17
17
  # str.chars.first
18
18
  # str.chars.first(2)
19
- # str.chars.last
20
- # str.chars.last(2)
21
19
  #
22
20
  # # good
23
21
  # str[0]
24
22
  # str[0...2].chars
25
- # str[-1]
26
- # str[-2..-1].chars
27
23
  #
28
24
  # # bad
29
25
  # str.chars.take(2)
30
- # str.chars.drop(2)
31
26
  # str.chars.length
32
27
  # str.chars.size
33
28
  # str.chars.empty?
34
29
  #
35
30
  # # good
36
31
  # str[0...2].chars
37
- # str[2..-1].chars
38
32
  # str.length
39
33
  # str.size
40
34
  # str.empty?
41
35
  #
36
+ # # For example, if the receiver is a blank string, it will be incompatible.
37
+ # # If a negative value is specified for the receiver, `nil` is returned.
38
+ # str.chars.last # Incompatible with `str[-1]`.
39
+ # str.chars.last(2) # Incompatible with `str[-2..-1].chars`.
40
+ # str.chars.drop(2) # Incompatible with `str[2..-1].chars`.
41
+ #
42
42
  class RedundantStringChars < Base
43
43
  include RangeHelp
44
44
  extend AutoCorrector
45
45
 
46
46
  MSG = 'Use `%<good_method>s` instead of `%<bad_method>s`.'
47
- RESTRICT_ON_SEND = %i[[] slice first last take drop length size empty?].freeze
47
+ RESTRICT_ON_SEND = %i[[] slice first take length size empty?].freeze
48
48
 
49
49
  def_node_matcher :redundant_chars_call?, <<~PATTERN
50
50
  (send $(send _ :chars) $_ $...)
@@ -80,7 +80,6 @@ module RuboCop
80
80
  format(MSG, good_method: good_method, bad_method: bad_method)
81
81
  end
82
82
 
83
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength
84
83
  def build_good_method(method, args)
85
84
  case method
86
85
  when :[], :slice
@@ -91,21 +90,12 @@ module RuboCop
91
90
  else
92
91
  '[0]'
93
92
  end
94
- when :last
95
- if args.any?
96
- "[-#{args.first.source}..-1].chars"
97
- else
98
- '[-1]'
99
- end
100
93
  when :take
101
94
  "[0...#{args.first.source}].chars"
102
- when :drop
103
- "[#{args.first.source}..-1].chars"
104
95
  else
105
96
  ".#{method}"
106
97
  end
107
98
  end
108
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/MethodLength
109
99
 
110
100
  def build_bad_method(method, args)
111
101
  case method
@@ -78,8 +78,7 @@ module RuboCop
78
78
  # Constants are included in this list because it is unlikely that
79
79
  # someone will store `nil` as a constant and then use it for comparison
80
80
  TYPES_IMPLEMENTING_MATCH = %i[const regexp str sym].freeze
81
- MSG = 'Use `match?` instead of `%<current>s` when `MatchData` ' \
82
- 'is not used.'
81
+ MSG = 'Use `match?` instead of `%<current>s` when `MatchData` is not used.'
83
82
 
84
83
  def_node_matcher :match_method?, <<~PATTERN
85
84
  {
@@ -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/)
@@ -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
@@ -156,7 +156,7 @@ module RuboCop
156
156
  end
157
157
 
158
158
  def sum_method_range(node)
159
- range_between(node.loc.selector.begin_pos, node.loc.end.end_pos)
159
+ range_between(node.loc.selector.begin_pos, node.loc.expression.end_pos)
160
160
  end
161
161
 
162
162
  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
@@ -45,7 +45,10 @@ module RuboCop
45
45
  return unless dup_string?(node) || string_new?(node)
46
46
 
47
47
  add_offense(node) do |corrector|
48
- corrector.replace(node, "+#{string_value(node)}")
48
+ string_value = "+#{string_value(node)}"
49
+ string_value = "(#{string_value})" if node.parent&.send_type?
50
+
51
+ corrector.replace(node, string_value)
49
52
  end
50
53
  end
51
54
 
@@ -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'
@@ -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.2'
7
+ STRING = '1.12.0'
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.2
4
+ version: 1.12.0
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-05-05 00:00:00.000000000 Z
13
+ date: 2021-10-31 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
@@ -121,7 +122,7 @@ metadata:
121
122
  homepage_uri: https://docs.rubocop.org/rubocop-performance/
122
123
  changelog_uri: https://github.com/rubocop/rubocop-performance/blob/master/CHANGELOG.md
123
124
  source_code_uri: https://github.com/rubocop/rubocop-performance/
124
- documentation_uri: https://docs.rubocop.org/rubocop-performance/1.11/
125
+ documentation_uri: https://docs.rubocop.org/rubocop-performance/1.12/
125
126
  bug_tracker_uri: https://github.com/rubocop/rubocop-performance/issues
126
127
  post_install_message:
127
128
  rdoc_options: []
@@ -138,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
139
  - !ruby/object:Gem::Version
139
140
  version: '0'
140
141
  requirements: []
141
- rubygems_version: 3.2.12
142
+ rubygems_version: 3.3.0.dev
142
143
  signing_key:
143
144
  specification_version: 4
144
145
  summary: Automatic performance checking tool for Ruby code.