]> git.lizzy.rs Git - rust.git/blob - src/collapsible_if.rs
eae3222945c2ba7eda32f415a6f13bf4dc90730e
[rust.git] / src / collapsible_if.rs
1 //! Checks for if expressions that contain only an if expression.
2 //!
3 //! For example, the lint would catch:
4 //!
5 //! ```
6 //! if x {
7 //!     if y {
8 //!         println!("Hello world");
9 //!     }
10 //! }
11 //! ```
12 //!
13 //! This lint is **warn** by default
14
15 use rustc::plugin::Registry;
16 use rustc::lint::*;
17 use rustc::middle::def::*;
18 use syntax::ast::*;
19 use syntax::ptr::P;
20 use syntax::codemap::{Span, Spanned, ExpnInfo};
21 use syntax::print::pprust::expr_to_string;
22 use utils::{in_macro, span_lint};
23
24 declare_lint! {
25     pub COLLAPSIBLE_IF,
26     Warn,
27     "Warn on if expressions that can be collapsed"
28 }
29
30 #[derive(Copy,Clone)]
31 pub struct CollapsibleIf;
32
33 impl LintPass for CollapsibleIf {
34     fn get_lints(&self) -> LintArray {
35         lint_array!(COLLAPSIBLE_IF)
36     }
37
38     fn check_expr(&mut self, cx: &Context, expr: &Expr) {
39         cx.sess().codemap().with_expn_info(expr.span.expn_id,
40             |info| check_expr_expd(cx, expr, info))
41     }
42 }
43
44 fn check_expr_expd(cx: &Context, e: &Expr, info: Option<&ExpnInfo>) {
45     if in_macro(cx, info) { return; }
46
47     if let ExprIf(ref check, ref then, None) = e.node {
48         if let Some(&Expr{ node: ExprIf(ref check_inner, _, None), ..}) =
49             single_stmt_of_block(then) {
50                 span_lint(cx, COLLAPSIBLE_IF, e.span, &format!(
51                     "This if statement can be collapsed. Try: if {} && {}\n{:?}",
52                     check_to_string(check), check_to_string(check_inner), e));
53             }
54     }
55 }
56
57 fn requires_brackets(e: &Expr) -> bool {
58     match e.node {
59         ExprBinary(Spanned {node: n, ..}, _, _) if n == BiEq => false,
60         _ => true
61     }
62 }
63
64 fn check_to_string(e: &Expr) -> String {
65     if requires_brackets(e) {
66         format!("({})", expr_to_string(e))
67     } else {
68         format!("{}", expr_to_string(e))
69     }
70 }
71
72 fn single_stmt_of_block(block: &Block) -> Option<&Expr> {
73     if block.stmts.len() == 1 && block.expr.is_none() {
74         if let StmtExpr(ref expr, _) = block.stmts[0].node {
75             single_stmt_of_expr(expr)
76         } else { None }
77     } else {
78         if block.stmts.is_empty() {
79             if let Some(ref p) = block.expr { Some(&*p) } else { None }
80         } else { None }
81     }
82 }
83
84 fn single_stmt_of_expr(expr: &Expr) -> Option<&Expr> {
85     if let ExprBlock(ref block) = expr.node {
86         single_stmt_of_block(block)
87     } else { Some(expr) }
88 }