]> git.lizzy.rs Git - rust.git/commitdiff
Add a new "short_circuit_statement" lint (fixes #1194)
authorSamuel Tardieu <sam@rfc1149.net>
Thu, 29 Dec 2016 23:00:55 +0000 (00:00 +0100)
committerSamuel Tardieu <sam@rfc1149.net>
Sat, 31 Dec 2016 00:17:39 +0000 (01:17 +0100)
CHANGELOG.md
README.md
clippy_lints/src/lib.rs
clippy_lints/src/misc.rs
clippy_lints/src/no_effect.rs
tests/compile-fail/diverging_sub_expression.rs
tests/compile-fail/eq_op.rs
tests/compile-fail/short_circuit_statement.rs [new file with mode: 0644]

index 7eb5df200f069e1ace6a26b3487d024f5991ced2..6d292950564d052104f8830eaab68fbb4c254f0d 100644 (file)
@@ -372,6 +372,7 @@ All notable changes to this project will be documented in this file.
 [`shadow_reuse`]: https://github.com/Manishearth/rust-clippy/wiki#shadow_reuse
 [`shadow_same`]: https://github.com/Manishearth/rust-clippy/wiki#shadow_same
 [`shadow_unrelated`]: https://github.com/Manishearth/rust-clippy/wiki#shadow_unrelated
+[`short_circuit_statement`]: https://github.com/Manishearth/rust-clippy/wiki#short_circuit_statement
 [`should_implement_trait`]: https://github.com/Manishearth/rust-clippy/wiki#should_implement_trait
 [`similar_names`]: https://github.com/Manishearth/rust-clippy/wiki#similar_names
 [`single_char_pattern`]: https://github.com/Manishearth/rust-clippy/wiki#single_char_pattern
index 94017cd53c68663b6f902adc61e92c2c57b21a5f..85c6136b99e33a0c3abec4019742cfd6c8607cd8 100644 (file)
--- a/README.md
+++ b/README.md
@@ -179,7 +179,7 @@ transparently:
 
 ## Lints
 
-There are 181 lints included in this crate:
+There are 182 lints included in this crate:
 
 name                                                                                                                   | default | triggers on
 -----------------------------------------------------------------------------------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------
@@ -320,6 +320,7 @@ name
 [shadow_reuse](https://github.com/Manishearth/rust-clippy/wiki#shadow_reuse)                                           | allow   | rebinding a name to an expression that re-uses the original value, e.g. `let x = x + 1`
 [shadow_same](https://github.com/Manishearth/rust-clippy/wiki#shadow_same)                                             | allow   | rebinding a name to itself, e.g. `let mut x = &mut x`
 [shadow_unrelated](https://github.com/Manishearth/rust-clippy/wiki#shadow_unrelated)                                   | allow   | rebinding a name without even using the original value
+[short_circuit_statement](https://github.com/Manishearth/rust-clippy/wiki#short_circuit_statement)                     | warn    | using a short circuit boolean condition as a statement
 [should_implement_trait](https://github.com/Manishearth/rust-clippy/wiki#should_implement_trait)                       | warn    | defining a method that should be implementing a std trait
 [similar_names](https://github.com/Manishearth/rust-clippy/wiki#similar_names)                                         | allow   | similarly named items and bindings
 [single_char_pattern](https://github.com/Manishearth/rust-clippy/wiki#single_char_pattern)                             | warn    | using a single-character str where a char could be used, e.g. `_.split("x")`
index ac92d5ec97bc5ce1ae9b4bcb1439b028050ae37e..0dc2d8bc93676731774f3dc688711be762587c65 100644 (file)
@@ -421,6 +421,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
         misc::FLOAT_CMP,
         misc::MODULO_ONE,
         misc::REDUNDANT_PATTERN,
+        misc::SHORT_CIRCUIT_STATEMENT,
         misc::TOPLEVEL_REF_ARG,
         misc_early::BUILTIN_TYPE_SHADOW,
         misc_early::DOUBLE_NEG,
index 409a7de86f06d4e57ae19521587d47437f1b73d2..d1689ba6cf0c88b193c7d81f2e776ab436a6793c 100644 (file)
     "using a binding which is prefixed with an underscore"
 }
 
+/// **What it does:** Checks for the use of short circuit boolean conditions as a
+/// statement.
+///
+/// **Why is this bad?** Using a short circuit boolean condition as a statement may
+/// hide the fact that the second part is executed or not depending on the outcome of
+/// the first part.
+///
+/// **Known problems:** None.
+///
+/// **Example:**
+/// ```rust
+/// f() && g();  // We should write `if f() { g(); }`.
+/// ```
+declare_lint! {
+    pub SHORT_CIRCUIT_STATEMENT,
+    Warn,
+    "using a short circuit boolean condition as a statement"
+}
+
 #[derive(Copy, Clone)]
 pub struct Pass;
 
@@ -165,7 +184,8 @@ fn get_lints(&self) -> LintArray {
                     CMP_OWNED,
                     MODULO_ONE,
                     REDUNDANT_PATTERN,
-                    USED_UNDERSCORE_BINDING)
+                    USED_UNDERSCORE_BINDING,
+                    SHORT_CIRCUIT_STATEMENT)
     }
 }
 
@@ -224,7 +244,23 @@ fn check_stmt(&mut self, cx: &LateContext<'a, 'tcx>, s: &'tcx Stmt) {
                                                initref=initref));
                 }
             );
-        }}
+        }};
+        if_let_chain! {[
+            let StmtSemi(ref expr, _) = s.node,
+            let Expr_::ExprBinary(ref binop, ref a, ref b) = expr.node,
+            binop.node == BiAnd || binop.node == BiOr,
+            let Some(sugg) = Sugg::hir_opt(cx, a),
+        ], {
+            span_lint_and_then(cx,
+                SHORT_CIRCUIT_STATEMENT,
+                s.span,
+                "boolean short circuit operator in statement may be clearer using an explicit test",
+                |db| {
+                    let sugg = if binop.node == BiOr { !sugg } else { sugg };
+                    db.span_suggestion(s.span, "replace it with",
+                                       format!("if {} {{ {}; }}", sugg, &snippet(cx, b.span, "..")));
+                });
+        }};
     }
 
     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
