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