fix: map_unwrap_or fail to cover Result::unwrap_or

This commit is contained in:
yanglsh 2025-09-19 23:51:46 +08:00 committed by Linshu Yang
parent 34fab5cd37
commit f2283e2ef5
9 changed files with 325 additions and 227 deletions

View file

@ -1,71 +1,199 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::MaybeDef;
use clippy_utils::source::snippet;
use clippy_utils::usage::mutated_variables;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_copy;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{Visitor, walk_path};
use rustc_hir::{ExprKind, HirId, Node, PatKind, Path, QPath};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
use rustc_middle::hir::nested_filter;
use rustc_span::{Span, sym};
use std::ops::ControlFlow;
use super::MAP_UNWRAP_OR;
/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
///
/// Returns true if the lint was emitted
/// lint use of `map().unwrap_or()` for `Option`s and `Result`s
#[expect(clippy::too_many_arguments)]
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<'_>,
expr: &rustc_hir::Expr<'_>,
recv: &rustc_hir::Expr<'_>,
map_arg: &'tcx rustc_hir::Expr<'_>,
unwrap_recv: &rustc_hir::Expr<'_>,
unwrap_arg: &'tcx rustc_hir::Expr<'_>,
map_span: Span,
msrv: Msrv,
) -> bool {
// lint if the caller of `map()` is an `Option` or a `Result`.
let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option);
let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result);
if is_result && !msrv.meets(cx, msrvs::RESULT_MAP_OR_ELSE) {
if is_result && !msrv.meets(cx, msrvs::RESULT_MAP_OR) {
return false;
}
// lint if the caller of `map()` is an `Option`
if is_option || is_result {
// Don't make a suggestion that may fail to compile due to mutably borrowing
// the same variable twice.
let map_mutated_vars = mutated_variables(recv, cx);
let unwrap_mutated_vars = mutated_variables(unwrap_arg, cx);
if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) {
if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() {
if !is_copy(cx, cx.typeck_results().expr_ty(unwrap_arg)) {
// Replacing `.map(<f>).unwrap_or(<a>)` with `.map_or(<a>, <f>)` can sometimes lead to
// borrowck errors, see #10579 for one such instance.
// In particular, if `a` causes a move and `f` references that moved binding, then we cannot lint:
// ```
// let x = vec![1, 2];
// x.get(0..1).map(|s| s.to_vec()).unwrap_or(x);
// ```
// This compiles, but changing it to `map_or` will produce a compile error:
// ```
// let x = vec![1, 2];
// x.get(0..1).map_or(x, |s| s.to_vec())
// ^ moving `x` here
// ^^^^^^^^^^^ while it is borrowed here (and later used in the closure)
// ```
// So, we have to check that `a` is not referenced anywhere (even outside of the `.map` closure!)
// before the call to `unwrap_or`.
let mut unwrap_visitor = UnwrapVisitor {
cx,
identifiers: FxHashSet::default(),
};
unwrap_visitor.visit_expr(unwrap_arg);
let mut reference_visitor = ReferenceVisitor {
cx,
identifiers: unwrap_visitor.identifiers,
unwrap_or_span: unwrap_arg.span,
};
let body = cx.tcx.hir_body_owned_by(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
// Visit the body, and return if we've found a reference
if reference_visitor.visit_body(body).is_break() {
return false;
}
} else {
}
if !unwrap_arg.span.eq_ctxt(map_span) {
return false;
}
// is_some_and is stabilised && `unwrap_or` argument is false; suggest `is_some_and` instead
let suggest_is_some_and = matches!(&unwrap_arg.kind, ExprKind::Lit(lit)
if matches!(lit.node, rustc_ast::LitKind::Bool(false)))
&& msrv.meets(cx, msrvs::OPTION_RESULT_IS_VARIANT_AND);
let mut applicability = Applicability::MachineApplicable;
// get snippet for unwrap_or()
let unwrap_snippet = snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability);
// lint message
let msg = if is_option {
"called `map(<f>).unwrap_or_else(<g>)` on an `Option` value"
// comparing the snippet from source to raw text ("None") below is safe
// because we already have checked the type.
let unwrap_snippet_none = is_option && unwrap_snippet == "None";
let arg = if unwrap_snippet_none {
"None"
} else if suggest_is_some_and {
"false"
} else {
"called `map(<f>).unwrap_or_else(<g>)` on a `Result` value"
"<a>"
};
// 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;
}
let suggest = if unwrap_snippet_none {
"and_then(<f>)"
} else if suggest_is_some_and {
if is_result {
"is_ok_and(<f>)"
} else {
"is_some_and(<f>)"
}
} else {
"map_or(<a>, <f>)"
};
let msg = format!(
"called `map(<f>).unwrap_or({arg})` on an `{}` value",
if is_option { "Option" } else { "Result" }
);
span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| {
let map_arg_span = map_arg.span;
let mut suggestion = vec![
(
map_span,
String::from(if unwrap_snippet_none {
"and_then"
} else if suggest_is_some_and {
if is_result { "is_ok_and" } else { "is_some_and" }
} else {
"map_or"
}),
),
(expr.span.with_lo(unwrap_recv.span.hi()), String::new()),
];
if !unwrap_snippet_none && !suggest_is_some_and {
suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{unwrap_snippet}, ")));
}
diag.multipart_suggestion(format!("use `{suggest}` instead"), suggestion, applicability);
});
return true;
}
false
}
struct UnwrapVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
identifiers: FxHashSet<HirId>,
}
impl<'tcx> Visitor<'tcx> for UnwrapVisitor<'_, 'tcx> {
type NestedFilter = nested_filter::All;
fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) {
if let Res::Local(local_id) = path.res
&& let Node::Pat(pat) = self.cx.tcx.hir_node(local_id)
&& let PatKind::Binding(_, local_id, ..) = pat.kind
{
self.identifiers.insert(local_id);
}
walk_path(self, path);
}
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
self.cx.tcx
}
}
struct ReferenceVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
identifiers: FxHashSet<HirId>,
unwrap_or_span: Span,
}
impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> {
type NestedFilter = nested_filter::All;
type Result = ControlFlow<()>;
fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'_>) -> ControlFlow<()> {
// If we haven't found a reference yet, check if this references
// one of the locals that was moved in the `unwrap_or` argument.
// We are only interested in exprs that appear before the `unwrap_or` call.
if expr.span < self.unwrap_or_span
&& let ExprKind::Path(ref path) = expr.kind
&& let QPath::Resolved(_, path) = path
&& let Res::Local(local_id) = path.res
&& let Node::Pat(pat) = self.cx.tcx.hir_node(local_id)
&& let PatKind::Binding(_, local_id, ..) = pat.kind
&& self.identifiers.contains(&local_id)
{
return ControlFlow::Break(());
}
rustc_hir::intravisit::walk_expr(self, expr)
}
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
self.cx.tcx
}
}

