33//! This lint is **warn** by default
44
55use crate :: utils:: sugg:: Sugg ;
6- use crate :: utils:: { higher, parent_node_is_if_expr, span_lint, span_lint_and_sugg} ;
6+ use crate :: utils:: { higher, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg} ;
7+ use if_chain:: if_chain;
78use rustc_ast:: ast:: LitKind ;
89use rustc_errors:: Applicability ;
9- use rustc_hir:: { BinOpKind , Block , Expr , ExprKind , StmtKind } ;
10+ use rustc_hir:: { BinOpKind , Block , Expr , ExprKind , StmtKind , UnOp } ;
1011use rustc_lint:: { LateContext , LateLintPass } ;
1112use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1213use rustc_span:: source_map:: Spanned ;
14+ use rustc_span:: Span ;
1315
1416declare_clippy_lint ! {
1517 /// **What it does:** Checks for expressions of the form `if c { true } else {
@@ -188,6 +190,34 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoolComparison {
188190 }
189191}
190192
193+ struct ExpressionInfoWithSpan {
194+ one_side_is_unary_not : bool ,
195+ left_span : Span ,
196+ right_span : Span ,
197+ }
198+
199+ fn is_unary_not ( e : & Expr < ' _ > ) -> ( bool , Span ) {
200+ if_chain ! {
201+ if let ExprKind :: Unary ( unop, operand) = e. kind;
202+ if let UnOp :: UnNot = unop;
203+ then {
204+ return ( true , operand. span) ;
205+ }
206+ } ;
207+ ( false , e. span )
208+ }
209+
210+ fn one_side_is_unary_not < ' tcx > ( left_side : & ' tcx Expr < ' _ > , right_side : & ' tcx Expr < ' _ > ) -> ExpressionInfoWithSpan {
211+ let left = is_unary_not ( left_side) ;
212+ let right = is_unary_not ( right_side) ;
213+
214+ ExpressionInfoWithSpan {
215+ one_side_is_unary_not : left. 0 != right. 0 ,
216+ left_span : left. 1 ,
217+ right_span : right. 1 ,
218+ }
219+ }
220+
191221fn check_comparison < ' a , ' tcx > (
192222 cx : & LateContext < ' a , ' tcx > ,
193223 e : & ' tcx Expr < ' _ > ,
@@ -199,10 +229,30 @@ fn check_comparison<'a, 'tcx>(
199229) {
200230 use self :: Expression :: { Bool , Other } ;
201231
202- if let ExprKind :: Binary ( _ , ref left_side, ref right_side) = e. kind {
232+ if let ExprKind :: Binary ( op , ref left_side, ref right_side) = e. kind {
203233 let ( l_ty, r_ty) = ( cx. tables . expr_ty ( left_side) , cx. tables . expr_ty ( right_side) ) ;
204234 if l_ty. is_bool ( ) && r_ty. is_bool ( ) {
205235 let mut applicability = Applicability :: MachineApplicable ;
236+
237+ if let BinOpKind :: Eq = op. node {
238+ let expression_info = one_side_is_unary_not ( & left_side, & right_side) ;
239+ if expression_info. one_side_is_unary_not {
240+ span_lint_and_sugg (
241+ cx,
242+ BOOL_COMPARISON ,
243+ e. span ,
244+ "This comparison might be written more concisely" ,
245+ "try simplifying it as shown" ,
246+ format ! (
247+ "{} != {}" ,
248+ snippet_with_applicability( cx, expression_info. left_span, ".." , & mut applicability) ,
249+ snippet_with_applicability( cx, expression_info. right_span, ".." , & mut applicability)
250+ ) ,
251+ applicability,
252+ )
253+ }
254+ }
255+
206256 match ( fetch_bool_expr ( left_side) , fetch_bool_expr ( right_side) ) {
207257 ( Bool ( true ) , Other ) => left_true. map_or ( ( ) , |( h, m) | {
208258 suggest_bool_comparison ( cx, e, right_side, applicability, m, h)
0 commit comments