| 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 | |
| } |