Skip to content

Commit dc84759

Browse files
authored
Merge pull request #1224 from oli-obk/divergence
lint diverging expressions that are sub-expressions of others
2 parents 05e734b + 9427a4a commit dc84759

File tree

5 files changed

+138
-3
lines changed

5 files changed

+138
-3
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ All notable changes to this project will be documented in this file.
211211
[`cyclomatic_complexity`]: https://github.com/Manishearth/rust-clippy/wiki#cyclomatic_complexity
212212
[`deprecated_semver`]: https://github.com/Manishearth/rust-clippy/wiki#deprecated_semver
213213
[`derive_hash_xor_eq`]: https://github.com/Manishearth/rust-clippy/wiki#derive_hash_xor_eq
214+
[`diverging_sub_expression`]: https://github.com/Manishearth/rust-clippy/wiki#diverging_sub_expression
214215
[`doc_markdown`]: https://github.com/Manishearth/rust-clippy/wiki#doc_markdown
215216
[`double_neg`]: https://github.com/Manishearth/rust-clippy/wiki#double_neg
216217
[`drop_ref`]: https://github.com/Manishearth/rust-clippy/wiki#drop_ref

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Table of contents:
1717

1818
## Lints
1919

20-
There are 170 lints included in this crate:
20+
There are 171 lints included in this crate:
2121

