@@ -85,7 +85,117 @@ impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
85
85
) {
86
86
NonminimalBoolVisitor { cx } . visit_body ( body) ;
87
87
}
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
+ ) ;
88
197
}
198
+
89
199
struct NonminimalBoolVisitor < ' a , ' tcx > {
90
200
cx : & ' a LateContext < ' tcx > ,
91
201
}
0 commit comments