@@ -85,7 +85,117 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
8585 ) {
8686 NonminimalBoolVisitor { cx } . visit_body ( body) ;
8787 }
88+
89+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
90+ match expr. kind {
91+ ExprKind :: Unary ( UnOp :: Not , sub) => check_inverted_condition ( cx, expr. span , sub) ,
92+ // This check the case where an element in a boolean comparison is inverted, like:
93+ //
94+ // ```
95+ // let a = true;
96+ // !a == false;
97+ // ```
98+ ExprKind :: Binary ( op, left, right) if matches ! ( op. node, BinOpKind :: Eq | BinOpKind :: Ne ) => {
99+ check_inverted_bool_in_condition ( cx, expr. span , op. node , left, right) ;
100+ } ,
101+ _ => { } ,
102+ }
103+ }
104+ }
105+
106+ fn inverted_bin_op_eq_str ( op : BinOpKind ) -> Option < & ' static str > {
107+ match op {
108+ BinOpKind :: Eq => Some ( "!=" ) ,
109+ BinOpKind :: Ne => Some ( "==" ) ,
110+ _ => None ,
111+ }
112+ }
113+
114+ fn bin_op_eq_str ( op : BinOpKind ) -> Option < & ' static str > {
115+ match op {
116+ BinOpKind :: Eq => Some ( "==" ) ,
117+ BinOpKind :: Ne => Some ( "!=" ) ,
118+ _ => None ,
119+ }
120+ }
121+
122+ fn check_inverted_condition ( cx : & LateContext < ' _ > , expr_span : Span , sub_expr : & Expr < ' _ > ) {
123+ if !expr_span. from_expansion ( )
124+ && let ExprKind :: Binary ( op, left, right) = sub_expr. kind
125+ && let Some ( left) = snippet_opt ( cx, left. span )
126+ && let Some ( right) = snippet_opt ( cx, right. span )
127+ {
128+ let Some ( op) = inverted_bin_op_eq_str ( op. node ) else {
129+ return ;
130+ } ;
131+ span_lint_and_sugg (
132+ cx,
133+ NONMINIMAL_BOOL ,
134+ expr_span,
135+ "this boolean expression can be simplified" ,
136+ "try" ,
137+ format ! ( "{left} {op} {right}" , ) ,
138+ Applicability :: MachineApplicable ,
139+ ) ;
140+ }
141+ }
142+
143+ fn check_inverted_bool_in_condition (
144+ cx : & LateContext < ' _ > ,
145+ expr_span : Span ,
146+ op : BinOpKind ,
147+ left : & Expr < ' _ > ,
148+ right : & Expr < ' _ > ,
149+ ) {
150+ if expr_span. from_expansion ( )
151+ && ( !cx. typeck_results ( ) . node_types ( ) [ left. hir_id ] . is_bool ( )
152+ || !cx. typeck_results ( ) . node_types ( ) [ right. hir_id ] . is_bool ( ) )
153+ {
154+ return ;
155+ }
156+
157+ let suggestion = match ( left. kind , right. kind ) {
158+ ( ExprKind :: Unary ( UnOp :: Not , left_sub) , ExprKind :: Unary ( UnOp :: Not , right_sub) ) => {
159+ let Some ( left) = snippet_opt ( cx, left_sub. span ) else {
160+ return ;
161+ } ;
162+ let Some ( right) = snippet_opt ( cx, right_sub. span ) else {
163+ return ;
164+ } ;
165+ let Some ( op) = bin_op_eq_str ( op) else { return } ;
166+ format ! ( "{left} {op} {right}" )
167+ } ,
168+ ( ExprKind :: Unary ( UnOp :: Not , left_sub) , _) => {
169+ let Some ( left) = snippet_opt ( cx, left_sub. span ) else {
170+ return ;
171+ } ;
172+ let Some ( right) = snippet_opt ( cx, right. span ) else {
173+ return ;
174+ } ;
175+ let Some ( op) = inverted_bin_op_eq_str ( op) else { return } ;
176+ format ! ( "{left} {op} {right}" )
177+ } ,
178+ ( _, ExprKind :: Unary ( UnOp :: Not , right_sub) ) => {
179+ let Some ( left) = snippet_opt ( cx, left. span ) else { return } ;
180+ let Some ( right) = snippet_opt ( cx, right_sub. span ) else {
181+ return ;
182+ } ;
183+ let Some ( op) = inverted_bin_op_eq_str ( op) else { return } ;
184+ format ! ( "{left} {op} {right}" )
185+ } ,
186+ _ => return ,
187+ } ;
188+ span_lint_and_sugg (
189+ cx,
190+ NONMINIMAL_BOOL ,
191+ expr_span,
192+ "this boolean expression can be simplified" ,
193+ "try" ,
194+ suggestion,
195+ Applicability :: MachineApplicable ,
196+ ) ;
88197}
198+
89199struct NonminimalBoolVisitor < ' a , ' tcx > {
90200 cx : & ' a LateContext < ' tcx > ,
91201}
0 commit comments