]> git.lizzy.rs Git - rust.git/blob - src/precedence.rs
Rust upgrade to rustc 1.7.0-nightly (8ad12c3e2 2015-12-19)
[rust.git] / src / precedence.rs
1 use rustc::lint::*;
2 use syntax::codemap::Spanned;
3 use syntax::ast::*;
4
5 use utils::{span_lint, snippet};
6
7 /// **What it does:** This lint checks for operations where precedence may be unclear and `Warn`'s about them by default, suggesting to add parentheses. Currently it catches the following:
8 /// * mixed usage of arithmetic and bit shifting/combining operators without parentheses
9 /// * a "negative" numeric literal (which is really a unary `-` followed by a numeric literal) followed by a method call
10 ///
11 /// **Why is this bad?** Because not everyone knows the precedence of those operators by heart, so expressions like these may trip others trying to reason about the code.
12 ///
13 /// **Known problems:** None
14 ///
15 /// **Examples:**
16 /// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7
17 /// * `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1
18 declare_lint!(pub PRECEDENCE, Warn,
19               "catches operations where precedence may be unclear. See the wiki for a \
20                list of cases caught");
21
22 #[derive(Copy,Clone)]
23 pub struct Precedence;
24
25 impl LintPass for Precedence {
26     fn get_lints(&self) -> LintArray {
27         lint_array!(PRECEDENCE)
28     }
29 }
30
31 impl EarlyLintPass for Precedence {
32     fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
33         if let ExprBinary(Spanned { node: op, ..}, ref left, ref right) = expr.node {
34             if !is_bit_op(op) { return; }
35             match (is_arith_expr(left), is_arith_expr(right)) {
36                 (true, 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                          op.to_string(), snippet(cx, right.span, ".."))),
41                 (true, false) => span_lint(cx, PRECEDENCE, expr.span, 
42                     &format!("operator precedence can trip the unwary. \
43                          Consider parenthesizing your expression:\
44                          `({}) {} {}`", snippet(cx, left.span, ".."),
45                          op.to_string(), snippet(cx, right.span, ".."))),
46                 (false, true) => span_lint(cx, PRECEDENCE, expr.span, 
47                     &format!("operator precedence can trip the unwary. \
48                          Consider parenthesizing your expression:\
49                          `{} {} ({})`", snippet(cx, left.span, ".."),
50                          op.to_string(), snippet(cx, right.span, ".."))),
51                 _ => (),
52             }
53         }
54
55         if let ExprUnary(UnNeg, ref rhs) = expr.node {
56             if let ExprMethodCall(_, _, ref args) = rhs.node {
57                 if let Some(slf) = args.first() {
58                     if let ExprLit(ref lit) = slf.node {
59                         match lit.node {
60                             LitInt(..) | LitFloat(..) | LitFloatUnsuffixed(..) =>
61                                 span_lint(cx, PRECEDENCE, expr.span, &format!(
62                                     "unary minus has lower precedence than \
63                                      method call. Consider adding parentheses \
64                                      to clarify your intent: -({})",
65                                      snippet(cx, rhs.span, ".."))),
66                             _ => ()
67                         }
68                     }
69                 }
70             }
71         }
72     }
73 }
74
75 fn is_arith_expr(expr: &Expr) -> bool {
76     match expr.node {
77         ExprBinary(Spanned { node: op, ..}, _, _) => is_arith_op(op),
78         _ => false
79     }
80 }
81
82 fn is_bit_op(op: BinOp_) -> bool {
83     match op {
84         BiBitXor | BiBitAnd | BiBitOr | BiShl | BiShr => true,
85         _ => false
86     }
87 }
88
89 fn is_arith_op(op: BinOp_) -> bool {
90     match op {
91         BiAdd | BiSub | BiMul | BiDiv | BiRem => true,
92         _ => false
93     }
94 }