View file

@ -0,0 +1,71 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::MaybeDef;
use clippy_utils::source::snippet;
use clippy_utils::usage::mutated_variables;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
use super::MAP_UNWRAP_OR;
/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
///
/// Returns true if the lint was emitted
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 {
// lint if the caller of `map()` is an `Option` or a `Result`.
let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option);
let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result);
if is_result && !msrv.meets(cx, msrvs::RESULT_MAP_OR_ELSE) {
return false;
}
if is_option || is_result {
// Don't make a suggestion that may fail to compile due to mutably borrowing
// the same variable twice.
let map_mutated_vars = mutated_variables(recv, cx);
let unwrap_mutated_vars = mutated_variables(unwrap_arg, cx);
if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) {
if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() {
return false;
}
} else {
return false;
}
// lint message
let msg = if is_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
}

View file

@ -74,6 +74,7 @@ mod map_err_ignore;
mod map_flatten;
mod map_identity;
mod map_unwrap_or;
mod map_unwrap_or_else;
mod map_with_unused_argument_over_ranges;
mod mut_mutex_lock;
mod needless_as_bytes;
@ -89,7 +90,6 @@ mod open_options;
mod option_as_ref_cloned;
mod option_as_ref_deref;
mod option_map_or_none;
mod option_map_unwrap_or;
mod or_fun_call;
mod or_then_unwrap;
mod path_buf_push_overwrite;
@ -5607,7 +5607,7 @@ impl Methods {
manual_saturating_arithmetic::check_unwrap_or(cx, expr, lhs, rhs, u_arg, arith);
},
Some((sym::map, m_recv, [m_arg], span, _)) => {
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv);
map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, self.msrv);
},
Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => {
obfuscated_if_else::check(
@ -5648,7 +5648,7 @@ impl Methods {
(sym::unwrap_or_else, [u_arg]) => {
match method_call(recv) {
Some((sym::map, recv, [map_arg], _, _))
if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
if map_unwrap_or_else::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
Some((then_method @ (sym::then | sym::then_some), t_recv, [t_arg], _, _)) => {
obfuscated_if_else::check(
cx,

View file

@ -1,180 +0,0 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::res::MaybeDef;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_copy;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{Visitor, walk_path};
use rustc_hir::{ExprKind, HirId, Node, PatKind, Path, QPath};
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
use rustc_span::{Span, sym};
use std::ops::ControlFlow;
use super::MAP_UNWRAP_OR;
/// lint use of `map().unwrap_or()` for `Option`s
#[expect(clippy::too_many_arguments)]
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &rustc_hir::Expr<'_>,
recv: &rustc_hir::Expr<'_>,
map_arg: &'tcx rustc_hir::Expr<'_>,
unwrap_recv: &rustc_hir::Expr<'_>,
unwrap_arg: &'tcx rustc_hir::Expr<'_>,
map_span: Span,
msrv: Msrv,
) {
// lint if the caller of `map()` is an `Option`
if cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option) {
if !is_copy(cx, cx.typeck_results().expr_ty(unwrap_arg)) {
// Replacing `.map(<f>).unwrap_or(<a>)` with `.map_or(<a>, <f>)` can sometimes lead to
// borrowck errors, see #10579 for one such instance.
// In particular, if `a` causes a move and `f` references that moved binding, then we cannot lint:
// ```
// let x = vec![1, 2];
// x.get(0..1).map(|s| s.to_vec()).unwrap_or(x);
// ```
// This compiles, but changing it to `map_or` will produce a compile error:
// ```
// let x = vec![1, 2];
// x.get(0..1).map_or(x, |s| s.to_vec())
// ^ moving `x` here
// ^^^^^^^^^^^ while it is borrowed here (and later used in the closure)
// ```
// So, we have to check that `a` is not referenced anywhere (even outside of the `.map` closure!)
// before the call to `unwrap_or`.
let mut unwrap_visitor = UnwrapVisitor {
cx,
identifiers: FxHashSet::default(),
};
unwrap_visitor.visit_expr(unwrap_arg);
let mut reference_visitor = ReferenceVisitor {
cx,
identifiers: unwrap_visitor.identifiers,
unwrap_or_span: unwrap_arg.span,
};
let body = cx.tcx.hir_body_owned_by(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
// Visit the body, and return if we've found a reference
if reference_visitor.visit_body(body).is_break() {
return;
}
}
if !unwrap_arg.span.eq_ctxt(map_span) {
return;
}
// is_some_and is stabilised && `unwrap_or` argument is false; suggest `is_some_and` instead
let suggest_is_some_and = matches!(&unwrap_arg.kind, ExprKind::Lit(lit)
if matches!(lit.node, rustc_ast::LitKind::Bool(false)))
&& msrv.meets(cx, msrvs::OPTION_RESULT_IS_VARIANT_AND);
let mut applicability = Applicability::MachineApplicable;
// get snippet for unwrap_or()
let unwrap_snippet = snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability);
// lint message
// comparing the snippet from source to raw text ("None") below is safe
// because we already have checked the type.
let arg = if unwrap_snippet == "None" {
"None"
} else if suggest_is_some_and {
"false"
} else {
"<a>"
};
let unwrap_snippet_none = unwrap_snippet == "None";
let suggest = if unwrap_snippet_none {
"and_then(<f>)"
} else if suggest_is_some_and {
"is_some_and(<f>)"
} else {
"map_or(<a>, <f>)"
};
let msg = format!("called `map(<f>).unwrap_or({arg})` on an `Option` value");
span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| {
let map_arg_span = map_arg.span;
let mut suggestion = vec![
(
map_span,
String::from(if unwrap_snippet_none {
"and_then"
} else if suggest_is_some_and {
"is_some_and"
} else {
"map_or"
}),
),
(expr.span.with_lo(unwrap_recv.span.hi()), String::new()),
];
if !unwrap_snippet_none && !suggest_is_some_and {
suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{unwrap_snippet}, ")));
}
diag.multipart_suggestion(format!("use `{suggest}` instead"), suggestion, applicability);
});
}
}
struct UnwrapVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
identifiers: FxHashSet<HirId>,
}
impl<'tcx> Visitor<'tcx> for UnwrapVisitor<'_, 'tcx> {
type NestedFilter = nested_filter::All;
fn visit_path(&mut self, path: &Path<'tcx>, _: HirId) {
if let Res::Local(local_id) = path.res
&& let Node::Pat(pat) = self.cx.tcx.hir_node(local_id)
&& let PatKind::Binding(_, local_id, ..) = pat.kind
{
self.identifiers.insert(local_id);
}
walk_path(self, path);
}
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
self.cx.tcx
}
}
struct ReferenceVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
identifiers: FxHashSet<HirId>,
unwrap_or_span: Span,
}
impl<'tcx> Visitor<'tcx> for ReferenceVisitor<'_, 'tcx> {
type NestedFilter = nested_filter::All;
type Result = ControlFlow<()>;
fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'_>) -> ControlFlow<()> {
// If we haven't found a reference yet, check if this references
// one of the locals that was moved in the `unwrap_or` argument.
// We are only interested in exprs that appear before the `unwrap_or` call.
if expr.span < self.unwrap_or_span
&& let ExprKind::Path(ref path) = expr.kind
&& let QPath::Resolved(_, path) = path
&& let Res::Local(local_id) = path.res
&& let Node::Pat(pat) = self.cx.tcx.hir_node(local_id)
&& let PatKind::Binding(_, local_id, ..) = pat.kind
&& self.identifiers.contains(&local_id)
{
return ControlFlow::Break(());
}
rustc_hir::intravisit::walk_expr(self, expr)
}
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
self.cx.tcx
}
}

