80 lines
2.8 KiB
Rust
80 lines
2.8 KiB
Rust
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::get_parent_expr;
|
|
use clippy_utils::source::snippet_with_applicability;
|
|
use clippy_utils::sugg::Sugg;
|
|
use rustc_errors::Applicability;
|
|
use rustc_hir as hir;
|
|
use rustc_hir::ExprKind;
|
|
use rustc_lint::LateContext;
|
|
|
|
pub(super) fn check<'tcx>(
|
|
cx: &LateContext<'tcx>,
|
|
expr: &'tcx hir::Expr<'_>,
|
|
then_recv: &'tcx hir::Expr<'_>,
|
|
then_arg: &'tcx hir::Expr<'_>,
|
|
unwrap_arg: &'tcx hir::Expr<'_>,
|
|
then_method_name: &str,
|
|
unwrap_method_name: &str,
|
|
) {
|
|
let recv_ty = cx.typeck_results().expr_ty(then_recv);
|
|
|
|
if recv_ty.is_bool() {
|
|
let mut applicability = if switch_to_eager_eval(cx, then_arg) && switch_to_eager_eval(cx, unwrap_arg) {
|
|
Applicability::MachineApplicable
|
|
} else {
|
|
Applicability::MaybeIncorrect
|
|
};
|
|
|
|
let if_then = match then_method_name {
|
|
"then" if let ExprKind::Closure(closure) = then_arg.kind => {
|
|
let body = cx.tcx.hir_body(closure.body);
|
|
snippet_with_applicability(cx, body.value.span, "..", &mut applicability)
|
|
},
|
|
"then_some" => snippet_with_applicability(cx, then_arg.span, "..", &mut applicability),
|
|
_ => return,
|
|
};
|
|
|
|
// FIXME: Add `unwrap_or_else` symbol
|
|
let els = match unwrap_method_name {
|
|
"unwrap_or" => snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability),
|
|
"unwrap_or_else" if let ExprKind::Closure(closure) = unwrap_arg.kind => {
|
|
let body = cx.tcx.hir_body(closure.body);
|
|
snippet_with_applicability(cx, body.value.span, "..", &mut applicability)
|
|
},
|
|
"unwrap_or_else" if let ExprKind::Path(_) = unwrap_arg.kind => {
|
|
snippet_with_applicability(cx, unwrap_arg.span, "_", &mut applicability) + "()"
|
|
},
|
|
_ => return,
|
|
};
|
|
|
|
let sugg = format!(
|
|
"if {} {{ {} }} else {{ {} }}",
|
|
Sugg::hir_with_applicability(cx, then_recv, "..", &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,
|
|
);
|
|
}
|
|
}
|