]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/needless_bool.rs
Merge #3399
[rust.git] / clippy_lints / src / needless_bool.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 //! Checks for needless boolean results of if-else expressions
12 //!
13 //! This lint is **warn** by default
14
15 use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
16 use crate::rustc::{declare_tool_lint, lint_array};
17 use crate::rustc::hir::*;
18 use crate::syntax::ast::LitKind;
19 use crate::syntax::source_map::Spanned;
20 use crate::utils::{in_macro, snippet, span_lint, span_lint_and_sugg};
21 use crate::utils::sugg::Sugg;
22
23 /// **What it does:** Checks for expressions of the form `if c { true } else {
24 /// false }`
25 /// (or vice versa) and suggest using the condition directly.
26 ///
27 /// **Why is this bad?** Redundant code.
28 ///
29 /// **Known problems:** Maybe false positives: Sometimes, the two branches are
30 /// painstakingly documented (which we of course do not detect), so they *may*
31 /// have some value. Even then, the documentation can be rewritten to match the
32 /// shorter code.
33 ///
34 /// **Example:**
35 /// ```rust
36 /// if x { false } else { true }
37 /// ```
38 declare_clippy_lint! {
39     pub NEEDLESS_BOOL,
40     complexity,
41     "if-statements with plain booleans in the then- and else-clause, e.g. \
42      `if p { true } else { false }`"
43 }
44
45 /// **What it does:** Checks for expressions of the form `x == true` (or vice
46 /// versa) and suggest using the variable directly.
47 ///
48 /// **Why is this bad?** Unnecessary code.
49 ///
50 /// **Known problems:** None.
51 ///
52 /// **Example:**
53 /// ```rust
54 /// if x == true { }  // could be `if x { }`
55 /// ```
56 declare_clippy_lint! {
57     pub BOOL_COMPARISON,
58     complexity,
59     "comparing a variable to a boolean, e.g. `if x == true`"
60 }
61
62 #[derive(Copy, Clone)]
63 pub struct NeedlessBool;
64
65 impl LintPass for NeedlessBool {
66     fn get_lints(&self) -> LintArray {
67         lint_array!(NEEDLESS_BOOL)
68     }
69 }
70
71 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBool {
72     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
73         use self::Expression::*;
74         if let ExprKind::If(ref pred, ref then_block, Some(ref else_expr)) = e.node {
75             let reduce = |ret, not| {
76                 let snip = Sugg::hir(cx, pred, "<predicate>");
77                 let snip = if not { !snip } else { snip };
78
79                 let hint = if ret {
80                     format!("return {}", snip)
81                 } else {
82                     snip.to_string()
83                 };
84
85                 span_lint_and_sugg(
86                     cx,
87                     NEEDLESS_BOOL,
88                     e.span,
89                     "this if-then-else expression returns a bool literal",
90                     "you can reduce it to",
91                     hint,
92                 );
93             };
94             if let ExprKind::Block(ref then_block, _) = then_block.node {
95                 match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) {
96                     (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => {
97                         span_lint(
98                             cx,
99                             NEEDLESS_BOOL,
100                             e.span,
101                             "this if-then-else expression will always return true",
102                         );
103                     },
104                     (RetBool(false), RetBool(false)) | (Bool(false), Bool(false)) => {
105                         span_lint(
106                             cx,
107                             NEEDLESS_BOOL,
108                             e.span,
109                             "this if-then-else expression will always return false",
110                         );
111                     },
112                     (RetBool(true), RetBool(false)) => reduce(true, false),
113                     (Bool(true), Bool(false)) => reduce(false, false),
114                     (RetBool(false), RetBool(true)) => reduce(true, true),
115                     (Bool(false), Bool(true)) => reduce(false, true),
116                     _ => (),
117                 }
118             } else {
119                 panic!("IfExpr 'then' node is not an ExprKind::Block");
120             }
121         }
122     }
123 }
124
125 #[derive(Copy, Clone)]
126 pub struct BoolComparison;
127
128 impl LintPass for BoolComparison {
129     fn get_lints(&self) -> LintArray {
130         lint_array!(BOOL_COMPARISON)
131     }
132 }
133
134 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoolComparison {
135     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
136         use self::Expression::*;
137
138         if in_macro(e.span) {
139             return;
140         }
141
142         if let ExprKind::Binary(Spanned { node: BinOpKind::Eq, .. }, ref left_side, ref right_side) = e.node {
143             match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
144                 (Bool(true), Other) => {
145                     let hint = snippet(cx, right_side.span, "..").into_owned();
146                     span_lint_and_sugg(
147                         cx,
148                         BOOL_COMPARISON,
149                         e.span,
150                         "equality checks against true are unnecessary",
151                         "try simplifying it as shown",
152                         hint,
153                     );
154                 },
155                 (Other, Bool(true)) => {
156                     let hint = snippet(cx, left_side.span, "..").into_owned();
157                     span_lint_and_sugg(
158                         cx,
159                         BOOL_COMPARISON,
160                         e.span,
161                         "equality checks against true are unnecessary",
162                         "try simplifying it as shown",
163                         hint,
164                     );
165                 },
166                 (Bool(false), Other) => {
167                     let hint = Sugg::hir(cx, right_side, "..");
168                     span_lint_and_sugg(
169                         cx,
170                         BOOL_COMPARISON,
171                         e.span,
172                         "equality checks against false can be replaced by a negation",
173                         "try simplifying it as shown",
174                         (!hint).to_string(),
175                     );
176                 },
177                 (Other, Bool(false)) => {
178                     let hint = Sugg::hir(cx, left_side, "..");
179                     span_lint_and_sugg(
180                         cx,
181                         BOOL_COMPARISON,
182                         e.span,
183                         "equality checks against false can be replaced by a negation",
184                         "try simplifying it as shown",
185                         (!hint).to_string(),
186                     );
187                 },
188                 _ => (),
189             }
190         }
191     }
192 }
193
194 enum Expression {
195     Bool(bool),
196     RetBool(bool),
197     Other,
198 }
199
200 fn fetch_bool_block(block: &Block) -> Expression {
201     match (&*block.stmts, block.expr.as_ref()) {
202         (&[], Some(e)) => fetch_bool_expr(&**e),
203         (&[ref e], None) => if let StmtKind::Semi(ref e, _) = e.node {
204             if let ExprKind::Ret(_) = e.node {
205                 fetch_bool_expr(&**e)
206             } else {
207                 Expression::Other
208             }
209         } else {
210             Expression::Other
211         },
212         _ => Expression::Other,
213     }
214 }
215
216 fn fetch_bool_expr(expr: &Expr) -> Expression {
217     match expr.node {
218         ExprKind::Block(ref block, _) => fetch_bool_block(block),
219         ExprKind::Lit(ref lit_ptr) => if let LitKind::Bool(value) = lit_ptr.node {
220             Expression::Bool(value)
221         } else {
222             Expression::Other
223         },
224         ExprKind::Ret(Some(ref expr)) => match fetch_bool_expr(expr) {
225             Expression::Bool(value) => Expression::RetBool(value),
226             _ => Expression::Other,
227         },
228         _ => Expression::Other,
229     }
230 }