rubocop-performance 1.4.1 → 1.6.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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +5 -1
  4. data/config/default.yml +29 -10
  5. data/lib/rubocop/cop/mixin/regexp_metacharacter.rb +76 -0
  6. data/lib/rubocop/cop/performance/bind_call.rb +87 -0
  7. data/lib/rubocop/cop/performance/caller.rb +3 -3
  8. data/lib/rubocop/cop/performance/casecmp.rb +5 -3
  9. data/lib/rubocop/cop/performance/chain_array_allocation.rb +1 -1
  10. data/lib/rubocop/cop/performance/compare_with_block.rb +2 -2
  11. data/lib/rubocop/cop/performance/count.rb +3 -6
  12. data/lib/rubocop/cop/performance/delete_prefix.rb +96 -0
  13. data/lib/rubocop/cop/performance/delete_suffix.rb +96 -0
  14. data/lib/rubocop/cop/performance/detect.rb +1 -5
  15. data/lib/rubocop/cop/performance/double_start_end_with.rb +2 -2
  16. data/lib/rubocop/cop/performance/end_with.rb +36 -13
  17. data/lib/rubocop/cop/performance/fixed_size.rb +1 -1
  18. data/lib/rubocop/cop/performance/flat_map.rb +9 -2
  19. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +1 -1
  20. data/lib/rubocop/cop/performance/open_struct.rb +1 -1
  21. data/lib/rubocop/cop/performance/range_include.rb +1 -1
  22. data/lib/rubocop/cop/performance/redundant_block_call.rb +3 -3
  23. data/lib/rubocop/cop/performance/redundant_match.rb +2 -2
  24. data/lib/rubocop/cop/performance/redundant_merge.rb +22 -9
  25. data/lib/rubocop/cop/performance/regexp_match.rb +13 -13
  26. data/lib/rubocop/cop/performance/reverse_each.rb +3 -2
  27. data/lib/rubocop/cop/performance/size.rb +2 -2
  28. data/lib/rubocop/cop/performance/start_with.rb +36 -16
  29. data/lib/rubocop/cop/performance/string_replacement.rb +4 -11
  30. data/lib/rubocop/cop/performance/times_map.rb +1 -1
  31. data/lib/rubocop/cop/performance/unfreeze_string.rb +3 -7
  32. data/lib/rubocop/cop/performance/uri_default_parser.rb +1 -1
  33. data/lib/rubocop/cop/performance_cops.rb +5 -0
  34. data/lib/rubocop/performance/inject.rb +1 -1
  35. data/lib/rubocop/performance/version.rb +1 -1
  36. metadata +13 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c7ae023c81bdf2e4816ffa5bed9a98fc10bcaec5e6e37c89f51eb9fb11fa452f
4
- data.tar.gz: 01bc8929e0c9ab2d80933c13c35dbcc713a04574ff51002ff2f7182e05045917
3
+ metadata.gz: 111d420207542c8522d9f34b5b8bf24356572b5fbcf0c11dfcb1e7f738a4c76f
4
+ data.tar.gz: 97632031f51e154320bc3d585da448740596436882b67277f1b5d26ddfe55fba
5
5
  SHA512:
6
- metadata.gz: 05da6b08dea1f6b8f16aa077bbfaa021b9586efb6895452f6af465fa03f695740370d8eb126d467c79565c204a2d76125fd0ce6137b9e18bb3dbe47d6c9ed44d
7
- data.tar.gz: b0c11d70c9677b6e7e8796523b602a7dfe8ed308dfe124b26a4935193eee4293f5147cdd198b2a277841ccea131dde686c0be965811659cb030b16e3a0841b68
6
+ metadata.gz: a36e6950e6fe7847303c87862df76767d32c4f68640c229ad4ceb6115d4ae2093be18a8296c2e49324c67e8eb06dd99db42f8823586f1b572e10a74693b831d1
7
+ data.tar.gz: 782352b1a7c420ac3c5498aeff749577e6280fb35d6e36844ae875511e9b6b44becfd9a2789d1b4c672cb71a91b8ab1cc63c89ec41abda8d1f34f1dacd26a07c
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-18 Bozhidar Batsov
1
+ Copyright (c) 2012-20 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
@@ -16,7 +16,7 @@ gem install rubocop-performance
16
16
  or if you use bundler put this in your `Gemfile`
