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