]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/needless_bool.rs
Auto merge of #4551 - mikerite:fix-ice-reporting, r=llogiq
[rust.git] / clippy_lints / src / needless_bool.rs
index 3bb87fbf5e92119a824901f8a41fde2dd70d640a..b761457f64ce23e035ea042c9dab34085e11e5bb 100644 (file)
@@ -1,90 +1,81 @@
-// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
 //! Checks for needless boolean results of if-else expressions
 //!
 //! This lint is **warn** by default
 
-use crate::rustc::hir::*;
-use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
-use crate::rustc::{declare_tool_lint, lint_array};
-use crate::rustc_errors::Applicability;
-use crate::syntax::ast::LitKind;
-use crate::syntax::source_map::Spanned;
 use crate::utils::sugg::Sugg;
-use crate::utils::{in_macro, span_lint, span_lint_and_sugg};
-
-/// **What it does:** Checks for expressions of the form `if c { true } else {
-/// false }`
-/// (or vice versa) and suggest using the condition directly.
-///
-/// **Why is this bad?** Redundant code.
-///
-/// **Known problems:** Maybe false positives: Sometimes, the two branches are
-/// painstakingly documented (which we of course do not detect), so they *may*
-/// have some value. Even then, the documentation can be rewritten to match the
-/// shorter code.
-///
-/// **Example:**
-/// ```rust
-/// if x {
-///     false
-/// } else {
-///     true
-/// }
-/// ```
+use crate::utils::{higher, span_lint, span_lint_and_sugg};
+use rustc::hir::*;
+use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
+use rustc::{declare_lint_pass, declare_tool_lint};
+use rustc_errors::Applicability;
+use syntax::ast::LitKind;
+use syntax::source_map::Spanned;
+
 declare_clippy_lint! {
+    /// **What it does:** Checks for expressions of the form `if c { true } else {
+    /// false }`
+    /// (or vice versa) and suggest using the condition directly.
+    ///
+    /// **Why is this bad?** Redundant code.
+    ///
+    /// **Known problems:** Maybe false positives: Sometimes, the two branches are
+    /// painstakingly documented (which we, of course, do not detect), so they *may*
+    /// have some value. Even then, the documentation can be rewritten to match the
+    /// shorter code.
+    ///
+    /// **Example:**
+    /// ```rust,ignore
+    /// if x {
+    ///     false
+    /// } else {
+    ///     true
+    /// }
+    /// ```
+    /// Could be written as
+    /// ```rust,ignore
+    /// !x
+    /// ```
     pub NEEDLESS_BOOL,
     complexity,
-    "if-statements with plain booleans in the then- and else-clause, e.g. `if p { true } else { false }`"
+    "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`"
 }
 
-/// **What it does:** Checks for expressions of the form `x == true` and
-/// `x != true` (or vice versa) and suggest using the variable directly.
-///
-/// **Why is this bad?** Unnecessary code.
-///
-/// **Known problems:** None.
-///
-/// **Example:**
-/// ```rust
-/// if x == true {} // could be `if x { }`
-/// ```
 declare_clippy_lint! {
+    /// **What it does:** Checks for expressions of the form `x == true`,
+    /// `x != true` and order comparisons such as `x < true` (or vice versa) and
+    /// suggest using the variable directly.
+    ///
+    /// **Why is this bad?** Unnecessary code.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust,ignore
+    /// if x == true {} // could be `if x { }`
+    /// ```
     pub BOOL_COMPARISON,
     complexity,
-    "comparing a variable to a boolean, e.g. `if x == true` or `if x != true`"
+    "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
 }
 
