@@ -37,6 +37,22 @@ module Rails
3737 #
3838 # # good
3939 # a.presence || b
40+ #
41+ # @example
42+ # # bad
43+ # a.present? ? a.foo : nil
44+ #
45+ # # bad
46+ # !a.present? ? nil : a.foo
47+ #
48+ # # bad
49+ # a.blank? ? nil : a.foo
50+ #
51+ # # bad
52+ # !a.blank? ? a.foo : nil
53+ #
54+ # # good
55+ # a.presence&.foo
4056 class Presence < Base
4157 include RangeHelp
4258 extend AutoCorrector
@@ -46,29 +62,29 @@ class Presence < Base
4662 def_node_matcher :redundant_receiver_and_other , <<~PATTERN
4763 {
4864 (if
49- (send $_recv :present?)
50- _recv
65+ {(send $_recv :blank?) (send (send $_recv :present?) :!)}
5166 $!begin
67+ _recv
5268 )
5369 (if
54- (send $_recv :blank?)
55- $!begin
70+ {(send $_recv :present?) (send (send $_recv :blank?) :!)}
5671 _recv
72+ $!begin
5773 )
5874 }
5975 PATTERN
6076
61- def_node_matcher :redundant_negative_receiver_and_other , <<~PATTERN
77+ def_node_matcher :redundant_receiver_and_chain , <<~PATTERN
6278 {
6379 (if
64- (send (send $_recv :present?) :!)
65- $!begin
66- _recv
80+ { (send $_recv :blank?) (send (send $_recv :present?) :!)}
81+ {nil? nil_type?}
82+ $(send _recv ...)
6783 )
6884 (if
69- (send (send $_recv :blank?) :!)
70- _recv
71- $!begin
85+ { (send $_recv :present?) (send (send $_recv :blank?) :!)}
86+ $(send _recv ...)
87+ {nil? nil_type?}
7288 )
7389 }
7490 PATTERN
@@ -82,18 +98,26 @@ def on_if(node)
8298 register_offense ( node , receiver , other )
8399 end
84100
85- redundant_negative_receiver_and_other ( node ) do |receiver , other |
86- return if ignore_other_node? ( other ) || receiver . nil?
101+ redundant_receiver_and_chain ( node ) do |receiver , chain |
102+ return if ignore_chain_node? ( chain ) || receiver . nil?
87103
88- register_offense ( node , receiver , other )
104+ register_chain_offense ( node , receiver , chain )
89105 end
90106 end
91107
92108 private
93109
94110 def register_offense ( node , receiver , other )
95- add_offense ( node , message : message ( node , receiver , other ) ) do |corrector |
96- corrector . replace ( node , replacement ( receiver , other , node . left_sibling ) )
111+ replacement = replacement ( receiver , other , node . left_sibling )
112+ add_offense ( node , message : message ( node , replacement ) ) do |corrector |
113+ corrector . replace ( node , replacement )
114+ end
115+ end
116+
117+ def register_chain_offense ( node , receiver , chain )
118+ replacement = chain_replacement ( receiver , chain , node . left_sibling )
119+ add_offense ( node , message : message ( node , replacement ) ) do |corrector |
120+ corrector . replace ( node , replacement )
97121 end
98122 end
99123
@@ -105,8 +129,12 @@ def ignore_other_node?(node)
105129 node &.type? ( :if , :rescue , :while )
106130 end
107131
108- def message ( node , receiver , other )
109- prefer = replacement ( receiver , other , node . left_sibling ) . gsub ( /^\s *|\n / , '' )
132+ def ignore_chain_node? ( node )
133+ node . method? ( '[]' ) || node . arithmetic_operation?
134+ end
135+
136+ def message ( node , replacement )
137+ prefer = replacement . gsub ( /^\s *|\n / , '' )
110138 current = current ( node ) . gsub ( /^\s *|\n / , '' )
111139 format ( MSG , prefer : prefer , current : current )
112140 end
@@ -146,6 +174,12 @@ def build_source_for_or_method(other)
146174 def method_range ( node )
147175 range_between ( node . source_range . begin_pos , node . first_argument . source_range . begin_pos - 1 )
148176 end
177+
178+ def chain_replacement ( receiver , chain , left_sibling )
179+ replaced = "#{ receiver . source } .presence&.#{ chain . method_name } "
180+ replaced += "(#{ chain . arguments . map ( &:source ) . join ( ', ' ) } )" if chain . arguments?
181+ left_sibling ? "(#{ replaced } )" : replaced
182+ end
149183 end
150184 end
151185 end
0 commit comments