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.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +10 -2
- data/config/default.yml +47 -6
- data/lib/rubocop/cop/mixin/regexp_metacharacter.rb +4 -4
- data/lib/rubocop/cop/performance/ancestors_include.rb +1 -0
- data/lib/rubocop/cop/performance/array_semi_infinite_range_slice.rb +77 -0
- data/lib/rubocop/cop/performance/big_decimal_with_numeric_argument.rb +1 -0
- data/lib/rubocop/cop/performance/bind_call.rb +3 -2
- data/lib/rubocop/cop/performance/block_given_with_explicit_block.rb +52 -0
- data/lib/rubocop/cop/performance/caller.rb +13 -15
- data/lib/rubocop/cop/performance/casecmp.rb +1 -0
- data/lib/rubocop/cop/performance/chain_array_allocation.rb +20 -18
- data/lib/rubocop/cop/performance/collection_literal_in_loop.rb +1 -1
- data/lib/rubocop/cop/performance/constant_regexp.rb +73 -0
- data/lib/rubocop/cop/performance/count.rb +1 -0
- data/lib/rubocop/cop/performance/delete_prefix.rb +1 -0
- data/lib/rubocop/cop/performance/delete_suffix.rb +1 -0
- data/lib/rubocop/cop/performance/detect.rb +47 -17
- data/lib/rubocop/cop/performance/end_with.rb +1 -0
- data/lib/rubocop/cop/performance/fixed_size.rb +1 -0
- data/lib/rubocop/cop/performance/flat_map.rb +1 -0
- data/lib/rubocop/cop/performance/inefficient_hash_search.rb +2 -0
- data/lib/rubocop/cop/performance/io_readlines.rb +3 -7
- data/lib/rubocop/cop/performance/method_object_as_block.rb +32 -0
- data/lib/rubocop/cop/performance/open_struct.rb +1 -0
- data/lib/rubocop/cop/performance/range_include.rb +1 -0
- data/lib/rubocop/cop/performance/redundant_block_call.rb +4 -4
- data/lib/rubocop/cop/performance/redundant_equality_comparison_block.rb +72 -0
- data/lib/rubocop/cop/performance/redundant_match.rb +1 -0
- data/lib/rubocop/cop/performance/redundant_merge.rb +1 -0
- data/lib/rubocop/cop/performance/redundant_split_regexp_argument.rb +67 -0
- data/lib/rubocop/cop/performance/redundant_string_chars.rb +2 -6
- data/lib/rubocop/cop/performance/reverse_each.rb +7 -10
- data/lib/rubocop/cop/performance/reverse_first.rb +1 -0
- data/lib/rubocop/cop/performance/size.rb +1 -0
- data/lib/rubocop/cop/performance/squeeze.rb +2 -1
- data/lib/rubocop/cop/performance/start_with.rb +1 -0
- data/lib/rubocop/cop/performance/string_include.rb +2 -1
- data/lib/rubocop/cop/performance/string_replacement.rb +1 -0
- data/lib/rubocop/cop/performance/sum.rb +131 -18
- data/lib/rubocop/cop/performance/times_map.rb +1 -0
- data/lib/rubocop/cop/performance/unfreeze_string.rb +19 -1
- data/lib/rubocop/cop/performance/uri_default_parser.rb +1 -0
- data/lib/rubocop/cop/performance_cops.rb +6 -0
- data/lib/rubocop/performance/version.rb +6 -1
- 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 =
|
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 =
|
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 =
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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 =
|
48
|
-
|
49
|
-
|
50
|
-
|
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 :
|
55
|
-
{
|
56
|
-
(send
|
57
|
-
(
|
58
|
-
(send
|
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
|
-
|
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
|
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
|
@@ -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
|
-
#
|
8
|
-
#
|
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 _
|
38
|
-
(send $(send _
|
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
|
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
|
80
|
-
|
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
|
-
|
83
|
-
|
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.'
|
@@ -29,14 +29,14 @@ module RuboCop
|
|
29
29
|
extend AutoCorrector
|
30
30
|
|
31
31
|
MSG = 'Use `%<good>s` instead of `%<bad>s`.'
|
32
|
-
|
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 ...)
|
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 ...)
|
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
|
-
|
83
|
+
blockarg_calls(body, argname).map do |call|
|
84
|
+
return [] if args_include_block_pass?(call)
|
84
85
|
|
85
|
-
|
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
|