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