17
17
 
18
18
  ```ruby
19
- gem 'rubocop-performance'
19
+ gem 'rubocop-performance', require: false
20
20
  ```
21
21
 
22
22
  ## Usage
@@ -72,6 +72,10 @@ Performance/Size:
72
72
  - lib/example.rb
73
73
  ```
74
74
 
75
+ ## Documentation
76
+
77
+ You can read a lot more about RuboCop Performance in its [official docs](https://docs.rubocop.org/rubocop-performance/).
78
+
75
79
  ## Contributing
76
80
 
77
81
  Checkout the [contribution guidelines](CONTRIBUTING.md).
@@ -1,5 +1,10 @@
1
1
  # This is the default configuration file.
2
2
 
3
+ Performance/BindCall:
4
+ Description: 'Use `bind_call(obj, args, ...)` instead of `bind(obj).call(args, ...)`.'
5
+ Enabled: true
6
+ VersionAdded: '1.6'
7
+
3
8
  Performance/Caller:
4
9
  Description: >-
5
10
  Use `caller(n..n)` instead of `caller`.
@@ -21,6 +26,7 @@ Performance/Casecmp:
21
26
  Use `casecmp` rather than `downcase ==`, `upcase ==`, `== downcase`, or `== upcase`..
22
27
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringcasecmp-vs-stringdowncase---code'
23
28
  Enabled: true
29
+ Safe: false
24
30
  VersionAdded: '0.36'
25
31
 
26
32
  Performance/ChainArrayAllocation:
@@ -44,11 +50,22 @@ Performance/Count:
44
50
  # This cop has known compatibility issues with `ActiveRecord` and other
45
51
  # frameworks. ActiveRecord's `count` ignores the block that is passed to it.
46
52
  # For more information, see the documentation in the cop itself.
47
- # If you understand the known risk, you can disable `SafeMode`.
48
- SafeMode: true
53
+ SafeAutoCorrect: false
49
54
  Enabled: true
50
55
  VersionAdded: '0.31'
51
- VersionChanged: '0.39'
56
+ VersionChanged: '1.5'
57
+
58
+ Performance/DeletePrefix:
59
+ Description: 'Use `delete_prefix` instead of `gsub`.'
60
+ Enabled: true
61
+ SafeMultiline: true
62
+ VersionAdded: '1.6'
63
+
64
+ Performance/DeleteSuffix:
65
+ Description: 'Use `delete_suffix` instead of `gsub`.'
66
+ Enabled: true
67
+ SafeMultiline: true
68
+ VersionAdded: '1.6'
52
69
 
53
70
  Performance/Detect:
54
71
  Description: >-
@@ -59,10 +76,10 @@ Performance/Detect:
59
76
  # frameworks. `ActiveRecord` does not implement a `detect` method and `find`
60
77
  # has its own meaning. Correcting `ActiveRecord` methods with this cop
61
78
  # should be considered unsafe.
62
- SafeMode: true
79
+ SafeAutoCorrect: false
63
80
  Enabled: true
64
81
  VersionAdded: '0.30'
65
- VersionChanged: '0.39'
82
+ VersionChanged: '1.5'
66
83
 
67
84
  Performance/DoubleStartEndWith:
68
85
  Description: >-
@@ -84,11 +101,12 @@ Performance/EndWith:
84
101
  SafeAutoCorrect: false
85
102
  AutoCorrect: false
86
103
  Enabled: true
104
+ SafeMultiline: true
87
105
  VersionAdded: '0.36'
88
- VersionChanged: '0.44'
106
+ VersionChanged: '1.6'
89
107
 
90
108
  Performance/FixedSize:
91
- Description: 'Do not compute the size of statically sized objects except in constants'
109
+ Description: 'Do not compute the size of statically sized objects except in constants.'
92
110
  Enabled: true
93
111
  VersionAdded: '0.35'
94
112
 
@@ -96,7 +114,7 @@ Performance/FlatMap:
96
114
  Description: >-
97
115
  Use `Enumerable#flat_map`
