]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/precedence.rs
Auto merge of #4992 - phansch:rustup_foobar, r=matthiaskrgr
[rust.git] / clippy_lints / src / precedence.rs
1 use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
2 use rustc::declare_lint_pass;
3 use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
4 use rustc_errors::Applicability;
5 use rustc_session::declare_tool_lint;
6 use rustc_span::source_map::Spanned;
7 use syntax::ast::*;
8
9 declare_clippy_lint! {
10     /// **What it does:** Checks for operations where precedence may be unclear
11     /// and suggests to add parentheses. Currently it catches the following:
12     /// * mixed usage of arithmetic and bit shifting/combining operators without
13     /// parentheses
14     /// * a "negative" numeric literal (which is really a unary `-` followed by a
15     /// numeric literal)
16     ///   followed by a method call
17     ///
18     /// **Why is this bad?** Not everyone knows the precedence of those operators by
19     /// heart, so expressions like these may trip others trying to reason about the
20     /// code.
21     ///
22     /// **Known problems:** None.
23     ///
24     /// **Example:**
25     /// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7
26     /// * `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1
27     pub PRECEDENCE,
28     complexity,
29     "operations where precedence may be unclear"
30 }
31
32 declare_lint_pass!(Precedence => [PRECEDENCE]);
33
34 impl EarlyLintPass for Precedence {
35     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
36         if expr.span.from_expansion() {
37             return;
38         }
39
40         if let ExprKind::Binary(Spanned { node: op, .. }, ref left, ref right) = expr.kind {
41             let span_sugg = |expr: &Expr, sugg, appl| {
42                 span_lint_and_sugg(
43                     cx,
44                     PRECEDENCE,
45                     expr.span,
46                     "operator precedence can trip the unwary",
47                     "consider parenthesizing your expression",
48                     sugg,
49                     appl,
50                 );
51             };
52
53             if !is_bit_op(op) {
54                 return;
55             }
56             let mut applicability = Applicability::MachineApplicable;
57             match (is_arith_expr(left), is_arith_expr(right)) {
58                 (true, true) => {
59                     let sugg = format!(
60                         "({}) {} ({})",
61                         snippet_with_applicability(cx, left.span, "..", &mut applicability),
62                         op.to_string(),
63                         snippet_with_applicability(cx, right.span, "..", &mut applicability)
64                     );
65                     span_sugg(expr, sugg, applicability);
66                 },
67                 (true, false) => {
68                     let sugg = format!(
69                         "({}) {} {}",
70                         snippet_with_applicability(cx, left.span, "..", &mut applicability),
71                         op.to_string(),
72                         snippet_with_applicability(cx, right.span, "..", &mut applicability)
73                     );
74                     span_sugg(expr, sugg, applicability);
75                 },
76                 (false, true) => {
77                     let sugg = format!(
78                         "{} {} ({})",
79                         snippet_with_applicability(cx, left.span, "..", &mut applicability),
80                         op.to_string(),
81                         snippet_with_applicability(cx, right.span, "..", &mut applicability)
82                     );
83                     span_sugg(expr, sugg, applicability);
84                 },
85                 (false, false) => (),
86             }
87         }
88
89         if let ExprKind::Unary(UnOp::Neg, ref rhs) = expr.kind {
90             if let ExprKind::MethodCall(_, ref args) = rhs.kind {
91                 if let Some(slf) = args.first() {
92                     if let ExprKind::Lit(ref lit) = slf.kind {
93                         match lit.kind {
94                             LitKind::Int(..) | LitKind::Float(..) => {
95                                 let mut applicability = Applicability::MachineApplicable;
96                                 span_lint_and_sugg(
97                                     cx,
98                                     PRECEDENCE,
99                                     expr.span,
100                                     "unary minus has lower precedence than method call",
101                                     "consider adding parentheses to clarify your intent",
102                                     format!(
103                                         "-({})",
104                                         snippet_with_applicability(cx, rhs.span, "..", &mut applicability)
105                                     ),
106                                     applicability,
107                                 );
108                             },
109                             _ => (),
110                         }
111                     }
112                 }
113             }
114         }
115     }
116 }
117
118 fn is_arith_expr(expr: &Expr) -> bool {
119     match expr.kind {
120         ExprKind::Binary(Spanned { node: op, .. }, _, _) => is_arith_op(op),
121         _ => false,
122     }
123 }
124
125 #[must_use]
126 fn is_bit_op(op: BinOpKind) -> bool {
127     use syntax::ast::BinOpKind::*;
128     match op {
129         BitXor | BitAnd | BitOr | Shl | Shr => true,
130         _ => false,
131     }
132 }
133
134 #[must_use]
135 fn is_arith_op(op: BinOpKind) -> bool {
136     use syntax::ast::BinOpKind::*;
137     match op {
138         Add | Sub | Mul | Div | Rem => true,
139         _ => false,
140     }
141 }