View file

@ -61,7 +61,7 @@ msrv_aliases! {
1,45,0 { STR_STRIP_PREFIX }
1,43,0 { LOG2_10, LOG10_2, NUMERIC_ASSOCIATED_CONSTANTS }
1,42,0 { MATCHES_MACRO, SLICE_PATTERNS, PTR_SLICE_RAW_PARTS }
1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR, RESULT_MAP_OR_ELSE }
1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
1,38,0 { POINTER_CAST, REM_EUCLID }
1,37,0 { TYPE_ALIAS_ENUM_VARIANTS }

View file

@ -281,8 +281,7 @@ impl CrateWithSource {
CrateSource::Path { path } => {
fn is_cache_dir(entry: &DirEntry) -> bool {
fs::read(entry.path().join("CACHEDIR.TAG"))
.map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55"))
.unwrap_or(false)
.is_ok_and(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55"))
}
// copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file.

View file

@ -51,3 +51,19 @@ fn main() {
option_methods();
result_methods();
}
fn issue15714() {
let o: Option<i32> = Some(3);
let r: Result<i32, ()> = Ok(3);
println!("{}", o.map_or(3, |y| y + 1));
//~^ map_unwrap_or
println!("{}", o.map_or_else(|| 3, |y| y + 1));
//~^ map_unwrap_or
println!("{}", r.map_or(3, |y| y + 1));
//~^ map_unwrap_or
println!("{}", r.map_or_else(|()| 3, |y| y + 1));
//~^ map_unwrap_or
println!("{}", r.is_ok_and(|y| y == 1));
//~^ map_unwrap_or
}

View file

@ -57,3 +57,19 @@ fn main() {
option_methods();
result_methods();
}
fn issue15714() {
let o: Option<i32> = Some(3);
let r: Result<i32, ()> = Ok(3);
println!("{}", o.map(|y| y + 1).unwrap_or(3));
//~^ map_unwrap_or
println!("{}", o.map(|y| y + 1).unwrap_or_else(|| 3));
//~^ map_unwrap_or
println!("{}", r.map(|y| y + 1).unwrap_or(3));
//~^ map_unwrap_or
println!("{}", r.map(|y| y + 1).unwrap_or_else(|()| 3));
//~^ map_unwrap_or
println!("{}", r.map(|y| y == 1).unwrap_or(false));
//~^ map_unwrap_or
}

View file

@ -19,5 +19,53 @@ LL | let _ = res.map(|x| x + 1)
LL | | .unwrap_or_else(|_e| 0);
| |_______________________________^ help: try: `res.map_or_else(|_e| 0, |x| x + 1)`
error: aborting due to 2 previous errors
error: called `map(<f>).unwrap_or(<a>)` on an `Option` value
--> tests/ui/map_unwrap_or_fixable.rs:64:20
|
LL | println!("{}", o.map(|y| y + 1).unwrap_or(3));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: use `map_or(<a>, <f>)` instead
|
LL - println!("{}", o.map(|y| y + 1).unwrap_or(3));
LL + println!("{}", o.map_or(3, |y| y + 1));
|
error: called `map(<f>).unwrap_or_else(<g>)` on an `Option` value
--> tests/ui/map_unwrap_or_fixable.rs:66:20
|
LL | println!("{}", o.map(|y| y + 1).unwrap_or_else(|| 3));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `o.map_or_else(|| 3, |y| y + 1)`
error: called `map(<f>).unwrap_or(<a>)` on an `Result` value
--> tests/ui/map_unwrap_or_fixable.rs:68:20
|
LL | println!("{}", r.map(|y| y + 1).unwrap_or(3));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: use `map_or(<a>, <f>)` instead
|
LL - println!("{}", r.map(|y| y + 1).unwrap_or(3));
LL + println!("{}", r.map_or(3, |y| y + 1));
|
error: called `map(<f>).unwrap_or_else(<g>)` on a `Result` value
--> tests/ui/map_unwrap_or_fixable.rs:70:20
|
LL | println!("{}", r.map(|y| y + 1).unwrap_or_else(|()| 3));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `r.map_or_else(|()| 3, |y| y + 1)`
error: called `map(<f>).unwrap_or(false)` on an `Result` value
--> tests/ui/map_unwrap_or_fixable.rs:73:20
|
LL | println!("{}", r.map(|y| y == 1).unwrap_or(false));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: use `is_ok_and(<f>)` instead
|
LL - println!("{}", r.map(|y| y == 1).unwrap_or(false));
LL + println!("{}", r.is_ok_and(|y| y == 1));
|
error: aborting due to 7 previous errors