98
116
  instead of `Enumerable#map...Array#flatten(1)`
99
- or `Enumberable#collect..Array#flatten(1)`
117
+ or `Enumberable#collect..Array#flatten(1)`.
100
118
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code'
101
119
  Enabled: true
102
120
  VersionAdded: '0.30'
@@ -107,7 +125,7 @@ Performance/FlatMap:
107
125
  # `flatten` without any parameters can flatten multiple levels.
108
126
 
109
127
  Performance/InefficientHashSearch:
110
- Description: 'Use `key?` or `value?` instead of `keys.include?` or `values.include?`'
128
+ Description: 'Use `key?` or `value?` instead of `keys.include?` or `values.include?`.'
111
129
  Reference: 'https://github.com/JuanitoFatas/fast-ruby#hashkey-instead-of-hashkeysinclude-code'
112
130
  Enabled: true
113
131
  VersionAdded: '0.56'
@@ -178,8 +196,9 @@ Performance/StartWith:
178
196
  SafeAutoCorrect: false
179
197
  AutoCorrect: false
180
198
  Enabled: true
199
+ SafeMultiline: true
181
200
  VersionAdded: '0.36'
182
- VersionChanged: '0.44'
201
+ VersionChanged: '1.6'
183
202
 
184
203
  Performance/StringReplacement:
