Merge pull request #1224 from oli-obk/divergence

lint diverging expressions that are sub-expressions of others
This commit is contained in:
Martin Carton 2016-09-13 15:58:31 +02:00 committed by GitHub
commit dc84759ac5
5 changed files with 138 additions and 3 deletions

View file

@ -211,6 +211,7 @@ All notable changes to this project will be documented in this file.
[`cyclomatic_complexity`]: https://github.com/Manishearth/rust-clippy/wiki#cyclomatic_complexity
[`deprecated_semver`]: https://github.com/Manishearth/rust-clippy/wiki#deprecated_semver
[`derive_hash_xor_eq`]: https://github.com/Manishearth/rust-clippy/wiki#derive_hash_xor_eq
[`diverging_sub_expression`]: https://github.com/Manishearth/rust-clippy/wiki#diverging_sub_expression
[`doc_markdown`]: https://github.com/Manishearth/rust-clippy/wiki#doc_markdown
[`double_neg`]: https://github.com/Manishearth/rust-clippy/wiki#double_neg
[`drop_ref`]: https://github.com/Manishearth/rust-clippy/wiki#drop_ref

View file

@ -17,7 +17,7 @@ Table of contents:
## Lints
There are 170 lints included in this crate:
There are 171 lints included in this crate:
name | default | triggers on
---------------------------------------------------------------------------------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------
@ -50,6 +50,7 @@ name
[cyclomatic_complexity](https://github.com/Manishearth/rust-clippy/wiki#cyclomatic_complexity) | warn | functions that should be split up into multiple functions
[deprecated_semver](https://github.com/Manishearth/rust-clippy/wiki#deprecated_semver) | warn | use of `#[deprecated(since = "x")]` where x is not semver
[derive_hash_xor_eq](https://github.com/Manishearth/rust-clippy/wiki#derive_hash_xor_eq) | warn | deriving `Hash` but implementing `PartialEq` explicitly
[diverging_sub_expression](https://github.com/Manishearth/rust-clippy/wiki#diverging_sub_expression) | warn | whether an expression contains a diverging sub expression
[doc_markdown](https://github.com/Manishearth/rust-clippy/wiki#doc_markdown) | warn | presence of `_`, `::` or camel-case outside backticks in documentation
[double_neg](https://github.com/Manishearth/rust-clippy/wiki#double_neg) | warn | `--x`, which is a double negation of `x` and not a pre-decrement as in C/C++
[drop_ref](https://github.com/Manishearth/rust-clippy/wiki#drop_ref) | warn | calls to `std::mem::drop` with a reference instead of an owned value

View file

@ -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`)

View file

@ -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,

View file

@ -0,0 +1,37 @@
#![feature(plugin, never_type)]
#![plugin(clippy)]
#![deny(diverging_sub_expression)]
#[allow(empty_loop)]
fn diverge() -> ! { loop {} }
struct A;
impl A {
fn foo(&self) -> ! { diverge() }
}
#[allow(unused_variables, unnecessary_operation)]
fn main() {
let b = true;
b || diverge(); //~ ERROR sub-expression diverges
b || A.foo(); //~ ERROR sub-expression diverges
let y = (5, diverge(), 6); //~ ERROR sub-expression diverges
println!("{}", y.1);
}
#[allow(dead_code, unused_variables)]
fn foobar() {
loop {
let x = match 5 {
4 => return,
5 => continue,
6 => (println!("foo"), return), //~ ERROR sub-expression diverges
7 => (println!("bar"), continue), //~ ERROR sub-expression diverges
8 => break,
9 => diverge(),
3 => (println!("moo"), diverge()), //~ ERROR sub-expression diverges
_ => (println!("boo"), break), //~ ERROR sub-expression diverges
};
}
}