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