1 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
2 use clippy_utils::msrvs::{self, Msrv};
3 use clippy_utils::source::snippet;
4 use clippy_utils::ty::is_type_diagnostic_item;
5 use clippy_utils::usage::mutated_variables;
6 use rustc_errors::Applicability;
8 use rustc_lint::LateContext;
9 use rustc_span::symbol::sym;
11 use super::MAP_UNWRAP_OR;
13 /// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
14 /// Return true if lint triggered
15 pub(super) fn check<'tcx>(
16 cx: &LateContext<'tcx>,
17 expr: &'tcx hir::Expr<'_>,
18 recv: &'tcx hir::Expr<'_>,
19 map_arg: &'tcx hir::Expr<'_>,
20 unwrap_arg: &'tcx hir::Expr<'_>,
23 // lint if the caller of `map()` is an `Option`
24 let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
25 let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
27 if is_result && !msrv.meets(msrvs::RESULT_MAP_OR_ELSE) {
31 if is_option || is_result {
32 // Don't make a suggestion that may fail to compile due to mutably borrowing
33 // the same variable twice.
34 let map_mutated_vars = mutated_variables(recv, cx);
35 let unwrap_mutated_vars = mutated_variables(unwrap_arg, cx);
36 if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) {
37 if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() {
45 let msg = if is_option {
46 "called `map(<f>).unwrap_or_else(<g>)` on an `Option` value. This can be done more directly by calling \
47 `map_or_else(<g>, <f>)` instead"
49 "called `map(<f>).unwrap_or_else(<g>)` on a `Result` value. This can be done more directly by calling \
50 `.map_or_else(<g>, <f>)` instead"
52 // get snippets for args to map() and unwrap_or_else()
53 let map_snippet = snippet(cx, map_arg.span, "..");
54 let unwrap_snippet = snippet(cx, unwrap_arg.span, "..");
55 // lint, with note if neither arg is > 1 line and both map() and
56 // unwrap_or_else() have the same span
57 let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
58 let same_span = map_arg.span.ctxt() == unwrap_arg.span.ctxt();
59 if same_span && !multiline {
60 let var_snippet = snippet(cx, recv.span, "..");
67 format!("{var_snippet}.map_or_else({unwrap_snippet}, {map_snippet})"),
68 Applicability::MachineApplicable,
71 } else if same_span && multiline {
72 span_lint(cx, MAP_UNWRAP_OR, expr.span, msg);