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