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