3
3
module RuboCop
4
4
module Cop
5
5
module Performance
6
- # Checks for double `#start_with?` or `#end_with?` calls
7
- # separated by `||`. In some cases such calls can be replaced
8
- # with an single `#start_with?`/`#end_with?` call.
6
+ # Checks for consecutive `#start_with?` or `#end_with?` calls.
7
+ # These methods accept multiple arguments, so in some cases like when
8
+ # they are separated by `||`, they can be combined into a single method call.
9
9
#
10
10
# `IncludeActiveSupportAliases` configuration option is used to check for
11
11
# `starts_with?` and `ends_with?`. These methods are defined by Active Support.
@@ -14,11 +14,13 @@ module Performance
14
14
# # bad
15
15
# str.start_with?("a") || str.start_with?(Some::CONST)
16
16
# str.start_with?("a", "b") || str.start_with?("c")
17
+ # !str.start_with?(foo) && !str.start_with?(bar)
17
18
# str.end_with?(var1) || str.end_with?(var2)
18
19
#
19
20
# # good
20
21
# str.start_with?("a", Some::CONST)
21
22
# str.start_with?("a", "b", "c")
23
+ # !str.start_with?(foo, bar)
22
24
# str.end_with?(var1, var2)
23
25
#
24
26
# @example IncludeActiveSupportAliases: false (default)
@@ -43,20 +45,33 @@ class DoubleStartEndWith < Base
43
45
44
46
MSG = 'Use `%<replacement>s` instead of `%<original_code>s`.'
45
47
48
+ METHODS = %i[ start_with? end_with? ] . to_set
49
+ METHODS_WITH_ACTIVE_SUPPORT = %i[ start_with? starts_with? end_with? ends_with? ] . to_set
50
+
46
51
def on_or ( node )
47
- receiver , method , first_call_args , second_call_args = process_source ( node )
52
+ two_start_end_with_calls ( node , methods_to_check : methods ) do |*matched |
53
+ check ( node , *matched )
54
+ end
55
+ end
56
+
57
+ def on_and ( node )
58
+ two_start_end_with_calls_negated ( node , methods_to_check : methods ) do |*matched |
59
+ check ( node , *matched )
60
+ end
61
+ end
48
62
63
+ private
64
+
65
+ def check ( node , receiver , method , first_call_args , second_call_args )
49
66
return unless receiver && second_call_args . all? ( &:pure? )
50
67
51
68
combined_args = combine_args ( first_call_args , second_call_args )
52
69
53
- add_offense ( node , message : message ( node , receiver , first_call_args , method , combined_args ) ) do |corrector |
70
+ add_offense ( node , message : message ( node , receiver , method , combined_args ) ) do |corrector |
54
71
autocorrect ( corrector , first_call_args , second_call_args , combined_args )
55
72
end
56
73
end
57
74
58
- private
59
-
60
75
def autocorrect ( corrector , first_call_args , second_call_args , combined_args )
61
76
first_argument = first_call_args . first . source_range
62
77
last_argument = second_call_args . last . source_range
@@ -65,17 +80,20 @@ def autocorrect(corrector, first_call_args, second_call_args, combined_args)
65
80
corrector . replace ( range , combined_args )
66
81
end
67
82
68
- def process_source ( node )
83
+ def methods
69
84
if check_for_active_support_aliases?
70
- check_with_active_support_aliases ( node )
85
+ METHODS_WITH_ACTIVE_SUPPORT
71
86
else
72
- two_start_end_with_calls ( node )
87
+ METHODS
73
88
end
74
89
end
75
90
76
- def message ( node , receiver , first_call_args , method , combined_args )
77
- dot = first_call_args . first . parent . send_type? ? '.' : '&.'
78
- replacement = "#{ receiver . source } #{ dot } #{ method } (#{ combined_args } )"
91
+ def message ( node , receiver , method , combined_args )
92
+ parent = receiver . parent
93
+ grandparent = parent . parent
94
+ dot = parent . send_type? ? '.' : '&.'
95
+ bang = grandparent . send_type? && grandparent . prefix_bang? ? '!' : ''
96
+ replacement = "#{ bang } #{ receiver . source } #{ dot } #{ method } (#{ combined_args } )"
79
97
format ( MSG , replacement : replacement , original_code : node . source )
80
98
end
81
99
@@ -89,16 +107,14 @@ def check_for_active_support_aliases?
89
107
90
108
def_node_matcher :two_start_end_with_calls , <<~PATTERN
91
109
(or
92
- (call $_recv [{:start_with? :end_with?} $_method] $...)
110
+ (call $_recv [%methods_to_check $_method] $...)
93
111
(call _recv _method $...))
94
112
PATTERN
95
113
96
- def_node_matcher :check_with_active_support_aliases , <<~PATTERN
97
- (or
98
- (call $_recv
99
- [{:start_with? :starts_with? :end_with? :ends_with?} $_method]
100
- $...)
101
- (call _recv _method $...))
114
+ def_node_matcher :two_start_end_with_calls_negated , <<~PATTERN
115
+ (and
116
+ (send (call $_recv [%methods_to_check $_method] $...) :!)
117
+ (send (call _recv _method $...) :!))
102
118
PATTERN
103
119
end
104
120
end
0 commit comments