rubocop-performance 1.8.0 → 1.10.0

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +10 -2
  4. data/config/default.yml +47 -6
  5. data/lib/rubocop/cop/mixin/regexp_metacharacter.rb +4 -4
  6. data/lib/rubocop/cop/performance/ancestors_include.rb +1 -0
  7. data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +77 -0
  8. data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +1 -0
  9. data/lib/rubocop/cop/performance/bind_call.rb +3 -2
  10. data/lib/rubocop/cop/performance/block_given_with_explicit_block.rb +52 -0
  11. data/lib/rubocop/cop/performance/caller.rb +13 -15
  12. data/lib/rubocop/cop/performance/casecmp.rb +1 -0
  13. data/lib/rubocop/cop/performance/chain_array_allocation.rb +20 -18
  14. data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +1 -1
  15. data/lib/rubocop/cop/performance/constant_regexp.rb +73 -0
  16. data/lib/rubocop/cop/performance/count.rb +1 -0
  17. data/lib/rubocop/cop/performance/delete_prefix.rb +1 -0
  18. data/lib/rubocop/cop/performance/delete_suffix.rb +1 -0
  19. data/lib/rubocop/cop/performance/detect.rb +47 -17
  20. data/lib/rubocop/cop/performance/end_with.rb +1 -0
  21. data/lib/rubocop/cop/performance/fixed_size.rb +1 -0
  22. data/lib/rubocop/cop/performance/flat_map.rb +1 -0
  23. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +2 -0
  24. data/lib/rubocop/cop/performance/io_readlines.rb +3 -7
  25. data/lib/rubocop/cop/performance/method_object_as_block.rb +32 -0
  26. data/lib/rubocop/cop/performance/open_struct.rb +1 -0
  27. data/lib/rubocop/cop/performance/range_include.rb +1 -0
  28. data/lib/rubocop/cop/performance/redundant_block_call.rb +4 -4
  29. data/lib/rubocop/cop/performance/redundant_equality_comparison_block.rb +72 -0
  30. data/lib/rubocop/cop/performance/redundant_match.rb +1 -0
  31. data/lib/rubocop/cop/performance/redundant_merge.rb +1 -0
  32. data/lib/rubocop/cop/performance/redundant_split_regexp_argument.rb +67 -0
  33. data/lib/rubocop/cop/performance/redundant_string_chars.rb +2 -6
  34. data/lib/rubocop/cop/performance/reverse_each.rb +7 -10
  35. data/lib/rubocop/cop/performance/reverse_first.rb +1 -0
  36. data/lib/rubocop/cop/performance/size.rb +1 -0
  37. data/lib/rubocop/cop/performance/squeeze.rb +2 -1
  38. data/lib/rubocop/cop/performance/start_with.rb +1 -0
  39. data/lib/rubocop/cop/performance/string_include.rb +2 -1
  40. data/lib/rubocop/cop/performance/string_replacement.rb +1 -0
  41. data/lib/rubocop/cop/performance/sum.rb +131 -18
  42. data/lib/rubocop/cop/performance/times_map.rb +1 -0
  43. data/lib/rubocop/cop/performance/unfreeze_string.rb +19 -1
  44. data/lib/rubocop/cop/performance/uri_default_parser.rb +1 -0
  45. data/lib/rubocop/cop/performance_cops.rb +6 -0
  46. data/lib/rubocop/performance/version.rb +6 -1
  47. metadata +29 -17
@@ -29,38 +29,40 @@ module RuboCop
29
29
  # [1,2].first # => 1
30
30
  # [1,2].first(1) # => [1]
31
31
  #
32
- RETURN_NEW_ARRAY_WHEN_ARGS = ':first :last :pop :sample :shift '
32
+ RETURN_NEW_ARRAY_WHEN_ARGS = %i[first last pop sample shift].to_set.freeze
33
33
 
34
34
  # These methods return a new array only when called without a block.
35
- RETURNS_NEW_ARRAY_WHEN_NO_BLOCK = ':zip :product '
35
+ RETURNS_NEW_ARRAY_WHEN_NO_BLOCK = %i[zip product].to_set.freeze
36
36
 
