about summary refs log tree commit diff
path: root/clippy_lints/src
diff options
context:
space:
mode:
authorMartin Carton <cartonmartin+github@gmail.com>2016-09-13 15:58:31 +0200
committerGitHub <noreply@github.com>2016-09-13 15:58:31 +0200
commitdc84759ac587590b9cdeeeee54ff15276d31104f (patch)
tree0b33b05b66cc2cc6d0f4a6fed49a6143cf82a312 /clippy_lints/src
parent05e734b5552ed1a04ab799f06a8dc7db2db73fe7 (diff)
parent9427a4ae8055d3ecf3ecfb2296d6a8e60868cbc0 (diff)
downloadrust-dc84759ac587590b9cdeeeee54ff15276d31104f.tar.gz
rust-dc84759ac587590b9cdeeeee54ff15276d31104f.zip
Merge pull request #1224 from oli-obk/divergence
lint diverging expressions that are sub-expressions of others
Diffstat (limited to 'clippy_lints/src')
-rw-r--r--clippy_lints/src/eval_order_dependence.rs99
-rw-r--r--clippy_lints/src/lib.rs1
2 files changed, 98 insertions, 2 deletions
diff --git a/clippy_lints/src/eval_order_dependence.rs b/clippy_lints/src/eval_order_dependence.rs
index 4465d31bff5..e53c830e11d 100644
--- a/clippy_lints/src/eval_order_dependence.rs
+++ b/clippy_lints/src/eval_order_dependence.rs
@@ -1,8 +1,9 @@
 use rustc::hir::def_id::DefId;
 use rustc::hir::intravisit::{Visitor, walk_expr};
 use rustc::hir::*;
+use rustc::ty;
 use rustc::lint::*;
-use utils::{get_parent_expr, span_note_and_lint};
+use utils::{get_parent_expr, span_note_and_lint, span_lint};
 
 /// **What it does:** Checks for a read and a write to the same variable where
 /// whether the read occurs before or after the write depends on the evaluation
@@ -26,12 +27,32 @@ declare_lint! {
     "whether a variable read occurs before a write depends on sub-expression evaluation order"
 }
 
+/// **What it does:** Checks for diverging calls that are not match arms or statements.
+///
+/// **Why is this bad?** It is often confusing to read. In addition, the
+/// sub-expression evaluation order for Rust is not well documented.
+///
+/// **Known problems:** Someone might want to use `some_bool || panic!()` as a shorthand.
+///
+/// **Example:**
+/// ```rust
+/// let a = b() || panic!() || c();
+/// // `c()` is dead, `panic!()` is only called if `b()` returns `false`
+/// let x = (a, b, c, panic!());
+/// // can simply be replaced by `panic!()`
+/// ```
+declare_lint! {
+    pub DIVERGING_SUB_EXPRESSION,
+    Warn,
+    "whether an expression contains a diverging sub expression"
+}
+
 #[derive(Copy,Clone)]
 pub struct EvalOrderDependence;
 
 impl LintPass for EvalOrderDependence {
     fn get_lints(&self) -> LintArray {
-        lint_array!(EVAL_ORDER_DEPENDENCE)
+        lint_array!(EVAL_ORDER_DEPENDENCE, DIVERGING_SUB_EXPRESSION)
     }
 }
 
@@ -56,6 +77,80 @@ impl LateLintPass for EvalOrderDependence {
             _ => {}
         }
     }
+    fn check_stmt(&mut self, cx: &LateContext, stmt: &Stmt) {
+        match stmt.node {
+            StmtExpr(ref e, _) | StmtSemi(ref e, _) => DivergenceVisitor(cx).maybe_walk_expr(e),
+            StmtDecl(ref d, _) => {
+                if let DeclLocal(ref local) = d.node {
+                    if let Local { init: Some(ref e), .. } = **local {
+                        DivergenceVisitor(cx).visit_expr(e);
+                    }
+                }
+            },
+        }
+    }
+}
+
+struct DivergenceVisitor<'a, 'tcx: 'a>(&'a LateContext<'a, 'tcx>);
+
+impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
+    fn maybe_walk_expr(&mut self, e: &Expr) {
+        match e.node {
+            ExprClosure(..) => {},
+            ExprMatch(ref e, ref arms, _) => {
+                self.visit_expr(e);
+                for arm in arms {
+                    if let Some(ref guard) = arm.guard {
+                        self.visit_expr(guard);
+                    }
+                    // make sure top level arm expressions aren't linted
+                    walk_expr(self, &*arm.body);
+                }
+            }
+            _ => walk_expr(self, e),
+        }
+    }
+    fn report_diverging_sub_expr(&mut self, e: &Expr) {
+        span_lint(
+            self.0,
+            DIVERGING_SUB_EXPRESSION,
+            e.span,
+            "sub-expression diverges",
+        );
+    }
+}
+
+impl<'a, 'tcx, 'v> Visitor<'v> for DivergenceVisitor<'a, 'tcx> {
+    fn visit_expr(&mut self, e: &'v Expr) {
+        match e.node {
+            ExprAgain(_) |
+            ExprBreak(_) |
+            ExprRet(_) => self.report_diverging_sub_expr(e),
+            ExprCall(ref func, _) => match self.0.tcx.expr_ty(func).sty {
+                ty::TyFnDef(_, _, fn_ty) |
+                ty::TyFnPtr(fn_ty) => if let ty::TyNever = self.0.tcx.erase_late_bound_regions(&fn_ty.sig).output.sty {
+                    self.report_diverging_sub_expr(e);
+                },
+                _ => {},
+            },
+            ExprMethodCall(..) => {
+                let method_call = ty::MethodCall::expr(e.id);
+                let borrowed_table = self.0.tcx.tables.borrow();
+                let method_type = borrowed_table.method_map.get(&method_call).expect("This should never happen.");
+                let result_ty = method_type.ty.fn_ret();
+                if let ty::TyNever = self.0.tcx.erase_late_bound_regions(&result_ty).sty {
+                    self.report_diverging_sub_expr(e);
+                }
+            },
+            _ => {
+                // do not lint expressions referencing objects of type `!`, as that required a diverging expression to begin with
+            },
+        }
+        self.maybe_walk_expr(e);
+    }
+    fn visit_block(&mut self, _: &'v Block) {
+        // don't continue over blocks, LateLintPass already does that
+    }
 }
 
 /// Walks up the AST from the the given write expression (`vis.write_expr`)
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 46a2a737d7c..67108eb5688 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -331,6 +331,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
         eq_op::EQ_OP,
         escape::BOXED_LOCAL,
         eta_reduction::REDUNDANT_CLOSURE,
+        eval_order_dependence::DIVERGING_SUB_EXPRESSION,
         eval_order_dependence::EVAL_ORDER_DEPENDENCE,
         format::USELESS_FORMAT,
         formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,