2222
name | default | triggers on
2323
---------------------------------------------------------------------------------------------------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------
@@ -50,6 +50,7 @@ name
5050
[cyclomatic_complexity](https://github.com/Manishearth/rust-clippy/wiki#cyclomatic_complexity) | warn | functions that should be split up into multiple functions
5151
[deprecated_semver](https://github.com/Manishearth/rust-clippy/wiki#deprecated_semver) | warn | use of `#[deprecated(since = "x")]` where x is not semver
5252
[derive_hash_xor_eq](https://github.com/Manishearth/rust-clippy/wiki#derive_hash_xor_eq) | warn | deriving `Hash` but implementing `PartialEq` explicitly
53+
[diverging_sub_expression](https://github.com/Manishearth/rust-clippy/wiki#diverging_sub_expression) | warn | whether an expression contains a diverging sub expression
5354
[doc_markdown](https://github.com/Manishearth/rust-clippy/wiki#doc_markdown) | warn | presence of `_`, `::` or camel-case outside backticks in documentation
5455
[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++
5556
[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

clippy_lints/src/eval_order_dependence.rs

+97-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use rustc::hir::def_id::DefId;
22
use rustc::hir::intravisit::{Visitor, walk_expr};
33
use rustc::hir::*;
4+
use rustc::ty;
45
use rustc::lint::*;
5-
use utils::{get_parent_expr, span_note_and_lint};
6+
use utils::{get_parent_expr, span_note_and_lint, span_lint};
67

78
/// **What it does:** Checks for a read and a write to the same variable where
89
/// whether the read occurs before or after the write depends on the evaluation
@@ -26,12 +27,32 @@ declare_lint! {
2627
"whether a variable read occurs before a write depends on sub-expression evaluation order"
2728
}
2829

30+
/// **What it does:** Checks for diverging calls that are not match arms or statements.
31+
///
32+
/// **Why is this bad?** It is often confusing to read. In addition, the
33+
/// sub-expression evaluation order for Rust is not well documented.
34+
///
35+
/// **Known problems:** Someone might want to use `some_bool || panic!()` as a shorthand.
36+
///
37+
/// **Example:**
38+
/// ```rust
39+
/// let a = b() || panic!() || c();
40+
/// // `c()` is dead, `panic!()` is only called if `b()` returns `false`
41+
/// let x = (a, b, c, panic!());
42+
/// // can simply be replaced by `panic!()`
43+
/// ```
44+
declare_lint! {
45+
pub DIVERGING_SUB_EXPRESSION,
46+
Warn,
47+
"whether an expression contains a diverging sub expression"
48+
}
49+
2950
#[derive(Copy,Clone)]
3051
pub struct EvalOrderDependence;
3152

3253
impl LintPass for EvalOrderDependence {
3354
fn get_lints(&self) -> LintArray {
34-
lint_array!(EVAL_ORDER_DEPENDENCE)
55+
lint_array!(EVAL_ORDER_DEPENDENCE, DIVERGING_SUB_EXPRESSION)
3556
}
3657
}
3758

@@ -56,6 +77,80 @@ impl LateLintPass for EvalOrderDependence {
5677
_ => {}
5778
}
5879
}
80+
fn check_stmt(&mut self, cx: &LateContext, stmt: &Stmt) {
81+
match stmt.node {
82+
StmtExpr(ref e, _) | StmtSemi(ref e, _) => DivergenceVisitor(cx).maybe_walk_expr(e),
83+
StmtDecl(ref d, _) => {
84+
if let DeclLocal(ref local) = d.node {
85+
if let Local { init: Some(ref e), .. } = **local {
86+
DivergenceVisitor(cx).visit_expr(e);
87+
}
88+
}
89+
},
90+
}
91+
}
92+
}
93+
94+
struct DivergenceVisitor<'a, 'tcx: 'a>(&'a LateContext<'a, 'tcx>);
95+
96+
impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
97+
fn maybe_walk_expr(&mut self, e: &Expr) {
98+
match e.node {
99+
ExprClosure(..) => {},
100+
ExprMatch(ref e, ref arms, _) => {
101+
self.visit_expr(e);
102+
for arm in arms {
103+
if let Some(ref guard) = arm.guard {
104+
self.visit_expr(guard);
105+
}
106+
// make sure top level arm expressions aren't linted
107+
walk_expr(self, &*arm.body);
108+
}
109+
}
110+
_ => walk_expr(self, e),
111+
}
112+
}
113+
fn report_diverging_sub_expr(&mut self, e: &Expr) {
114+
span_lint(
115+
self.0,
116+
DIVERGING_SUB_EXPRESSION,
117+
e.span,
118+
"sub-expression diverges",
119+
);
120+
}
121+
}
122+
123+
impl<'a, 'tcx, 'v> Visitor<'v> for DivergenceVisitor<'a, 'tcx> {
124+
fn visit_expr(&mut self, e: &'v Expr) {
125+
match e.node {
126+
ExprAgain(_) |
127+
ExprBreak(_) |
128+
ExprRet(_) => self.report_diverging_sub_expr(e),
129+
ExprCall(ref func, _) => match self.0.tcx.expr_ty(func).sty {
130+
ty::TyFnDef(_, _, fn_ty) |
131+
ty::TyFnPtr(fn_ty) => if let ty::TyNever = self.0.tcx.erase_late_bound_regions(&fn_ty.sig).output.sty {
132+
self.report_diverging_sub_expr(e);
133+
},
134+
_ => {},
135+
},
136+
ExprMethodCall(..) => {
137+
let method_call = ty::MethodCall::expr(e.id);
138+
let borrowed_table = self.0.tcx.tables.borrow();
139+
let method_type = borrowed_table.method_map.get(&method_call).expect("This should never happen.");
140+
let result_ty = method_type.ty.fn_ret();
141+
if let ty::TyNever = self.0.tcx.erase_late_bound_regions(&result_ty).sty {
142+
self.report_diverging_sub_expr(e);
143+
}
144+
},
145+
_ => {
146+
// do not lint expressions referencing objects of type `!`, as that required a diverging expression to begin with
147+
},
148+
}
149+
self.maybe_walk_expr(e);
150+
}
151+
fn visit_block(&mut self, _: &'v Block) {
152+
// don't continue over blocks, LateLintPass already does that
153+
}
59154
}
60155

61156
/// Walks up the AST from the the given write expression (`vis.write_expr`)

clippy_lints/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry) {
331331
eq_op::EQ_OP,
332332
escape::BOXED_LOCAL,
333333
eta_reduction::REDUNDANT_CLOSURE,
334+
eval_order_dependence::DIVERGING_SUB_EXPRESSION,
334335
eval_order_dependence::EVAL_ORDER_DEPENDENCE,
335336
format::USELESS_FORMAT,
336337
formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#![feature(plugin, never_type)]
2+
#![plugin(clippy)]
3+
#![deny(diverging_sub_expression)]
4+
5+
#[allow(empty_loop)]
6+
fn diverge() -> ! { loop {} }
7+
8+
struct A;
9+
10+
impl A {
11+
fn foo(&self) -> ! { diverge() }
12+
}
13+
14+
#[allow(unused_variables, unnecessary_operation)]
15+
fn main() {
16+
let b = true;
17+
b || diverge(); //~ ERROR sub-expression diverges
18+
b || A.foo(); //~ ERROR sub-expression diverges
19+
let y = (5, diverge(), 6); //~ ERROR sub-expression diverges
20+
println!("{}", y.1);
21+
}
22+
23+
#[allow(dead_code, unused_variables)]
24+
fn foobar() {
25+
loop {
26+
let x = match 5 {
27+
4 => return,
28+
5 => continue,
29+
6 => (println!("foo"), return), //~ ERROR sub-expression diverges
30+
7 => (println!("bar"), continue), //~ ERROR sub-expression diverges
31+
8 => break,
32+
9 => diverge(),
33+
3 => (println!("moo"), diverge()), //~ ERROR sub-expression diverges
34+
_ => (println!("boo"), break), //~ ERROR sub-expression diverges
35+
};
36+
}
37+
}

0 commit comments

Comments
 (0)