37
37
  # These methods ALWAYS return a new array
38
38
  # after they're called it's safe to mutate the the resulting array
39
- ALWAYS_RETURNS_NEW_ARRAY = ':* :+ :- :collect :compact :drop '\
40
- ':drop_while :flatten :map :reject ' \
41
- ':reverse :rotate :select :shuffle :sort ' \
42
- ':take :take_while :transpose :uniq ' \
43
- ':values_at :| '
39
+ ALWAYS_RETURNS_NEW_ARRAY = %i[* + - collect compact drop
40
+ drop_while flatten map reject
41
+ reverse rotate select shuffle sort
42
+ take take_while transpose uniq
43
+ values_at |].to_set.freeze
44
44
 
45
45
  # These methods have a mutation alternative. For example :collect
46
46
  # can be called as :collect!
47
- HAS_MUTATION_ALTERNATIVE = ':collect :compact :flatten :map :reject '\
48
- ':reverse :rotate :select :shuffle :sort '\
49
- ':uniq '
50
- MSG = 'Use unchained `%<method>s!` and `%<second_method>s!` '\
47
+ HAS_MUTATION_ALTERNATIVE = %i[collect compact flatten map reject
48
+ reverse rotate select shuffle sort uniq].to_set.freeze
49
+
50
+ RETURNS_NEW_ARRAY = (ALWAYS_RETURNS_NEW_ARRAY + RETURNS_NEW_ARRAY_WHEN_NO_BLOCK).freeze
51
+
52
+ MSG = 'Use unchained `%<method>s` and `%<second_method>s!` '\
51
53
  '(followed by `return array` if required) instead of chaining '\
52
54
  '`%<method>s...%<second_method>s`.'
53
55
 
