]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/needless_bool.rs
Rollup merge of #76060 - pickfire:patch-12, r=jyn514
[rust.git] / src / tools / clippy / 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, snippet_with_applicability, span_lint, span_lint_and_sugg};
7 use if_chain::if_chain;
8 use rustc_ast::ast::LitKind;
9 use rustc_errors::Applicability;
10 use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
11 use rustc_lint::{LateContext, LateLintPass};
12 use rustc_session::{declare_lint_pass, declare_tool_lint};
13 use rustc_span::source_map::Spanned;
14 use rustc_span::Span;
15
16 declare_clippy_lint! {
17     /// **What it does:** Checks for expressions of the form `if c { true } else {
18     /// false }` (or vice versa) and suggests 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 {}
56     /// if y == false {}
57     /// ```
58     /// use `x` directly:
59     /// ```rust,ignore
60     /// if x {}
61     /// if !y {}
62     /// ```
63     pub BOOL_COMPARISON,
64     complexity,
65     "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
66 }
67
68 declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL]);
69
70 impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
71     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
72         use self::Expression::{Bool, RetBool};
73         if let Some((ref pred, ref then_block, Some(ref else_expr))) = higher::if_block(&e) {
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.kind {
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 declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]);
129
130 impl<'tcx> LateLintPass<'tcx> for BoolComparison {
131     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
132         if e.span.from_expansion() {
133             return;
134         }
135
136         if let ExprKind::Binary(Spanned { node, .. }, ..) = e.kind {
137             let ignore_case = None::<(fn(_) -> _, &str)>;
138             let ignore_no_literal = None::<(fn(_, _) -> _, &str)>;
139             match node {
140                 BinOpKind::Eq => {
141                     let true_case = Some((|h| h, "equality checks against true are unnecessary"));
142                     let false_case = Some((
143                         |h: Sugg<'_>| !h,
144                         "equality checks against false can be replaced by a negation",
145                     ));
146                     check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal)
147                 },
148                 BinOpKind::Ne => {
149                     let true_case = Some((
150                         |h: Sugg<'_>| !h,
151                         "inequality checks against true can be replaced by a negation",
152                     ));
153                     let false_case = Some((|h| h, "inequality checks against false are unnecessary"));
154                     check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal)
155                 },
156                 BinOpKind::Lt => check_comparison(
157                     cx,
158                     e,
159                     ignore_case,
160                     Some((|h| h, "greater than checks against false are unnecessary")),
161                     Some((
162                         |h: Sugg<'_>| !h,
163                         "less than comparison against true can be replaced by a negation",
164                     )),
165                     ignore_case,
166                     Some((
167                         |l: Sugg<'_>, r: Sugg<'_>| (!l).bit_and(&r),
168                         "order comparisons between booleans can be simplified",
169                     )),
170                 ),
171                 BinOpKind::Gt => check_comparison(
172                     cx,
173                     e,
174                     Some((
175                         |h: Sugg<'_>| !h,
176                         "less than comparison against true can be replaced by a negation",
177                     )),
178                     ignore_case,
179                     ignore_case,
180                     Some((|h| h, "greater than checks against false are unnecessary")),
181                     Some((
182                         |l: Sugg<'_>, r: Sugg<'_>| l.bit_and(&(!r)),
183                         "order comparisons between booleans can be simplified",
184                     )),
185                 ),
186                 _ => (),
187             }
188         }
189     }
190 }
191
192 struct ExpressionInfoWithSpan {
193     one_side_is_unary_not: bool,
194     left_span: Span,
195     right_span: Span,
196 }
197
198 fn is_unary_not(e: &Expr<'_>) -> (bool, Span) {
199     if_chain! {
200         if let ExprKind::Unary(unop, operand) = e.kind;
201         if let UnOp::UnNot = unop;
202         then {
203             return (true, operand.span);
204         }
205     };
206     (false, e.span)
207 }
208
209 fn one_side_is_unary_not<'tcx>(left_side: &'tcx Expr<'_>, right_side: &'tcx Expr<'_>) -> ExpressionInfoWithSpan {
210     let left = is_unary_not(left_side);
211     let right = is_unary_not(right_side);
212
213     ExpressionInfoWithSpan {
214         one_side_is_unary_not: left.0 != right.0,
215         left_span: left.1,
216         right_span: right.1,
217     }
218 }
219
220 fn check_comparison<'a, 'tcx>(
221     cx: &LateContext<'tcx>,
222     e: &'tcx Expr<'_>,
223     left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
224     left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
225     right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
226     right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &str)>,
227     no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &str)>,
228 ) {
229     use self::Expression::{Bool, Other};
230
231     if let ExprKind::Binary(op, ref left_side, ref right_side) = e.kind {
232         let (l_ty, r_ty) = (
233             cx.typeck_results().expr_ty(left_side),
234             cx.typeck_results().expr_ty(right_side),
235         );
236         if l_ty.is_bool() && r_ty.is_bool() {
237             let mut applicability = Applicability::MachineApplicable;
238
239             if let BinOpKind::Eq = op.node {
240                 let expression_info = one_side_is_unary_not(&left_side, &right_side);
241                 if expression_info.one_side_is_unary_not {
242                     span_lint_and_sugg(
243                         cx,
244                         BOOL_COMPARISON,
245                         e.span,
246                         "this comparison might be written more concisely",
247                         "try simplifying it as shown",
248                         format!(
249                             "{} != {}",
250                             snippet_with_applicability(cx, expression_info.left_span, "..", &mut applicability),
251                             snippet_with_applicability(cx, expression_info.right_span, "..", &mut applicability)
252                         ),
253                         applicability,
254                     )
255                 }
256             }
257
258             match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
259                 (Bool(true), Other) => left_true.map_or((), |(h, m)| {
260                     suggest_bool_comparison(cx, e, right_side, applicability, m, h)
261                 }),
262                 (Other, Bool(true)) => right_true.map_or((), |(h, m)| {
263                     suggest_bool_comparison(cx, e, left_side, applicability, m, h)
264                 }),
265                 (Bool(false), Other) => left_false.map_or((), |(h, m)| {
266                     suggest_bool_comparison(cx, e, right_side, applicability, m, h)
267                 }),
268                 (Other, Bool(false)) => right_false.map_or((), |(h, m)| {
269                     suggest_bool_comparison(cx, e, left_side, applicability, m, h)
270                 }),
271                 (Other, Other) => no_literal.map_or((), |(h, m)| {
272                     let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability);
273                     let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability);
274                     span_lint_and_sugg(
275                         cx,
276                         BOOL_COMPARISON,
277                         e.span,
278                         m,
279                         "try simplifying it as shown",
280                         h(left_side, right_side).to_string(),
281                         applicability,
282                     )
283                 }),
284                 _ => (),
285             }
286         }
287     }
288 }
289
290 fn suggest_bool_comparison<'a, 'tcx>(
291     cx: &LateContext<'tcx>,
292     e: &'tcx Expr<'_>,
293     expr: &Expr<'_>,
294     mut applicability: Applicability,
295     message: &str,
296     conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
297 ) {
298     let hint = Sugg::hir_with_applicability(cx, expr, "..", &mut applicability);
299     span_lint_and_sugg(
300         cx,
301         BOOL_COMPARISON,
302         e.span,
303         message,
304         "try simplifying it as shown",
305         conv_hint(hint).to_string(),
306         applicability,
307     );
308 }
309
310 enum Expression {
311     Bool(bool),
312     RetBool(bool),
313     Other,
314 }
315
316 fn fetch_bool_block(block: &Block<'_>) -> Expression {
317     match (&*block.stmts, block.expr.as_ref()) {
318         (&[], Some(e)) => fetch_bool_expr(&**e),
319         (&[ref e], None) => {
320             if let StmtKind::Semi(ref e) = e.kind {
321                 if let ExprKind::Ret(_) = e.kind {
322                     fetch_bool_expr(&**e)
323                 } else {
324                     Expression::Other
325                 }
326             } else {
327                 Expression::Other
328             }
329         },
330         _ => Expression::Other,
331     }
332 }
333
334 fn fetch_bool_expr(expr: &Expr<'_>) -> Expression {
335     match expr.kind {
336         ExprKind::Block(ref block, _) => fetch_bool_block(block),
337         ExprKind::Lit(ref lit_ptr) => {
338             if let LitKind::Bool(value) = lit_ptr.node {
339                 Expression::Bool(value)
340             } else {
341                 Expression::Other
342             }
343         },
344         ExprKind::Ret(Some(ref expr)) => match fetch_bool_expr(expr) {
345             Expression::Bool(value) => Expression::RetBool(value),
346             _ => Expression::Other,
347         },
348         _ => Expression::Other,
349     }
350 }