|  | use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; | 
|  | use clippy_utils::source::snippet_with_applicability; | 
|  | use clippy_utils::sugg::Sugg; | 
|  | use clippy_utils::{ | 
|  | SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_parent_stmt, is_receiver_of_method_call, | 
|  | peel_blocks, peel_blocks_with_stmt, span_contains_comment, | 
|  | }; | 
|  | use rustc_ast::ast::LitKind; | 
|  | use rustc_errors::Applicability; | 
|  | use rustc_hir::{Expr, ExprKind}; | 
|  | use rustc_lint::{LateContext, LateLintPass}; | 
|  | use rustc_session::declare_lint_pass; | 
|  |  | 
|  | declare_clippy_lint! { | 
|  | /// ### What it does | 
|  | /// Checks for expressions of the form `if c { true } else { | 
|  | /// false }` (or vice versa) and suggests using the condition directly. | 
|  | /// | 
|  | /// ### Why is this bad? | 
|  | /// Redundant code. | 
|  | /// | 
|  | /// ### Known problems | 
|  | /// Maybe false positives: Sometimes, the two branches are | 
|  | /// painstakingly documented (which we, of course, do not detect), so they *may* | 
|  | /// have some value. Even then, the documentation can be rewritten to match the | 
|  | /// shorter code. | 
|  | /// | 
|  | /// ### Example | 
|  | /// ```no_run | 
|  | /// # let x = true; | 
|  | /// if x { | 
|  | ///     false | 
|  | /// } else { | 
|  | ///     true | 
|  | /// } | 
|  | /// # ; | 
|  | /// ``` | 
|  | /// | 
|  | /// Use instead: | 
|  | /// ```no_run | 
|  | /// # let x = true; | 
|  | /// !x | 
|  | /// # ; | 
|  | /// ``` | 
|  | #[clippy::version = "pre 1.29.0"] | 
|  | pub NEEDLESS_BOOL, | 
|  | complexity, | 
|  | "if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`" | 
|  | } | 
|  |  | 
|  | declare_clippy_lint! { | 
|  | /// ### What it does | 
|  | /// Checks for expressions of the form `if c { x = true } else { x = false }` | 
|  | /// (or vice versa) and suggest assigning the variable directly from the | 
|  | /// condition. | 
|  | /// | 
|  | /// ### Why is this bad? | 
|  | /// Redundant code. | 
|  | /// | 
|  | /// ### Example | 
|  | /// ```rust,ignore | 
|  | /// # fn must_keep(x: i32, y: i32) -> bool { x == y } | 
|  | /// # let x = 32; let y = 10; | 
|  | /// # let mut skip: bool; | 
|  | /// if must_keep(x, y) { | 
|  | ///     skip = false; | 
|  | /// } else { | 
|  | ///     skip = true; | 
|  | /// } | 
|  | /// ``` | 
|  | /// Use instead: | 
|  | /// ```rust,ignore | 
|  | /// # fn must_keep(x: i32, y: i32) -> bool { x == y } | 
|  | /// # let x = 32; let y = 10; | 
|  | /// # let mut skip: bool; | 
|  | /// skip = !must_keep(x, y); | 
|  | /// ``` | 
|  | #[clippy::version = "1.71.0"] | 
|  | pub NEEDLESS_BOOL_ASSIGN, | 
|  | complexity, | 
|  | "setting the same boolean variable in both branches of an if-statement" | 
|  | } | 
|  | declare_lint_pass!(NeedlessBool => [NEEDLESS_BOOL, NEEDLESS_BOOL_ASSIGN]); | 
|  |  | 
|  | fn condition_needs_parentheses(e: &Expr<'_>) -> bool { | 
|  | let mut inner = e; | 
|  | while let ExprKind::Binary(_, i, _) | 
|  | | ExprKind::Call(i, _) | 
|  | | ExprKind::Cast(i, _) | 
|  | | ExprKind::Type(i, _) | 
|  | | ExprKind::Index(i, _, _) = inner.kind | 
|  | { | 
|  | if is_block_like(i) { | 
|  | return true; | 
|  | } | 
|  | inner = i; | 
|  | } | 
|  | false | 
|  | } | 
|  |  | 
|  | impl<'tcx> LateLintPass<'tcx> for NeedlessBool { | 
|  | fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { | 
|  | use self::Expression::{Bool, RetBool}; | 
|  | if !e.span.from_expansion() | 
|  | && let Some(higher::If { | 
|  | cond, | 
|  | then, | 
|  | r#else: Some(else_expr), | 
|  | }) = higher::If::hir(e) | 
|  | && !span_contains_comment(cx.tcx.sess.source_map(), e.span) | 
|  | { | 
|  | let reduce = |ret, not| { | 
|  | let mut applicability = Applicability::MachineApplicable; | 
|  | let snip = Sugg::hir_with_applicability(cx, cond, "<predicate>", &mut applicability); | 
|  | let mut snip = if not { !snip } else { snip }; | 
|  |  | 
|  | if ret { | 
|  | snip = snip.make_return(); | 
|  | } | 
|  |  | 
|  | if is_else_clause(cx.tcx, e) { | 
|  | snip = snip.blockify(); | 
|  | } | 
|  |  | 
|  | if (condition_needs_parentheses(cond) && is_parent_stmt(cx, e.hir_id)) | 
|  | || is_receiver_of_method_call(cx, e) | 
|  | || is_as_argument(cx, e) | 
|  | { | 
|  | snip = snip.maybe_paren(); | 
|  | } | 
|  |  | 
|  | span_lint_and_sugg( | 
|  | cx, | 
|  | NEEDLESS_BOOL, | 
|  | e.span, | 
|  | "this if-then-else expression returns a bool literal", | 
|  | "you can reduce it to", | 
|  | snip.to_string(), | 
|  | applicability, | 
|  | ); | 
|  | }; | 
|  | if let Some(a) = fetch_bool_block(then) | 
|  | && let Some(b) = fetch_bool_block(else_expr) | 
|  | { | 
|  | match (a, b) { | 
|  | (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => { | 
|  | span_lint( | 
|  | cx, | 
|  | NEEDLESS_BOOL, | 
|  | e.span, | 
|  | "this if-then-else expression will always return true", | 
|  | ); | 
|  | }, | 
|  | (RetBool(false), RetBool(false)) | (Bool(false), Bool(false)) => { | 
|  | span_lint( | 
|  | cx, | 
|  | NEEDLESS_BOOL, | 
|  | e.span, | 
|  | "this if-then-else expression will always return false", | 
|  | ); | 
|  | }, | 
|  | (RetBool(true), RetBool(false)) => reduce(true, false), | 
|  | (Bool(true), Bool(false)) => reduce(false, false), | 
|  | (RetBool(false), RetBool(true)) => reduce(true, true), | 
|  | (Bool(false), Bool(true)) => reduce(false, true), | 
|  | _ => (), | 
|  | } | 
|  | } | 
|  | if let Some((lhs_a, a)) = fetch_assign(then) | 
|  | && let Some((lhs_b, b)) = fetch_assign(else_expr) | 
|  | && SpanlessEq::new(cx).eq_expr(lhs_a, lhs_b) | 
|  | { | 
|  | let mut applicability = Applicability::MachineApplicable; | 
|  | let cond = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability); | 
|  | let lhs = snippet_with_applicability(cx, lhs_a.span, "..", &mut applicability); | 
|  | let mut sugg = if a == b { | 
|  | format!("{cond}; {lhs} = {a:?};") | 
|  | } else { | 
|  | format!("{lhs} = {};", if a { cond } else { !cond }) | 
|  | }; | 
|  |  | 
|  | if is_else_clause(cx.tcx, e) { | 
|  | sugg = format!("{{ {sugg} }}"); | 
|  | } | 
|  |  | 
|  | span_lint_and_sugg( | 
|  | cx, | 
|  | NEEDLESS_BOOL_ASSIGN, | 
|  | e.span, | 
|  | "this if-then-else expression assigns a bool literal", | 
|  | "you can reduce it to", | 
|  | sugg, | 
|  | applicability, | 
|  | ); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | enum Expression { | 
|  | Bool(bool), | 
|  | RetBool(bool), | 
|  | } | 
|  |  | 
|  | fn fetch_bool_block(expr: &Expr<'_>) -> Option<Expression> { | 
|  | match peel_blocks_with_stmt(expr).kind { | 
|  | ExprKind::Ret(Some(ret)) => Some(Expression::RetBool(fetch_bool_expr(ret)?)), | 
|  | _ => Some(Expression::Bool(fetch_bool_expr(expr)?)), | 
|  | } | 
|  | } | 
|  |  | 
|  | fn fetch_bool_expr(expr: &Expr<'_>) -> Option<bool> { | 
|  | if let ExprKind::Lit(lit_ptr) = peel_blocks(expr).kind | 
|  | && let LitKind::Bool(value) = lit_ptr.node | 
|  | { | 
|  | return Some(value); | 
|  | } | 
|  | None | 
|  | } | 
|  |  | 
|  | fn fetch_assign<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(&'tcx Expr<'tcx>, bool)> { | 
|  | if let ExprKind::Assign(lhs, rhs, _) = peel_blocks_with_stmt(expr).kind { | 
|  | fetch_bool_expr(rhs).map(|b| (lhs, b)) | 
|  | } else { | 
|  | None | 
|  | } | 
|  | } | 
|  |  | 
|  | fn is_as_argument(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { | 
|  | matches!(get_parent_expr(cx, e).map(|e| e.kind), Some(ExprKind::Cast(_, _))) | 
|  | } |