| use super::OBFUSCATED_IF_ELSE; | |
| use clippy_utils::diagnostics::span_lint_and_sugg; | |
| use clippy_utils::eager_or_lazy::switch_to_eager_eval; | |
| use clippy_utils::source::snippet_with_context; | |
| use clippy_utils::sugg::Sugg; | |
| use clippy_utils::{get_parent_expr, sym}; | |
| use rustc_errors::Applicability; | |
| use rustc_hir::{Expr, ExprKind}; | |
| use rustc_lint::LateContext; | |
| use rustc_span::Symbol; | |
| #[expect(clippy::needless_pass_by_value)] | |
| pub(super) fn check<'tcx>( | |
| cx: &LateContext<'tcx>, | |
| expr: &'tcx Expr<'_>, | |
| then_recv: &'tcx Expr<'_>, | |
| then_arg: &'tcx Expr<'_>, | |
| then_method_name: Symbol, | |
| unwrap: Unwrap<'tcx>, | |
| ) { | |
| let recv_ty = cx.typeck_results().expr_ty(then_recv); | |
| if recv_ty.is_bool() { | |
| let then_eager = switch_to_eager_eval(cx, then_arg); | |
| let unwrap_eager = unwrap.arg().is_none_or(|arg| switch_to_eager_eval(cx, arg)); | |
| let mut applicability = if then_eager && unwrap_eager { | |
| Applicability::MachineApplicable | |
| } else { | |
| Applicability::MaybeIncorrect | |
| }; | |
| let if_then = match then_method_name { | |
| sym::then if let ExprKind::Closure(closure) = then_arg.kind => { | |
| let body = cx.tcx.hir_body(closure.body); | |
| snippet_with_context(cx, body.value.span, expr.span.ctxt(), "..", &mut applicability).0 | |
| }, | |
| sym::then_some => snippet_with_context(cx, then_arg.span, expr.span.ctxt(), "..", &mut applicability).0, | |
| _ => return, | |
| }; | |
| let els = match unwrap { | |
| Unwrap::Or(arg) => snippet_with_context(cx, arg.span, expr.span.ctxt(), "..", &mut applicability).0, | |
| Unwrap::OrElse(arg) => match arg.kind { | |
| ExprKind::Closure(closure) => { | |
| let body = cx.tcx.hir_body(closure.body); | |
| snippet_with_context(cx, body.value.span, expr.span.ctxt(), "..", &mut applicability).0 | |
| }, | |
| ExprKind::Path(_) => { | |
| snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut applicability).0 + "()" | |
| }, | |
| _ => return, | |
| }, | |
| Unwrap::OrDefault => "Default::default()".into(), | |
| }; | |
| let sugg = format!( | |
| "if {} {{ {} }} else {{ {} }}", | |
| Sugg::hir_with_context(cx, then_recv, expr.span.ctxt(), "..", &mut applicability), | |
| if_then, | |
| els | |
| ); | |
| // To be parsed as an expression, the `if { … } else { … }` as the left operand of a binary operator | |
| // requires parentheses. | |
| let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) | |
| && let ExprKind::Binary(_, left, _) = parent_expr.kind | |
| && left.hir_id == expr.hir_id | |
| { | |
| format!("({sugg})") | |
| } else { | |
| sugg | |
| }; | |
| span_lint_and_sugg( | |
| cx, | |
| OBFUSCATED_IF_ELSE, | |
| expr.span, | |
| "this method chain can be written more clearly with `if .. else ..`", | |
| "try", | |
| sugg, | |
| applicability, | |
| ); | |
| } | |
| } | |
| pub(super) enum Unwrap<'tcx> { | |
| Or(&'tcx Expr<'tcx>), | |
| OrElse(&'tcx Expr<'tcx>), | |
| OrDefault, | |
| } | |
| impl<'tcx> Unwrap<'tcx> { | |
| fn arg(&self) -> Option<&'tcx Expr<'tcx>> { | |
| match self { | |
| Self::Or(a) | Self::OrElse(a) => Some(a), | |
| Self::OrDefault => None, | |
| } | |
| } | |
| } |