rubocop-performance 1.8.0 → 1.10.0

Sign up to get free protection for your applications and to get access to all the features.
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