]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/int_plus_one.rs
Auto merge of #3598 - xfix:apply-cargo-fix-edition-idioms, r=phansch
[rust.git] / clippy_lints / src / int_plus_one.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 //! lint on blocks unnecessarily using >= with a + 1 or - 1
11
12 use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
13 use rustc::{declare_tool_lint, lint_array};
14 use rustc_errors::Applicability;
15 use syntax::ast::*;
16
17 use crate::utils::{snippet_opt, span_lint_and_then};
18
19 /// **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block
20 ///
21 ///
22 /// **Why is this bad?** Readability -- better to use `> y` instead of `>= y + 1`.
23 ///
24 /// **Known problems:** None.
25 ///
26 /// **Example:**
27 /// ```rust
28 /// x >= y + 1
29 /// ```
30 ///
31 /// Could be written:
32 ///
33 /// ```rust
34 /// x > y
35 /// ```
36 declare_clippy_lint! {
37     pub INT_PLUS_ONE,
38     complexity,
39     "instead of using x >= y + 1, use x > y"
40 }
41
42 pub struct IntPlusOne;
43
44 impl LintPass for IntPlusOne {
45     fn get_lints(&self) -> LintArray {
46         lint_array!(INT_PLUS_ONE)
47     }
48 }
49
50 // cases:
51 // BinOpKind::Ge
52 // x >= y + 1
53 // x - 1 >= y
54 //
55 // BinOpKind::Le
56 // x + 1 <= y
57 // x <= y - 1
58
59 #[derive(Copy, Clone)]
60 enum Side {
61     LHS,
62     RHS,
63 }
64
65 impl IntPlusOne {
66     #[allow(clippy::cast_sign_loss)]
67     fn check_lit(&self, lit: &Lit, target_value: i128) -> bool {
68         if let LitKind::Int(value, ..) = lit.node {
69             return value == (target_value as u128);
70         }
71         false
72     }
73
74     fn check_binop(&self, cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr) -> Option<String> {
75         match (binop, &lhs.node, &rhs.node) {
76             // case where `x - 1 >= ...` or `-1 + x >= ...`
77             (BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => {
78                 match (lhskind.node, &lhslhs.node, &lhsrhs.node) {
79                     // `-1 + x`
80                     (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if self.check_lit(lit, -1) => {
81                         self.generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS)
82                     },
83                     // `x - 1`
84                     (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if self.check_lit(lit, 1) => {
85                         self.generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS)
86                     },
87                     _ => None,
88                 }
89             },
90             // case where `... >= y + 1` or `... >= 1 + y`
91             (BinOpKind::Ge, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs))
92                 if rhskind.node == BinOpKind::Add =>
93             {
94                 match (&rhslhs.node, &rhsrhs.node) {
95                     // `y + 1` and `1 + y`
96                     (&ExprKind::Lit(ref lit), _) if self.check_lit(lit, 1) => {
97                         self.generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS)
98                     },
99                     (_, &ExprKind::Lit(ref lit)) if self.check_lit(lit, 1) => {
100                         self.generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS)
101                     },
102                     _ => None,
103                 }
104             }
105             // case where `x + 1 <= ...` or `1 + x <= ...`
106             (BinOpKind::Le, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _)
107                 if lhskind.node == BinOpKind::Add =>
108             {
109                 match (&lhslhs.node, &lhsrhs.node) {
110                     // `1 + x` and `x + 1`
111                     (&ExprKind::Lit(ref lit), _) if self.check_lit(lit, 1) => {
112                         self.generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS)
113                     },
114                     (_, &ExprKind::Lit(ref lit)) if self.check_lit(lit, 1) => {
115                         self.generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS)
116                     },
117                     _ => None,
118                 }
119             }
120             // case where `... >= y - 1` or `... >= -1 + y`
121             (BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => {
122                 match (rhskind.node, &rhslhs.node, &rhsrhs.node) {
123                     // `-1 + y`
124                     (BinOpKind::Add, &ExprKind::Lit(ref lit), _) if self.check_lit(lit, -1) => {
125                         self.generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS)
126                     },
127                     // `y - 1`
128                     (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) if self.check_lit(lit, 1) => {
129                         self.generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS)
130                     },
131                     _ => None,
132                 }
133             },
134             _ => None,
135         }
136     }
137
138     fn generate_recommendation(
139         &self,
140         cx: &EarlyContext<'_>,
141         binop: BinOpKind,
142         node: &Expr,
143         other_side: &Expr,
144         side: Side,
145     ) -> Option<String> {
146         let binop_string = match binop {
147             BinOpKind::Ge => ">",
148             BinOpKind::Le => "<",
149             _ => return None,
150         };
151         if let Some(snippet) = snippet_opt(cx, node.span) {
152             if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) {
153                 let rec = match side {
154                     Side::LHS => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)),
155                     Side::RHS => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)),
156                 };
157                 return rec;
158             }
159         }
160         None
161     }
162
163     fn emit_warning(&self, cx: &EarlyContext<'_>, block: &Expr, recommendation: String) {
164         span_lint_and_then(
165             cx,
166             INT_PLUS_ONE,
167             block.span,
168             "Unnecessary `>= y + 1` or `x - 1 >=`",
169             |db| {
170                 db.span_suggestion_with_applicability(
171                     block.span,
172                     "change `>= y + 1` to `> y` as shown",
173                     recommendation,
174                     Applicability::MachineApplicable, // snippet
175                 );
176             },
177         );
178     }
179 }
180
181 impl EarlyLintPass for IntPlusOne {
182     fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) {
183         if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.node {
184             if let Some(ref rec) = self.check_binop(cx, kind.node, lhs, rhs) {
185                 self.emit_warning(cx, item, rec.clone());
186             }
187         }
188     }
189 }