]> git.lizzy.rs Git - rust.git/blob - src/precedence.rs
Merged #366
[rust.git] / src / precedence.rs
1 use rustc::lint::*;
2 use syntax::codemap::Spanned;
3 use syntax::ast::*;
4 use syntax::ast_util::binop_to_string;
5
6 use utils::{span_lint, snippet};
7
8 declare_lint!(pub PRECEDENCE, Warn,
9               "catches operations where precedence may be unclear. See the wiki for a \
10                list of cases caught");
11
12 #[derive(Copy,Clone)]
13 pub struct Precedence;
14
15 impl LintPass for Precedence {
16     fn get_lints(&self) -> LintArray {
17         lint_array!(PRECEDENCE)
18     }
19 }
20
21 impl EarlyLintPass for Precedence {
22     fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
23         if let ExprBinary(Spanned { node: op, ..}, ref left, ref right) = expr.node {
24             if !is_bit_op(op) { return; }
25             match (is_arith_expr(left), is_arith_expr(right)) {
26                 (true, true) =>  span_lint(cx, PRECEDENCE, expr.span, 
27                     &format!("operator precedence can trip the unwary. \
28                          Consider parenthesizing your expression:\
29                          `({}) {} ({})`", snippet(cx, left.span, ".."),
30                          binop_to_string(op), snippet(cx, right.span, ".."))),
31                 (true, false) => span_lint(cx, PRECEDENCE, expr.span, 
32                     &format!("operator precedence can trip the unwary. \
33                          Consider parenthesizing your expression:\
34                          `({}) {} {}`", snippet(cx, left.span, ".."),
35                          binop_to_string(op), snippet(cx, right.span, ".."))),
36                 (false, true) => span_lint(cx, PRECEDENCE, expr.span, 
37                     &format!("operator precedence can trip the unwary. \
38                          Consider parenthesizing your expression:\
39                          `{} {} ({})`", snippet(cx, left.span, ".."),
40                          binop_to_string(op), snippet(cx, right.span, ".."))),
41                 _ => (),
42             }
43         }
44
45         if let ExprUnary(UnNeg, ref rhs) = expr.node {
46             if let ExprMethodCall(_, _, ref args) = rhs.node {
47                 if let Some(slf) = args.first() {
48                     if let ExprLit(ref lit) = slf.node {
49                         match lit.node {
50                             LitInt(..) | LitFloat(..) | LitFloatUnsuffixed(..) =>
51                                 span_lint(cx, PRECEDENCE, expr.span, &format!(
52                                     "unary minus has lower precedence than \
53                                      method call. Consider adding parentheses \
54                                      to clarify your intent: -({})",
55                                      snippet(cx, rhs.span, ".."))),
56                                 _ => ()
57                         }
58                     }
59                 }
60             }
61         }
62     }
63 }
64
65 fn is_arith_expr(expr : &Expr) -> bool {
66     match expr.node {
67         ExprBinary(Spanned { node: op, ..}, _, _) => is_arith_op(op),
68         _ => false
69     }
70 }
71
72 fn is_bit_op(op : BinOp_) -> bool {
73     match op {
74         BiBitXor | BiBitAnd | BiBitOr | BiShl | BiShr => true,
75         _ => false
76     }
77 }
78
79 fn is_arith_op(op : BinOp_) -> bool {
80     match op {
81         BiAdd | BiSub | BiMul | BiDiv | BiRem => true,
82         _ => false
83     }
84 }