]> git.lizzy.rs Git - rust.git/blob - src/collapsible_if.rs
moved in_macro to (new) utils.rs
[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;
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                         cx.span_lint(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 }