]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/precedence.rs
Improve docs
[rust.git] / clippy_lints / src / precedence.rs
1 use rustc::lint::*;
2 use syntax::ast::*;
3 use syntax::codemap::Spanned;
4 use utils::{span_lint, snippet};
5
6 /// **What it does:** This lint checks for operations where precedence may be unclear and suggests
7 /// 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)
10 ///   followed by a method call
11 ///
12 /// **Why is this bad?** Because not everyone knows the precedence of those operators by heart, so
13 /// expressions like these may trip others trying to reason about the code.
14 ///
15 /// **Known problems:** None
16 ///
17 /// **Examples:**
18 /// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7
19 /// * `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1
20 declare_lint! {
21     pub PRECEDENCE, Warn,
22     "catches operations where precedence may be unclear"
23 }
24
25 #[derive(Copy,Clone)]
26 pub struct Precedence;
27
28 impl LintPass for Precedence {
29     fn get_lints(&self) -> LintArray {
30         lint_array!(PRECEDENCE)
31     }
32 }
33
34 impl EarlyLintPass for Precedence {
35     fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
36         if let ExprKind::Binary(Spanned { node: op, .. }, ref left, ref right) = expr.node {
37             if !is_bit_op(op) {
38                 return;
39             }
40             match (is_arith_expr(left), is_arith_expr(right)) {
41                 (true, true) => {
42                     span_lint(cx,
43                               PRECEDENCE,
44                               expr.span,
45                               &format!("operator precedence can trip the unwary. Consider parenthesizing your \
46                                         expression:`({}) {} ({})`",
47                                        snippet(cx, left.span, ".."),
48                                        op.to_string(),
49                                        snippet(cx, right.span, "..")));
50                 }
51                 (true, false) => {
52                     span_lint(cx,
53                               PRECEDENCE,
54                               expr.span,
55                               &format!("operator precedence can trip the unwary. Consider parenthesizing your \
56                                         expression:`({}) {} {}`",
57                                        snippet(cx, left.span, ".."),
58                                        op.to_string(),
59                                        snippet(cx, right.span, "..")));
60                 }
61                 (false, true) => {
62                     span_lint(cx,
63                               PRECEDENCE,
64                               expr.span,
65                               &format!("operator precedence can trip the unwary. Consider parenthesizing your \
66                                         expression:`{} {} ({})`",
67                                        snippet(cx, left.span, ".."),
68                                        op.to_string(),
69                                        snippet(cx, right.span, "..")));
70                 }
71                 _ => (),
72             }
73         }
74
75         if let ExprKind::Unary(UnOp::Neg, ref rhs) = expr.node {
76             if let ExprKind::MethodCall(_, _, ref args) = rhs.node {
77                 if let Some(slf) = args.first() {
78                     if let ExprKind::Lit(ref lit) = slf.node {
79                         match lit.node {
80                             LitKind::Int(..) |
81                             LitKind::Float(..) |
82                             LitKind::FloatUnsuffixed(..) => {
83                                 span_lint(cx,
84                                           PRECEDENCE,
85                                           expr.span,
86                                           &format!("unary minus has lower precedence than method call. Consider \
87                                                     adding parentheses to clarify your intent: -({})",
88                                                    snippet(cx, rhs.span, "..")));
89                             }
90                             _ => (),
91                         }
92                     }
93                 }
94             }
95         }
96     }
97 }
98
99 fn is_arith_expr(expr: &Expr) -> bool {
100     match expr.node {
101         ExprKind::Binary(Spanned { node: op, .. }, _, _) => is_arith_op(op),
102         _ => false,
103     }
104 }
105
106 fn is_bit_op(op: BinOpKind) -> bool {
107     use syntax::ast::BinOpKind::*;
108     match op {
109         BitXor | BitAnd | BitOr | Shl | Shr => true,
110         _ => false,
111     }
112 }
113
114 fn is_arith_op(op: BinOpKind) -> bool {
115     use syntax::ast::BinOpKind::*;
116     match op {
117         Add | Sub | Mul | Div | Rem => true,
118         _ => false,
119     }
120 }