185
204
  Description: >-
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for handling regexp metacharacters.
6
+ module RegexpMetacharacter
7
+ private
8
+
9
+ def literal_at_start?(regexp)
10
+ return true if literal_at_start_with_backslash_a?(regexp)
11
+
12
+ !safe_multiline? && literal_at_start_with_caret?(regexp)
13
+ end
14
+
15
+ def literal_at_end?(regexp)
16
+ return true if literal_at_end_with_backslash_z?(regexp)
17
+
18
+ !safe_multiline? && literal_at_end_with_dollar?(regexp)
19
+ end
20
+
21
+ def literal_at_start_with_backslash_a?(regex_str)
22
+ # is this regexp 'literal' in the sense of only matching literal
23
+ # chars, rather than using metachars like `.` and `*` and so on?
24
+ # also, is it anchored at the start of the string?
25
+ # (tricky: \s, \d, and so on are metacharacters, but other characters
26
+ # escaped with a slash are just literals. LITERAL_REGEX takes all
27
+ # that into account.)
28
+ /\A\\A(?:#{Util::LITERAL_REGEX})+\z/.match?(regex_str)
29
+ end
30
+
31
+ def literal_at_start_with_caret?(regex_str)
32
+ # is this regexp 'literal' in the sense of only matching literal
33
+ # chars, rather than using metachars like `.` and `*` and so on?
34
+ # also, is it anchored at the start of the string?
35
+ # (tricky: \s, \d, and so on are metacharacters, but other characters
36
+ # escaped with a slash are just literals. LITERAL_REGEX takes all
37
+ # that into account.)
38
+ /\A\^(?:#{Util::LITERAL_REGEX})+\z/.match?(regex_str)
39
+ end
40
+
41
+ def literal_at_end_with_backslash_z?(regex_str)
42
+ # is this regexp 'literal' in the sense of only matching literal
43
+ # chars, rather than using metachars like . and * and so on?
44
+ # also, is it anchored at the end of the string?
45
+ /\A(?:#{Util::LITERAL_REGEX})+\\z\z/.match?(regex_str)
46
+ end
47
+
48
+ def literal_at_end_with_dollar?(regex_str)
49
+ # is this regexp 'literal' in the sense of only matching literal
50
+ # chars, rather than using metachars like . and * and so on?
51
+ # also, is it anchored at the end of the string?
52
+ /\A(?:#{Util::LITERAL_REGEX})+\$\z/.match?(regex_str)
53
+ end
54
+
55
+ def drop_start_metacharacter(regexp_string)
56
+ if regexp_string.start_with?('\\A')
57
+ regexp_string[2..-1] # drop `\A` anchor
58
+ else
59
+ regexp_string[1..-1] # drop `^` anchor
60
+ end
61
+ end
62
+
63
+ def drop_end_metacharacter(regexp_string)
64
+ if regexp_string.end_with?('\\z')
65
+ regexp_string.chomp('\z') # drop `\z` anchor
66
+ else
67
+ regexp_string.chop # drop `$` anchor
68
+ end
69
+ end
70
+
71
+ def safe_multiline?
72
+ cop_config.fetch('SafeMultiline', true)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # In Ruby 2.7, `UnboundMethod#bind_call` has been added.
7
+ #
8
+ # This cop identifies places where `bind(obj).call(args, ...)`
9
+ # can be replaced by `bind_call(obj, args, ...)`.
10
+ #
11
+ # The `bind_call(obj, args, ...)` method is faster than
12
+ # `bind(obj).call(args, ...)`.
13
+ #
14
+ # @example
15
+ # # bad
16
+ # umethod.bind(obj).call(foo, bar)
17
+ # umethod.bind(obj).(foo, bar)
18
+ #
19
+ # # good
20
+ # umethod.bind_call(obj, foo, bar)
21
+ #
22
+ class BindCall < Cop
23
+ include RangeHelp
24
+ extend TargetRubyVersion
25
+
26
+ minimum_target_ruby_version 2.7
27
+
28
+ MSG = 'Use `bind_call(%<bind_arg>s%<comma>s%<call_args>s)` ' \
29
+ 'instead of `bind(%<bind_arg>s).call(%<call_args>s)`.'
30
+
31
+ def_node_matcher :bind_with_call_method?, <<~PATTERN
32
+ (send
33
+ $(send
34
+ (send nil? _) :bind
35
+ $(...)) :call
36
+ $...)
37
+ PATTERN
38
+
39
+ 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)
53
+
54
+ range = correction_range(receiver, node)
55
+
56
+ call_args = build_call_args(call_args_node)
57
+ call_args = ", #{call_args}" unless call_args.empty?
58
+
59
+ replacement_method = "bind_call(#{bind_arg.source}#{call_args})"
60
+
61
+ lambda do |corrector|
62
+ corrector.replace(range, replacement_method)
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def message(bind_arg, call_args)
69
+ comma = call_args.empty? ? '' : ', '
70
+
71
+ format(MSG, bind_arg: bind_arg, comma: comma, call_args: call_args)
72
+ end
73
+
74
+ def correction_range(receiver, node)
75
+ location_of_bind = receiver.loc.selector.begin_pos
76
+ location_of_call = node.loc.end.end_pos
77
+
78
+ range_between(location_of_bind, location_of_call)
79
+ end
80
+
81
+ def build_call_args(call_args_node)
82
+ call_args_node.map(&:source).join(', ')
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -24,14 +24,14 @@ module RuboCop
24
24
  MSG_FIRST = 'Use `%<method>s(%<n>d..%<n>d).first`' \
25
25
  ' instead of `%<method>s.first`.'
26
26
 
27
- def_node_matcher :slow_caller?, <<-PATTERN
27
+ def_node_matcher :slow_caller?, <<~PATTERN
28
28
  {
29
29
  (send nil? {:caller :caller_locations})
30
30
  (send nil? {:caller :caller_locations} int)
31
31
  }
32
32
  PATTERN
33
33
 
34
- def_node_matcher :caller_with_scope_method?, <<-PATTERN
34
+ def_node_matcher :caller_with_scope_method?, <<~PATTERN
35
35
  {
36
36
  (send #slow_caller? :first)
37
37
  (send #slow_caller? :[] int)
@@ -51,7 +51,7 @@ module RuboCop
51
51
  caller_arg = node.receiver.first_argument
52
52
  n = caller_arg ? int_value(caller_arg) : 1
53
53
 
54
- if node.method_name == :[]
54
+ if node.method?(:[])
55
55
  m = int_value(node.first_argument)
56
56
  n += m
57
57
  format(MSG_BRACE, n: n, m: m, method: method_name)
@@ -5,6 +5,8 @@ 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
10
  #
9
11
  # @example
10
12
  # # bad
@@ -21,21 +23,21 @@ module RuboCop
21
23
  MSG = 'Use `%<good>s` instead of `%<bad>s`.'
22
24
  CASE_METHODS = %i[downcase upcase].freeze
23
25
 
24
- def_node_matcher :downcase_eq, <<-PATTERN
26
+ def_node_matcher :downcase_eq, <<~PATTERN
25
27
  (send
26
28
  $(send _ ${:downcase :upcase})
27
29
  ${:== :eql? :!=}
28
30
  ${str (send _ {:downcase :upcase} ...) (begin str)})
29
31
  PATTERN
30
32
 
31
- def_node_matcher :eq_downcase, <<-PATTERN
33
+ def_node_matcher :eq_downcase, <<~PATTERN
32
34
  (send
33
35
  {str (send _ {:downcase :upcase} ...) (begin str)}
34
36
  ${:== :eql? :!=}
35
37
  $(send _ ${:downcase :upcase}))
36
38
  PATTERN
37
39
 
38
- def_node_matcher :downcase_downcase, <<-PATTERN
40
+ def_node_matcher :downcase_downcase, <<~PATTERN
39
41
  (send
40
42
  $(send _ ${:downcase :upcase})
41
43
  ${:== :eql? :!=}
@@ -51,7 +51,7 @@ module RuboCop
51
51
  '(followed by `return array` if required) instead of chaining '\
52
52
  '`%<method>s...%<second_method>s`.'
53
53
 
54
- def_node_matcher :flat_map_candidate?, <<-PATTERN
54
+ def_node_matcher :flat_map_candidate?, <<~PATTERN
55
55
  {
56
56
  (send (send _ ${#{RETURN_NEW_ARRAY_WHEN_ARGS}} {int lvar ivar cvar gvar}) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
57
57
  (send (block (send _ ${#{ALWAYS_RETURNS_NEW_ARRAY} }) ...) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
@@ -30,14 +30,14 @@ module RuboCop
30
30
  '`%<compare_method>s { |%<var_a>s, %<var_b>s| %<str_a>s ' \
31
31
  '<=> %<str_b>s }`.'
32
32
 
33
- def_node_matcher :compare?, <<-PATTERN
33
+ def_node_matcher :compare?, <<~PATTERN
34
34
  (block
35
35
  $(send _ {:sort :min :max})
36
36
  (args (arg $_a) (arg $_b))
37
37
  $send)
38
38
  PATTERN
39
39
 
40
- def_node_matcher :replaceable_body?, <<-PATTERN
40
+ def_node_matcher :replaceable_body?, <<~PATTERN
41
41
  (send
42
42
  (send (lvar %1) $_method $...)
43
43
  :<=>
@@ -32,18 +32,17 @@ module RuboCop
32
32
  # make `count` work with a block is to call `to_a.count {...}`.
33
33
  #
34
34
  # Example:
35
- # Model.where(id: [1, 2, 3].select { |m| m.method == true }.size
35
+ # `Model.where(id: [1, 2, 3]).select { |m| m.method == true }.size`
36
36
  #
37
37
  # becomes:
38
38
  #
39
- # Model.where(id: [1, 2, 3]).to_a.count { |m| m.method == true }
39
+ # `Model.where(id: [1, 2, 3]).to_a.count { |m| m.method == true }`
40
40
  class Count < Cop
41
- include SafeMode
42
41
  include RangeHelp
43
42
 
44
43
  MSG = 'Use `count` instead of `%<selector>s...%<counter>s`.'
45
44
 
46
- def_node_matcher :count_candidate?, <<-PATTERN
45
+ def_node_matcher :count_candidate?, <<~PATTERN
47
46
  {
48
47
  (send (block $(send _ ${:select :reject}) ...) ${:count :length :size})
49
48
  (send $(send _ ${:select :reject} (:block_pass _)) ${:count :length :size})
@@ -51,8 +50,6 @@ module RuboCop
51
50
  PATTERN
52
51
 
53
52
  def on_send(node)
54
- return if rails_safe_mode?
55
-
56
53
  count_candidate?(node) do |selector_node, selector, counter|
57
54
  return unless eligible_node?(node)
58
55
 
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # In Ruby 2.5, `String#delete_prefix` has been added.
7
+ #
8
+ # This cop identifies places where `gsub(/\Aprefix/, '')` and `sub(/\Aprefix/, '')`
9
+ # can be replaced by `delete_prefix('prefix')`.
10
+ #
11
+ # This cop has `SafeMultiline` configuration option that `true` by default because
12
+ # `^prefix` is unsafe as it will behave incompatible with `delete_prefix`
13
+ # for receiver is multiline string.
14
+ #
15
+ # The `delete_prefix('prefix')` method is faster than `gsub(/\Aprefix/, '')`.
16
+ #
17
+ # @example
18
+ #
19
+ # # bad
20
+ # str.gsub(/\Aprefix/, '')
21
+ # str.gsub!(/\Aprefix/, '')
22
+ #
23
+ # str.sub(/\Aprefix/, '')
24
+ # str.sub!(/\Aprefix/, '')
25
+ #
26
+ # # good
27
+ # str.delete_prefix('prefix')
28
+ # str.delete_prefix!('prefix')
29
+ #
30
+ # @example SafeMultiline: true (default)
31
+ #
32
+ # # good
33
+ # str.gsub(/^prefix/, '')
34
+ # str.gsub!(/^prefix/, '')
35
+ # str.sub(/^prefix/, '')
36
+ # str.sub!(/^prefix/, '')
37
+ #
38
+ # @example SafeMultiline: false
39
+ #
40
+ # # bad
41
+ # str.gsub(/^prefix/, '')
42
+ # str.gsub!(/^prefix/, '')
43
+ # str.sub(/^prefix/, '')
44
+ # str.sub!(/^prefix/, '')
45
+ #
46
+ class DeletePrefix < Cop
47
+ extend TargetRubyVersion
48
+ include RegexpMetacharacter
49
+
50
+ minimum_target_ruby_version 2.5
51
+
52
+ MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
53
+
54
+ PREFERRED_METHODS = {
55
+ gsub: :delete_prefix,
56
+ gsub!: :delete_prefix!,
57
+ sub: :delete_prefix,
58
+ sub!: :delete_prefix!
59
+ }.freeze
60
+
61
+ def_node_matcher :delete_prefix_candidate?, <<~PATTERN
62
+ (send $!nil? ${:gsub :gsub! :sub :sub!} (regexp (str $#literal_at_start?) (regopt)) (str $_))
63
+ PATTERN
64
+
65
+ def on_send(node)
66
+ delete_prefix_candidate?(node) do |_, bad_method, _, replace_string|
67
+ return unless replace_string.blank?
68
+
69
+ good_method = PREFERRED_METHODS[bad_method]
70
+
71
+ message = format(MSG, current: bad_method, prefer: good_method)
72
+
73
+ add_offense(node, location: :selector, message: message)
74
+ end
75
+ end
76
+
77
+ def autocorrect(node)
78
+ delete_prefix_candidate?(node) do |receiver, bad_method, regexp_str, _|
79
+ lambda do |corrector|
80
+ good_method = PREFERRED_METHODS[bad_method]
81
+ regexp_str = drop_start_metacharacter(regexp_str)
82
+ regexp_str = interpret_string_escapes(regexp_str)
83
+ string_literal = to_string_literal(regexp_str)
84
+
85
+ new_code = "#{receiver.source}.#{good_method}(#{string_literal})"
86
+
87
+ # TODO: `source_range` is no longer required when RuboCop 0.81 or lower support will be dropped.
88
+ # https://github.com/rubocop-hq/rubocop/commit/82eb350d2cba16
89
+ corrector.replace(node.source_range, new_code)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end