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