index a87c9d0f7365d7642f2d1ac884345d6092d7bfff..97317d00cc41587ac2d32c291f97739e0b0ed855 100644 (file)
@@ -1,6 +1,6 @@
 use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
 use rustc::hir::def::Def;
-use rustc::hir::{Expr, Expr_, Stmt, StmtSemi, BlockCheckMode, UnsafeSource};
+use rustc::hir::{Expr, Expr_, Stmt, StmtSemi, BlockCheckMode, UnsafeSource, BiAnd, BiOr};
 use utils::{in_macro, span_lint, snippet_opt, span_lint_and_then};
 use std::ops::Deref;
 
@@ -134,8 +134,10 @@ fn reduce_expression<'a>(cx: &LateContext, expr: &'a Expr) -> Option<Vec<&'a Exp
         return None;
     }
     match expr.node {
-        Expr_::ExprIndex(ref a, ref b) |
-        Expr_::ExprBinary(_, ref a, ref b) => Some(vec![&**a, &**b]),
+        Expr_::ExprIndex(ref a, ref b) => Some(vec![&**a, &**b]),
+        Expr_::ExprBinary(ref binop, ref a, ref b) if binop.node != BiAnd && binop.node != BiOr => {
+            Some(vec![&**a, &**b])
+        },
         Expr_::ExprArray(ref v) |
         Expr_::ExprTup(ref v) => Some(v.iter().collect()),
         Expr_::ExprRepeat(ref inner, _) |
index e7daebfdf2f947bffcbcfedd69b8e87cd8fcb807..02e6d0693d1d86a83061b2f9b78aafdfc094e5ec 100644 (file)
@@ -12,7 +12,7 @@ impl A {
     fn foo(&self) -> ! { diverge() }
 }
 
-#[allow(unused_variables, unnecessary_operation)]
+#[allow(unused_variables, unnecessary_operation, short_circuit_statement)]
 fn main() {
     let b = true;
     b || diverge(); //~ ERROR sub-expression diverges
index 0ec86157d808c8eccf8336650fca3f745636b3b6..c133f42277767405e9dd5bf354489c493f0c6ef7 100644 (file)
@@ -3,7 +3,7 @@
 
 #[deny(eq_op)]
 #[allow(identity_op, double_parens)]
-#[allow(no_effect, unused_variables, unnecessary_operation)]
+#[allow(no_effect, unused_variables, unnecessary_operation, short_circuit_statement)]
 #[deny(nonminimal_bool)]
 fn main() {
     // simple values and comparisons
diff --git a/tests/compile-fail/short_circuit_statement.rs b/tests/compile-fail/short_circuit_statement.rs
new file mode 100644 (file)
index 0000000..23dfc0e
--- /dev/null
@@ -0,0 +1,27 @@
+#![feature(plugin)]
+#![plugin(clippy)]
+
+#![deny(short_circuit_statement)]
+
+fn main() {
+    f() && g();
+    //~^ ERROR boolean short circuit operator
+    //~| HELP replace it with
+    //~| SUGGESTION if f() { g(); }
+    f() || g();
+    //~^ ERROR boolean short circuit operator
+    //~| HELP replace it with
+    //~| SUGGESTION if !f() { g(); }
+    1 == 2 || g();
+    //~^ ERROR boolean short circuit operator
+    //~| HELP replace it with
+    //~| SUGGESTION if !(1 == 2) { g(); }
+}
+
+fn f() -> bool {
+    true
+}
+
+fn g() -> bool {
+    false
+}