blob: 8bb532b21635a042f33fa92ce6601967785e91c4 [file] [log] [blame]
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::MaybeDef as _;
use clippy_utils::source::snippet;
use clippy_utils::sym;
use clippy_utils::usage::mutated_variables;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use super::MAP_UNWRAP_OR;
/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
///
/// Is part of the `map_unwrap_or` lint, split into separate files for readability.
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
recv: &'tcx hir::Expr<'_>,
map_arg: &'tcx hir::Expr<'_>,
unwrap_arg: &'tcx hir::Expr<'_>,
msrv: Msrv,
) -> bool {
let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
let recv_ty_kind = match recv_ty.opt_diag_name(cx) {
Some(sym::Option) => sym::Option,
Some(sym::Result) if msrv.meets(cx, msrvs::RESULT_MAP_OR_ELSE) => sym::Result,
_ => return false,
};
// Don't make a suggestion that may fail to compile due to mutably borrowing
// the same variable twice.
let Some(map_mutated_vars) = mutated_variables(recv, cx) else {
return false;
};
let Some(unwrap_mutated_vars) = mutated_variables(unwrap_arg, cx) else {
return false;
};
if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() {
return false;
}
// lint message
let msg = if recv_ty_kind == sym::Option {
"called `map(<f>).unwrap_or_else(<g>)` on an `Option` value"
} else {
"called `map(<f>).unwrap_or_else(<g>)` on a `Result` value"
};
// get snippets for args to map() and unwrap_or_else()
let map_snippet = snippet(cx, map_arg.span, "..");
let unwrap_snippet = snippet(cx, unwrap_arg.span, "..");
// lint, with note if both map() and unwrap_or_else() have the same span
if map_arg.span.eq_ctxt(unwrap_arg.span) {
let var_snippet = snippet(cx, recv.span, "..");
span_lint_and_sugg(
cx,
MAP_UNWRAP_OR,
expr.span,
msg,
"try",
format!("{var_snippet}.map_or_else({unwrap_snippet}, {map_snippet})"),
Applicability::MachineApplicable,
);
return true;
}
false
}