-#[derive(Copy, Clone)]
-pub struct NeedlessBool;
-
-impl LintPass for NeedlessBool {
-    fn get_lints(&self) -> LintArray {
-        lint_array!(NEEDLESS_BOOL)
-    }
-}
+declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]);
 
 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBool {
     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
         use self::Expression::*;
-        if let ExprKind::If(ref pred, ref then_block, Some(ref else_expr)) = e.node {
+        if let Some((ref pred, ref then_block, Some(ref else_expr))) = higher::if_block(&e) {
             let reduce = |ret, not| {
                 let mut applicability = Applicability::MachineApplicable;
                 let snip = Sugg::hir_with_applicability(cx, pred, "<predicate>", &mut applicability);
-                let snip = if not { !snip } else { snip };
+                let mut snip = if not { !snip } else { snip };
 
-                let hint = if ret {
-                    format!("return {}", snip)
-                } else {
-                    snip.to_string()
-                };
+                if ret {
+                    snip = snip.make_return();
+                }
+
+                if parent_node_is_if_expr(&e, &cx) {
+                    snip = snip.blockify()
+                }
 
                 span_lint_and_sugg(
                     cx,
@@ -92,7 +83,7 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
                     e.span,
                     "this if-then-else expression returns a bool literal",
                     "you can reduce it to",
-                    hint,
+                    snip.to_string(),
                     applicability,
                 );
             };
@@ -127,38 +118,74 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
     }
 }
 
-#[derive(Copy, Clone)]
-pub struct BoolComparison;
+fn parent_node_is_if_expr<'a, 'b>(expr: &Expr, cx: &LateContext<'a, 'b>) -> bool {
+    let parent_id = cx.tcx.hir().get_parent_node(expr.hir_id);
+    let parent_node = cx.tcx.hir().get(parent_id);
 
-impl LintPass for BoolComparison {
-    fn get_lints(&self) -> LintArray {
-        lint_array!(BOOL_COMPARISON)
+    match parent_node {
+        rustc::hir::Node::Expr(e) => higher::if_block(&e).is_some(),
+        rustc::hir::Node::Arm(e) => higher::if_block(&e.body).is_some(),
+        _ => false,
     }
 }
 
+declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]);
+
 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoolComparison {
     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
-        if in_macro(e.span) {
+        if e.span.from_expansion() {
             return;
         }
 
         if let ExprKind::Binary(Spanned { node, .. }, ..) = e.node {
+            let ignore_case = None::<(fn(_) -> _, &str)>;
+            let ignore_no_literal = None::<(fn(_, _) -> _, &str)>;
             match node {
-                BinOpKind::Eq => check_comparison(
+                BinOpKind::Eq => {
+                    let true_case = Some((|h| h, "equality checks against true are unnecessary"));
+                    let false_case = Some((
+                        |h: Sugg<'_>| !h,
+                        "equality checks against false can be replaced by a negation",
+                    ));
+                    check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal)
+                },
+                BinOpKind::Ne => {
+                    let true_case = Some((
+                        |h: Sugg<'_>| !h,
+                        "inequality checks against true can be replaced by a negation",
+                    ));
+                    let false_case = Some((|h| h, "inequality checks against false are unnecessary"));
+                    check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal)
+                },
+                BinOpKind::Lt => check_comparison(
                     cx,
                     e,
-                    "equality checks against true are unnecessary",
-                    "equality checks against false can be replaced by a negation",
-                    |h| h,
-                    |h| !h,
+                    ignore_case,
+                    Some((|h| h, "greater than checks against false are unnecessary")),
+                    Some((
+                        |h: Sugg<'_>| !h,
+                        "less than comparison against true can be replaced by a negation",
+                    )),
+                    ignore_case,
+                    Some((
+                        |l: Sugg<'_>, r: Sugg<'_>| (!l).bit_and(&r),
+                        "order comparisons between booleans can be simplified",
+                    )),
                 ),
-                BinOpKind::Ne => check_comparison(
+                BinOpKind::Gt => check_comparison(
                     cx,
                     e,
-                    "inequality checks against true can be replaced by a negation",
-                    "inequality checks against false are unnecessary",
-                    |h| !h,
-                    |h| h,
+                    Some((
+                        |h: Sugg<'_>| !h,
+                        "less than comparison against true can be replaced by a negation",
+                    )),
+                    ignore_case,
+                    ignore_case,
+                    Some((|h| h, "greater than checks against false are unnecessary")),
+                    Some((
+                        |l: Sugg<'_>, r: Sugg<'_>| l.bit_and(&(!r)),
+                        "order comparisons between booleans can be simplified",
+                    )),
                 ),
                 _ => (),
             }
@@ -169,23 +196,46 @@ fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
 fn check_comparison<'a, 'tcx>(
     cx: &LateContext<'a, 'tcx>,
     e: &'tcx Expr,
-    true_message: &str,
-    false_message: &str,
-    true_hint: impl FnOnce(Sugg<'_>) -> Sugg<'_>,
-    false_hint: impl FnOnce(Sugg<'_>) -> Sugg<'_>,
+    left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
+    left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
+    right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
+    right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
+    no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &str)>,
 ) {
     use self::Expression::*;
 
     if let ExprKind::Binary(_, ref left_side, ref right_side) = e.node {
-        let applicability = Applicability::MachineApplicable;
-        match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
-            (Bool(true), Other) => suggest_bool_comparison(cx, e, right_side, applicability, true_message, true_hint),
-            (Other, Bool(true)) => suggest_bool_comparison(cx, e, left_side, applicability, true_message, true_hint),
-            (Bool(false), Other) => {
-                suggest_bool_comparison(cx, e, right_side, applicability, false_message, false_hint)
-            },
-            (Other, Bool(false)) => suggest_bool_comparison(cx, e, left_side, applicability, false_message, false_hint),
-            _ => (),
+        let (l_ty, r_ty) = (cx.tables.expr_ty(left_side), cx.tables.expr_ty(right_side));
+        if l_ty.is_bool() && r_ty.is_bool() {
+            let mut applicability = Applicability::MachineApplicable;
+            match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
+                (Bool(true), Other) => left_true.map_or((), |(h, m)| {
+                    suggest_bool_comparison(cx, e, right_side, applicability, m, h)
+                }),
+                (Other, Bool(true)) => right_true.map_or((), |(h, m)| {
+                    suggest_bool_comparison(cx, e, left_side, applicability, m, h)
+                }),
+                (Bool(false), Other) => left_false.map_or((), |(h, m)| {
+                    suggest_bool_comparison(cx, e, right_side, applicability, m, h)
+                }),
+                (Other, Bool(false)) => right_false.map_or((), |(h, m)| {
+                    suggest_bool_comparison(cx, e, left_side, applicability, m, h)
+                }),
+                (Other, Other) => no_literal.map_or((), |(h, m)| {
+                    let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability);
+                    let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability);
+                    span_lint_and_sugg(
+                        cx,
+                        BOOL_COMPARISON,
+                        e.span,
+                        m,
+                        "try simplifying it as shown",
+                        h(left_side, right_side).to_string(),
+                        applicability,
+                    )
+                }),
+                _ => (),
+            }
         }
     }
 }
@@ -196,7 +246,7 @@ fn suggest_bool_comparison<'a, 'tcx>(
     expr: &Expr,
     mut applicability: Applicability,
     message: &str,
-    conv_hint: impl FnOnce(Sugg<'_>) -> Sugg<'_>,
+    conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
 ) {
     let hint = Sugg::hir_with_applicability(cx, expr, "..", &mut applicability);
     span_lint_and_sugg(
@@ -220,7 +270,7 @@ fn fetch_bool_block(block: &Block) -> Expression {
     match (&*block.stmts, block.expr.as_ref()) {
         (&[], Some(e)) => fetch_bool_expr(&**e),
         (&[ref e], None) => {
-            if let StmtKind::Semi(ref e, _) = e.node {
+            if let StmtKind::Semi(ref e) = e.node {
                 if let ExprKind::Ret(_) = e.node {
                     fetch_bool_expr(&**e)
                 } else {