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