1 //! lint on if branches that could be swapped so no `!` operation is necessary 2 //! on the condition 3 4 use clippy_utils::diagnostics::span_lint_and_help; 5 use clippy_utils::is_else_clause; 6 use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; 7 use rustc_lint::{LateContext, LateLintPass}; 8 use rustc_session::{declare_lint_pass, declare_tool_lint}; 9 10 declare_clippy_lint! { 11 /// ### What it does 12 /// Checks for usage of `!` or `!=` in an if condition with an 13 /// else branch. 14 /// 15 /// ### Why is this bad? 16 /// Negations reduce the readability of statements. 17 /// 18 /// ### Example 19 /// ```rust 20 /// # let v: Vec<usize> = vec![]; 21 /// # fn a() {} 22 /// # fn b() {} 23 /// if !v.is_empty() { 24 /// a() 25 /// } else { 26 /// b() 27 /// } 28 /// ``` 29 /// 30 /// Could be written: 31 /// 32 /// ```rust 33 /// # let v: Vec<usize> = vec![]; 34 /// # fn a() {} 35 /// # fn b() {} 36 /// if v.is_empty() { 37 /// b() 38 /// } else { 39 /// a() 40 /// } 41 /// ``` 42 #[clippy::version = "pre 1.29.0"] 43 pub IF_NOT_ELSE, 44 pedantic, 45 "`if` branches that could be swapped so no negation operation is necessary on the condition" 46 } 47 48 declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]); 49 50 impl LateLintPass<'_> for IfNotElse { check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>)51 fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { 52 // While loops will be desugared to ExprKind::If. This will cause the lint to fire. 53 // To fix this, return early if this span comes from a macro or desugaring. 54 if item.span.from_expansion() { 55 return; 56 } 57 if let ExprKind::If(cond, _, Some(els)) = item.kind { 58 if let ExprKind::Block(..) = els.kind { 59 // Disable firing the lint in "else if" expressions. 60 if is_else_clause(cx.tcx, item) { 61 return; 62 } 63 64 match cond.peel_drop_temps().kind { 65 ExprKind::Unary(UnOp::Not, _) => { 66 span_lint_and_help( 67 cx, 68 IF_NOT_ELSE, 69 item.span, 70 "unnecessary boolean `not` operation", 71 None, 72 "remove the `!` and swap the blocks of the `if`/`else`", 73 ); 74 }, 75 ExprKind::Binary(ref kind, _, _) if kind.node == BinOpKind::Ne => { 76 span_lint_and_help( 77 cx, 78 IF_NOT_ELSE, 79 item.span, 80 "unnecessary `!=` operation", 81 None, 82 "change to `==` and swap the blocks of the `if`/`else`", 83 ); 84 }, 85 _ => (), 86 } 87 } 88 } 89 } 90 } 91