54
- def_node_matcher :flat_map_candidate?, <<~PATTERN
55
- {
56
- (send (send _ ${#{RETURN_NEW_ARRAY_WHEN_ARGS}} {int lvar ivar cvar gvar}) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
57
- (send (block (send _ ${#{ALWAYS_RETURNS_NEW_ARRAY} }) ...) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
58
- (send (send _ ${#{ALWAYS_RETURNS_NEW_ARRAY + RETURNS_NEW_ARRAY_WHEN_NO_BLOCK}} ...) ${#{HAS_MUTATION_ALTERNATIVE}} $...)
59
- }
56
+ def_node_matcher :chain_array_allocation?, <<~PATTERN
57
+ (send {
58
+ (send _ $%RETURN_NEW_ARRAY_WHEN_ARGS {int lvar ivar cvar gvar})
59
+ (block (send _ $%ALWAYS_RETURNS_NEW_ARRAY) ...)
60
+ (send _ $%RETURNS_NEW_ARRAY ...)
61
+ } $%HAS_MUTATION_ALTERNATIVE ...)
60
62
  PATTERN
61
63
 
62
64
  def on_send(node)
63
- flat_map_candidate?(node) do |fm, sm, _|
65
+ chain_array_allocation?(node) do |fm, sm|
64
66
  range = range_between(node.loc.dot.begin_pos, node.source_range.end_pos)
65
67
 
66
68
  add_offense(range, message: format(MSG, method: fm, second_method: sm))
@@ -115,7 +115,7 @@ module RuboCop
115
115
 
116
116
  def node_within_enumerable_loop?(node, ancestor)
117
117
  enumerable_loop?(ancestor) do |receiver|
118
- receiver != node && !receiver.descendants.include?(node)
118
+ receiver != node && !receiver&.descendants&.include?(node)
119
119
  end
120
120
  end
121
121
 
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop finds regular expressions with dynamic components that are all constants.
7
+ #
8
+ # Ruby allocates a new Regexp object every time it executes a code containing such
9
+ # a regular expression. It is more efficient to extract it into a constant,
10
+ # memoize it, or add an `/o` option to perform `#{}` interpolation only once and
11
+ # reuse that Regexp object.
12
+ #
13
+ # @example
14
+ #
15
+ # # bad
16
+ # def tokens(pattern)
17
+ # pattern.scan(TOKEN).reject { |token| token.match?(/\A#{SEPARATORS}\Z/) }
18
+ # end
19
+ #
20
+ # # good
21
+ # ALL_SEPARATORS = /\A#{SEPARATORS}\Z/
22
+ # def tokens(pattern)
23
+ # pattern.scan(TOKEN).reject { |token| token.match?(ALL_SEPARATORS) }
24
+ # end
25
+ #
26
+ # # good
27
+ # def tokens(pattern)
28
+ # pattern.scan(TOKEN).reject { |token| token.match?(/\A#{SEPARATORS}\Z/o) }
29
+ # end
30
+ #
31
+ # # good
32
+ # def separators
33
+ # @separators ||= /\A#{SEPARATORS}\Z/
34
+ # end
35
+ #
36
+ class ConstantRegexp < Base
37
+ extend AutoCorrector
38
+
39
+ MSG = 'Extract this regexp into a constant, memoize it, or append an `/o` option to its options.'
40
+
41
+ def on_regexp(node)
42
+ return if within_allowed_assignment?(node) ||
43
+ !include_interpolated_const?(node) ||
44
+ node.single_interpolation?
45
+
46
+ add_offense(node) do |corrector|
47
+ corrector.insert_after(node, 'o')
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def within_allowed_assignment?(node)
54
+ node.each_ancestor(:casgn, :or_asgn).any?
55
+ end
56
+
57
+ def_node_matcher :regexp_escape?, <<~PATTERN
58
+ (send
59
+ (const nil? :Regexp) :escape const_type?)
60
+ PATTERN
61
+
62
+ def include_interpolated_const?(node)
63
+ return false unless node.interpolation?
64
+
65
+ node.each_child_node(:begin).all? do |begin_node|
66
+ inner_node = begin_node.children.first
67
+ inner_node && (inner_node.const_type? || regexp_escape?(inner_node))
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -42,6 +42,7 @@ module RuboCop
42
42
  extend AutoCorrector
43
43
 
44
44
  MSG = 'Use `count` instead of `%<selector>s...%<counter>s`.'
45
+ RESTRICT_ON_SEND = %i[count length size].freeze
45
46
 
46
47
  def_node_matcher :count_candidate?, <<~PATTERN
47
48
  {
@@ -51,6 +51,7 @@ module RuboCop
51
51
  minimum_target_ruby_version 2.5
52
52
 
53
53
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
54
+ RESTRICT_ON_SEND = %i[gsub gsub! sub sub!].freeze
54
55
 
55
56
  PREFERRED_METHODS = {
56
57
  gsub: :delete_prefix,
@@ -51,6 +51,7 @@ module RuboCop
51
51
  minimum_target_ruby_version 2.5
52
52
 
53
53
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
54
+ RESTRICT_ON_SEND = %i[gsub gsub! sub sub!].freeze
54
55
 
55
56
  PREFERRED_METHODS = {
56
57
  gsub: :delete_suffix,
@@ -3,9 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Performance
6
- # This cop is used to identify usages of
7
- # `select.first`, `select.last`, `find_all.first`, `find_all.last`, `filter.first`, and `filter.last`
8
- # and change them to use `detect` instead.
6
+ # This cop is used to identify usages of `first`, `last`, `[0]` or `[-1]`
7
+ # chained to `select`, `find_all` or `filter` and change them to use
8
+ # `detect` instead.
9
9
  #
10
10
  # @example
11
11
  # # bad
@@ -15,6 +15,8 @@ module RuboCop
15
15
  # [].find_all { |item| true }.last
16
16
  # [].filter { |item| true }.first
17
17
  # [].filter { |item| true }.last
18
+ # [].filter { |item| true }[0]
19
+ # [].filter { |item| true }[-1]
18
20
  #
19
21
  # # good
20
22
  # [].detect { |item| true }
@@ -27,27 +29,41 @@ module RuboCop
27
29
  class Detect < Base
28
30
  extend AutoCorrector
29
31
 
32
+ CANDIDATE_METHODS = Set[:select, :find_all, :filter].freeze
33
+
30
34
  MSG = 'Use `%<prefer>s` instead of ' \
31
35
  '`%<first_method>s.%<second_method>s`.'
32
36
  REVERSE_MSG = 'Use `reverse.%<prefer>s` instead of ' \
33
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]`.'
42
+ RESTRICT_ON_SEND = %i[first last []].freeze
34
43
 
35
44
  def_node_matcher :detect_candidate?, <<~PATTERN
36
45
  {
37
- (send $(block (send _ {:select :find_all :filter}) ...) ${:first :last} $...)
38
- (send $(send _ {:select :find_all :filter} ...) ${:first :last} $...)
46
+ (send $(block (send _ %CANDIDATE_METHODS) ...) ${:first :last} $...)
47
+ (send $(block (send _ %CANDIDATE_METHODS) ...) $:[] (int ${0 -1}))
48
+ (send $(send _ %CANDIDATE_METHODS ...) ${:first :last} $...)
49
+ (send $(send _ %CANDIDATE_METHODS ...) $:[] (int ${0 -1}))
39
50
  }
40
51
  PATTERN
41
52
 
42
53
  def on_send(node)
43
54
  detect_candidate?(node) do |receiver, second_method, args|
55
+ if second_method == :[]
56
+ index = args
57
+ args = {}
58
+ end
59
+
44
60
  return unless args.empty?
45
61
  return unless receiver
46
62
 
47
63
  receiver, _args, body = *receiver if receiver.block_type?
48
64
  return if accept_first_call?(receiver, body)
49
65
 
50
- register_offense(node, receiver, second_method)
66
+ register_offense(node, receiver, second_method, index)
51
67
  end
52
68
  end
53
69
 
@@ -62,28 +78,31 @@ module RuboCop
62
78
  lazy?(caller)
63
79
  end
64
80
 
65
- def register_offense(node, receiver, second_method)
81
+ def register_offense(node, receiver, second_method, index)
66
82
  _caller, first_method, _args = *receiver
67
83
  range = receiver.loc.selector.join(node.loc.selector)
68
84
 
69
- message = second_method == :last ? REVERSE_MSG : MSG
85
+ message = message_for_method(second_method, index)
70
86
  formatted_message = format(message, prefer: preferred_method,
71
87
  first_method: first_method,
72
- second_method: second_method)
88
+ second_method: second_method,
89
+ index: index)
73
90
 
74
91
  add_offense(range, message: formatted_message) do |corrector|
75
- autocorrect(corrector, node)
92
+ autocorrect(corrector, node, replacement(second_method, index))
76
93
  end
77
94
  end
78
95
 
79
- def autocorrect(corrector, node)
80
- receiver, first_method = *node
96
+ def replacement(method, index)
97
+ if method == :last || method == :[] && index == -1
98
+ "reverse.#{preferred_method}"
99
+ else
100
+ preferred_method
101
+ end
102
+ end
81
103
 
82
- replacement = if first_method == :last
83
- "reverse.#{preferred_method}"
84
- else
85
- preferred_method
86
- end
104
+ def autocorrect(corrector, node, replacement)
105
+ receiver, _first_method = *node
87
106
 
88
107
  first_range = receiver.source_range.end.join(node.loc.selector)
89
108
 
@@ -93,6 +112,17 @@ module RuboCop
93
112
  corrector.replace(receiver.loc.selector, replacement)
94
113
  end
95
114
 
115
+ def message_for_method(method, index)
116
+ case method
117
+ when :[]
118
+ index == -1 ? INDEX_REVERSE_MSG : INDEX_MSG
119
+ when :last
120
+ REVERSE_MSG
121
+ else
122
+ MSG
123
+ end
124
+ end
125
+
96
126
  def preferred_method
97
127
  config.for_cop('Style/CollectionMethods')['PreferredMethods']['detect'] || 'detect'
98
128
  end
@@ -47,6 +47,7 @@ module RuboCop
47
47
 
48
48
  MSG = 'Use `String#end_with?` instead of a regex match anchored to ' \
49
49
  'the end of the string.'
50
+ RESTRICT_ON_SEND = %i[match =~ match?].freeze
50
51
 
51
52
  def_node_matcher :redundant_regex?, <<~PATTERN
52
53
  {(send $!nil? {:match :=~ :match?} (regexp (str $#literal_at_end?) (regopt)))
@@ -47,6 +47,7 @@ module RuboCop
47
47
  #
48
48
  class FixedSize < Base
49
49
  MSG = 'Do not compute the size of statically sized objects.'
50
+ RESTRICT_ON_SEND = %i[count length size].freeze
50
51
 
51
52
  def_node_matcher :counter, <<~MATCHER
52
53
  (send ${array hash str sym} {:count :length :size} $...)
@@ -19,6 +19,7 @@ module RuboCop
19
19
  extend AutoCorrector
20
20
 
21
21
  MSG = 'Use `flat_map` instead of `%<method>s...%<flatten>s`.'
22
+ RESTRICT_ON_SEND = %i[flatten flatten!].freeze
22
23
  FLATTEN_MULTIPLE_LEVELS = ' Beware, `flat_map` only flattens 1 level ' \
23
24
  'and `flatten` can be used to flatten ' \
24
25
  'multiple levels.'
@@ -39,6 +39,8 @@ module RuboCop
39
39
  class InefficientHashSearch < Base
40
40
  extend AutoCorrector
41
41
 
42
+ RESTRICT_ON_SEND = %i[include?].freeze
43
+
42
44
  def_node_matcher :inefficient_include?, <<~PATTERN
43
45
  (send (send $_ {:keys :values}) :include? _)
44
46
  PATTERN
@@ -29,14 +29,14 @@ module RuboCop
29
29
  extend AutoCorrector
30
30
 
31
31
  MSG = 'Use `%<good>s` instead of `%<bad>s`.'
32
- ENUMERABLE_METHODS = (Enumerable.instance_methods + [:each]).freeze
32
+ RESTRICT_ON_SEND = (Enumerable.instance_methods + [:each]).freeze
33
33
 
34
34
  def_node_matcher :readlines_on_class?, <<~PATTERN
35
- $(send $(send (const nil? {:IO :File}) :readlines ...) #enumerable_method?)
35
+ $(send $(send (const nil? {:IO :File}) :readlines ...) _)
36
36
  PATTERN
37
37
 
38
38
  def_node_matcher :readlines_on_instance?, <<~PATTERN
39
- $(send $(send ${nil? !const_type?} :readlines ...) #enumerable_method? ...)
39
+ $(send $(send ${nil? !const_type?} :readlines ...) _ ...)
40
40
  PATTERN
41
41
 
42
42
  def on_send(node)
@@ -55,10 +55,6 @@ module RuboCop
55
55
 
56
56
  private
57
57
 
58
- def enumerable_method?(node)
59
- ENUMERABLE_METHODS.include?(node.to_sym)
60
- end
61
-
62
58
  def autocorrect(corrector, enumerable_call, readlines_call, receiver)
63
59
  # We cannot safely correct `.readlines` method called on IO/File classes
64
60
  # due to its signature and we are not sure with implicit receiver
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop identifies places where methods are converted to blocks, with the
7
+ # use of `&method`, and passed as arguments to method calls.
8
+ # It is faster to replace those with explicit blocks, calling those methods inside.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # array.map(&method(:do_something))
13
+ # [1, 2, 3].each(&out.method(:puts))
14
+ #
15
+ # # good
16
+ # array.map { |x| do_something(x) }
17
+ # [1, 2, 3].each { |x| out.puts(x) }
18
+ #
19
+ class MethodObjectAsBlock < Base
20
+ MSG = 'Use block explicitly instead of block-passing a method object.'
21
+
22
+ def_node_matcher :method_object_as_argument?, <<~PATTERN
23
+ (^send (send _ :method sym))
24
+ PATTERN
25
+
26
+ def on_block_pass(node)
27
+ add_offense(node) if method_object_as_argument?(node)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -30,6 +30,7 @@ module RuboCop
30
30
  class OpenStruct < Base
31
31
  MSG = 'Consider using `Struct` over `OpenStruct` ' \
32
32
  'to optimize the performance.'
33
+ RESTRICT_ON_SEND = %i[new].freeze
33
34
 
34
35
  def_node_matcher :open_struct, <<~PATTERN
35
36
  (send (const {nil? cbase} :OpenStruct) :new ...)
@@ -28,6 +28,7 @@ module RuboCop
28
28
  extend AutoCorrector
29
29
 
30
30
  MSG = 'Use `Range#cover?` instead of `Range#%<bad_method>s`.'
31
+ RESTRICT_ON_SEND = %i[include? member?].freeze
31
32
 
32
33
  # TODO: If we traced out assignments of variables to their uses, we
33
34
  # might pick up on a few more instances of this issue
@@ -80,11 +80,11 @@ module RuboCop
80
80
  def calls_to_report(argname, body)
81
81
  return [] if blockarg_assigned?(body, argname)
82
82
 
83
- calls = to_enum(:blockarg_calls, body, argname)
83
+ blockarg_calls(body, argname).map do |call|
84
+ return [] if args_include_block_pass?(call)
84
85
 
85
- return [] if calls.any? { |call| args_include_block_pass?(call) }
86
-
87
- calls
86
+ call
87
+ end
88
88
  end
89
89
 
90
90
  def args_include_block_pass?(blockcall)
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Performance
6
+ # This cop checks for uses `Enumerable#all?`, `Enumerable#any?`, `Enumerable#one?`,
7
+ # and `Enumerable#none?` are compared with `===` or similar methods in block.
8
+ #
9
+ # By default, `Object#===` behaves the same as `Object#==`, but this
10
+ # behavior is appropriately overridden in subclass. For example,
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.
14
+ #
15
+ # @example
16
+ # # bad
17
+ # items.all? { |item| pattern === item }
18
+ # items.all? { |item| item == other }
19
+ # items.all? { |item| item.is_a?(Klass) }
20
+ # items.all? { |item| item.kind_of?(Klass) }
21
+ #
22
+ # # good
23
+ # items.all?(pattern)
24
+ #
25
+ class RedundantEqualityComparisonBlock < Base
26
+ extend AutoCorrector
27
+ extend TargetRubyVersion
28
+
29
+ minimum_target_ruby_version 2.5
30
+
31
+ MSG = 'Use `%<prefer>s` instead of block.'
32
+
33
+ TARGET_METHODS = %i[all? any? one? none?].freeze
34
+ COMPARISON_METHODS = %i[== === is_a? kind_of?].freeze
35
+
36
+ def on_block(node)
37
+ return unless TARGET_METHODS.include?(node.method_name)
38
+
39
+ block_argument = node.arguments.first
40
+ block_body = node.body
41
+ return unless use_equality_comparison_block?(block_body)
42
+ return unless (new_argument = new_argument(block_argument, block_body))
43
+
44
+ range = offense_range(node)
45
+ prefer = "#{node.method_name}(#{new_argument})"
46
+
47
+ add_offense(range, message: format(MSG, prefer: prefer)) do |corrector|
48
+ corrector.replace(range, prefer)
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def use_equality_comparison_block?(block_body)
55
+ block_body.send_type? && COMPARISON_METHODS.include?(block_body.method_name)
56
+ end
57
+
58
+ def new_argument(block_argument, block_body)
59
+ if block_argument.source == block_body.receiver.source
60
+ block_body.first_argument.source
61
+ elsif block_argument.source == block_body.first_argument.source
62
+ block_body.receiver.source
63
+ end
64
+ end
65
+
66
+ def offense_range(node)
67
+ node.send_node.loc.selector.join(node.source_range.end)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end