]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/needless_bool.rs
Auto merge of #3845 - euclio:unused-comments, r=phansch
[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 crate::utils::sugg::Sugg;
6 use crate::utils::{in_macro, span_lint, span_lint_and_sugg};
7 use rustc::hir::*;
8 use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
9 use rustc::{declare_tool_lint, lint_array};
10 use rustc_errors::Applicability;
11 use syntax::ast::LitKind;
12 use syntax::source_map::Spanned;
13
14 declare_clippy_lint! {
15     /// **What it does:** Checks for expressions of the form `if c { true } else {
16     /// false }`
17     /// (or vice versa) and suggest using the condition directly.
18     ///
19     /// **Why is this bad?** Redundant code.
20     ///
21     /// **Known problems:** Maybe false positives: Sometimes, the two branches are
22     /// painstakingly documented (which we of course do not detect), so they *may*
23     /// have some value. Even then, the documentation can be rewritten to match the
24     /// shorter code.
25     ///
26     /// **Example:**
27     /// ```rust
28     /// if x {
29     ///     false
30     /// } else {
31     ///     true
32     /// }
33     /// ```
34     pub NEEDLESS_BOOL,
35     complexity,
36     "if-statements with plain booleans in the then- and else-clause, e.g. `if p { true } else { false }`"
37 }
38
39 declare_clippy_lint! {
40     /// **What it does:** Checks for expressions of the form `x == true`,
41     /// `x != true` and order comparisons such as `x < true` (or vice versa) and
42     /// suggest using the variable directly.
43     ///
44     /// **Why is this bad?** Unnecessary code.
45     ///
46     /// **Known problems:** None.
47     ///
48     /// **Example:**
49     /// ```rust
50     /// if x == true {} // could be `if x { }`
51     /// ```
52     pub BOOL_COMPARISON,
53     complexity,
54     "comparing a variable to a boolean, e.g. `if x == true` or `if x != true`"
55 }
56
57 #[derive(Copy, Clone)]
58 pub struct NeedlessBool;
59
60 impl LintPass for NeedlessBool {
61     fn get_lints(&self) -> LintArray {
62         lint_array!(NEEDLESS_BOOL)
63     }
64
65     fn name(&self) -> &'static str {
66         "NeedlessBool"
67     }
68 }
69
70 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessBool {
71     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
72         use self::Expression::*;
73         if let ExprKind::If(ref pred, ref then_block, Some(ref else_expr)) = e.node {
74             let reduce = |ret, not| {
75                 let mut applicability = Applicability::MachineApplicable;
76                 let snip = Sugg::hir_with_applicability(cx, pred, "<predicate>", &mut applicability);
77                 let mut snip = if not { !snip } else { snip };
78
79                 if ret {
80                     snip = snip.make_return();
81                 }
82
83                 if parent_node_is_if_expr(&e, &cx) {
84                     snip = snip.blockify()
85                 }
86
87                 span_lint_and_sugg(
88                     cx,
89                     NEEDLESS_BOOL,
90                     e.span,
91                     "this if-then-else expression returns a bool literal",
92                     "you can reduce it to",
93                     snip.to_string(),
94                     applicability,
95                 );
96             };
97             if let ExprKind::Block(ref then_block, _) = then_block.node {
98                 match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) {
99                     (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => {
100                         span_lint(
101                             cx,
102                             NEEDLESS_BOOL,
103                             e.span,
104                             "this if-then-else expression will always return true",
105                         );
106                     },
107                     (RetBool(false), RetBool(false)) | (Bool(false), Bool(false)) => {
108                         span_lint(
109                             cx,
110                             NEEDLESS_BOOL,
111                             e.span,
112                             "this if-then-else expression will always return false",
113                         );
114                     },
115                     (RetBool(true), RetBool(false)) => reduce(true, false),
116                     (Bool(true), Bool(false)) => reduce(false, false),
117                     (RetBool(false), RetBool(true)) => reduce(true, true),
118                     (Bool(false), Bool(true)) => reduce(false, true),
119                     _ => (),
120                 }
121             } else {
122                 panic!("IfExpr 'then' node is not an ExprKind::Block");
123             }
124         }
125     }
126 }
127
128 fn parent_node_is_if_expr<'a, 'b>(expr: &Expr, cx: &LateContext<'a, 'b>) -> bool {
129     let parent_id = cx.tcx.hir().get_parent_node_by_hir_id(expr.hir_id);
130     let parent_node = cx.tcx.hir().get_by_hir_id(parent_id);
131
132     if let rustc::hir::Node::Expr(e) = parent_node {
133         if let ExprKind::If(_, _, _) = e.node {
134             return true;
135         }
136     }
137
138     false
139 }
140
141 #[derive(Copy, Clone)]
142 pub struct BoolComparison;
143
144 impl LintPass for BoolComparison {
145     fn get_lints(&self) -> LintArray {
146         lint_array!(BOOL_COMPARISON)
147     }
148
149     fn name(&self) -> &'static str {
150         "BoolComparison"
151     }
152 }
153
154 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for BoolComparison {
155     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
156         if in_macro(e.span) {
157             return;
158         }
159
160         if let ExprKind::Binary(Spanned { node, .. }, ..) = e.node {
161             let ignore_case = None::<(fn(_) -> _, &str)>;
162             let ignore_no_literal = None::<(fn(_, _) -> _, &str)>;
163             match node {
164                 BinOpKind::Eq => {
165                     let true_case = Some((|h| h, "equality checks against true are unnecessary"));
166                     let false_case = Some((
167                         |h: Sugg<'_>| !h,
168                         "equality checks against false can be replaced by a negation",
169                     ));
170                     check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal)
171                 },
172                 BinOpKind::Ne => {
173                     let true_case = Some((
174                         |h: Sugg<'_>| !h,
175                         "inequality checks against true can be replaced by a negation",
176                     ));
177                     let false_case = Some((|h| h, "inequality checks against false are unnecessary"));
178                     check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal)
179                 },
180                 BinOpKind::Lt => check_comparison(
181                     cx,
182                     e,
183                     ignore_case,
184                     Some((|h| h, "greater than checks against false are unnecessary")),
185                     Some((
186                         |h: Sugg<'_>| !h,
187                         "less than comparison against true can be replaced by a negation",
188                     )),
189                     ignore_case,
190                     Some((
191                         |l: Sugg<'_>, r: Sugg<'_>| (!l).bit_and(&r),
192                         "order comparisons between booleans can be simplified",
193                     )),
194                 ),
195                 BinOpKind::Gt => check_comparison(
196                     cx,
197                     e,
198                     Some((
199                         |h: Sugg<'_>| !h,
200                         "less than comparison against true can be replaced by a negation",
201                     )),
202                     ignore_case,
203                     ignore_case,
204                     Some((|h| h, "greater than checks against false are unnecessary")),
205                     Some((
206                         |l: Sugg<'_>, r: Sugg<'_>| l.bit_and(&(!r)),
207                         "order comparisons between booleans can be simplified",
208                     )),
209                 ),
210                 _ => (),
211             }
212         }
213     }
214 }
215
216 fn check_comparison<'a, 'tcx>(
217     cx: &LateContext<'a, 'tcx>,
218     e: &'tcx Expr,
219     left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
220     left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
221     right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
222     right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
223     no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &str)>,
224 ) {
225     use self::Expression::*;
226
227     if let ExprKind::Binary(_, ref left_side, ref right_side) = e.node {
228         let (l_ty, r_ty) = (cx.tables.expr_ty(left_side), cx.tables.expr_ty(right_side));
229         if l_ty.is_bool() && r_ty.is_bool() {
230             let mut applicability = Applicability::MachineApplicable;
231             match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
232                 (Bool(true), Other) => left_true.map_or((), |(h, m)| {
233                     suggest_bool_comparison(cx, e, right_side, applicability, m, h)
234                 }),
235                 (Other, Bool(true)) => right_true.map_or((), |(h, m)| {
236                     suggest_bool_comparison(cx, e, left_side, applicability, m, h)
237                 }),
238                 (Bool(false), Other) => left_false.map_or((), |(h, m)| {
239                     suggest_bool_comparison(cx, e, right_side, applicability, m, h)
240                 }),
241                 (Other, Bool(false)) => right_false.map_or((), |(h, m)| {
242                     suggest_bool_comparison(cx, e, left_side, applicability, m, h)
243                 }),
244                 (Other, Other) => no_literal.map_or((), |(h, m)| {
245                     let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability);
246                     let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability);
247                     span_lint_and_sugg(
248                         cx,
249                         BOOL_COMPARISON,
250                         e.span,
251                         m,
252                         "try simplifying it as shown",
253                         h(left_side, right_side).to_string(),
254                         applicability,
255                     )
256                 }),
257                 _ => (),
258             }
259         }
260     }
261 }
262
263 fn suggest_bool_comparison<'a, 'tcx>(
264     cx: &LateContext<'a, 'tcx>,
265     e: &'tcx Expr,
266     expr: &Expr,
267     mut applicability: Applicability,
268     message: &str,
269     conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
270 ) {
271     let hint = Sugg::hir_with_applicability(cx, expr, "..", &mut applicability);
272     span_lint_and_sugg(
273         cx,
274         BOOL_COMPARISON,
275         e.span,
276         message,
277         "try simplifying it as shown",
278         conv_hint(hint).to_string(),
279         applicability,
280     );
281 }
282
283 enum Expression {
284     Bool(bool),
285     RetBool(bool),
286     Other,
287 }
288
289 fn fetch_bool_block(block: &Block) -> Expression {
290     match (&*block.stmts, block.expr.as_ref()) {
291         (&[], Some(e)) => fetch_bool_expr(&**e),
292         (&[ref e], None) => {
293             if let StmtKind::Semi(ref e) = e.node {
294                 if let ExprKind::Ret(_) = e.node {
295                     fetch_bool_expr(&**e)
296                 } else {
297                     Expression::Other
298                 }
299             } else {
300                 Expression::Other
301             }
302         },
303         _ => Expression::Other,
304     }
305 }
306
307 fn fetch_bool_expr(expr: &Expr) -> Expression {
308     match expr.node {
309         ExprKind::Block(ref block, _) => fetch_bool_block(block),
310         ExprKind::Lit(ref lit_ptr) => {
311             if let LitKind::Bool(value) = lit_ptr.node {
312                 Expression::Bool(value)
313             } else {
314                 Expression::Other
315             }
316         },
317         ExprKind::Ret(Some(ref expr)) => match fetch_bool_expr(expr) {
318             Expression::Bool(value) => Expression::RetBool(value),
319             _ => Expression::Other,
320         },
321         _ => Expression::Other,
322     }
323 }