forked from rubocop/rubocop-performance
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdouble_start_end_with.rb
106 lines (91 loc) · 3.49 KB
/
double_start_end_with.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# frozen_string_literal: true
module RuboCop
module Cop
module Performance
# Checks for double `#start_with?` or `#end_with?` calls
# separated by `||`. In some cases such calls can be replaced
# with an single `#start_with?`/`#end_with?` call.
#
# `IncludeActiveSupportAliases` configuration option is used to check for
# `starts_with?` and `ends_with?`. These methods are defined by Active Support.
#
# @example
# # bad
# str.start_with?("a") || str.start_with?(Some::CONST)
# str.start_with?("a", "b") || str.start_with?("c")
# str.end_with?(var1) || str.end_with?(var2)
#
# # good
# str.start_with?("a", Some::CONST)
# str.start_with?("a", "b", "c")
# str.end_with?(var1, var2)
#
# @example IncludeActiveSupportAliases: false (default)
# # good
# str.starts_with?("a", "b") || str.starts_with?("c")
# str.ends_with?(var1) || str.ends_with?(var2)
#
# str.starts_with?("a", "b", "c")
# str.ends_with?(var1, var2)
#
# @example IncludeActiveSupportAliases: true
# # bad
# str.starts_with?("a", "b") || str.starts_with?("c")
# str.ends_with?(var1) || str.ends_with?(var2)
#
# # good
# str.starts_with?("a", "b", "c")
# str.ends_with?(var1, var2)
#
class DoubleStartEndWith < Base
extend AutoCorrector
MSG = 'Use `%<receiver>s.%<method>s(%<combined_args>s)` instead of `%<original_code>s`.'
def on_or(node)
receiver, method, first_call_args, second_call_args = process_source(node)
return unless receiver && second_call_args.all?(&:pure?)
combined_args = combine_args(first_call_args, second_call_args)
add_offense(node, message: message(node, receiver, method, combined_args)) do |corrector|
autocorrect(corrector, first_call_args, second_call_args, combined_args)
end
end
private
def autocorrect(corrector, first_call_args, second_call_args, combined_args)
first_argument = first_call_args.first.loc.expression
last_argument = second_call_args.last.loc.expression
range = first_argument.join(last_argument)
corrector.replace(range, combined_args)
end
def process_source(node)
if check_for_active_support_aliases?
check_with_active_support_aliases(node)
else
two_start_end_with_calls(node)
end
end
def message(node, receiver, method, combined_args)
format(
MSG, receiver: receiver.source, method: method, combined_args: combined_args, original_code: node.source
)
end
def combine_args(first_call_args, second_call_args)
(first_call_args + second_call_args).map(&:source).join(', ')
end
def check_for_active_support_aliases?
cop_config['IncludeActiveSupportAliases']
end
def_node_matcher :two_start_end_with_calls, <<~PATTERN
(or
(send $_recv [{:start_with? :end_with?} $_method] $...)
(send _recv _method $...))
PATTERN
def_node_matcher :check_with_active_support_aliases, <<~PATTERN
(or
(send $_recv
[{:start_with? :starts_with? :end_with? :ends_with?} $_method]
$...)
(send _recv _method $...))
PATTERN
end
end
end
end