]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/needless_bool.rs
Merge #3392
[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         if in_macro(e.span) {
137             return;
138         }
139         use self::Expression::*;
140         if let ExprKind::Binary(Spanned { node: BinOpKind::Eq, .. }, ref left_side, ref right_side) = e.node {
141             match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
142                 (Bool(true), Other) => {
143                     let hint = snippet(cx, right_side.span, "..").into_owned();
144                     span_lint_and_sugg(
145                         cx,
146                         BOOL_COMPARISON,
147                         e.span,
148                         "equality checks against true are unnecessary",
149                         "try simplifying it as shown",
150                         hint,
151                     );
152                 },
153                 (Other, Bool(true)) => {
154                     let hint = snippet(cx, left_side.span, "..").into_owned();
155                     span_lint_and_sugg(
156                         cx,
157                         BOOL_COMPARISON,
158                         e.span,
159                         "equality checks against true are unnecessary",
160                         "try simplifying it as shown",
161                         hint,
162                     );
163                 },
164                 (Bool(false), Other) => {
165                     let hint = Sugg::hir(cx, right_side, "..");
166                     span_lint_and_sugg(
167                         cx,
168                         BOOL_COMPARISON,
169                         e.span,
170                         "equality checks against false can be replaced by a negation",
171                         "try simplifying it as shown",
172                         (!hint).to_string(),
173                     );
174                 },
175                 (Other, Bool(false)) => {
176                     let hint = Sugg::hir(cx, left_side, "..");
177                     span_lint_and_sugg(
178                         cx,
179                         BOOL_COMPARISON,
180                         e.span,
181                         "equality checks against false can be replaced by a negation",
182                         "try simplifying it as shown",
183                         (!hint).to_string(),
184                     );
185                 },
186                 _ => (),
187             }
188         }
189     }
190 }
191
192 enum Expression {
193     Bool(bool),
194     RetBool(bool),
195     Other,
196 }
197
198 fn fetch_bool_block(block: &Block) -> Expression {
199     match (&*block.stmts, block.expr.as_ref()) {
200         (&[], Some(e)) => fetch_bool_expr(&**e),
201         (&[ref e], None) => if let StmtKind::Semi(ref e, _) = e.node {
202             if let ExprKind::Ret(_) = e.node {
203                 fetch_bool_expr(&**e)
204             } else {
205                 Expression::Other
206             }
207         } else {
208             Expression::Other
209         },
210         _ => Expression::Other,
211     }
212 }
213
214 fn fetch_bool_expr(expr: &Expr) -> Expression {
215     match expr.node {
216         ExprKind::Block(ref block, _) => fetch_bool_block(block),
217         ExprKind::Lit(ref lit_ptr) => if let LitKind::Bool(value) = lit_ptr.node {
218             Expression::Bool(value)
219         } else {
220             Expression::Other
221         },
222         ExprKind::Ret(Some(ref expr)) => match fetch_bool_expr(expr) {
223             Expression::Bool(value) => Expression::RetBool(value),
224             _ => Expression::Other,
225         },
226         _ => Expression::Other,
227     }
228 }