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