Skip to content

Commit 7e719c9

Browse files
committed
Add suggestion to use Option::map_or over erroneous Option::unwrap_or
1 parent 1616359 commit 7e719c9

File tree

3 files changed

+85
-1
lines changed

3 files changed

+85
-1
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+11
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
935935
&mut err,
936936
);
937937

938+
self.suggest_deref_unwrap_or(
939+
&mut err,
940+
error_span,
941+
callee_ty,
942+
call_ident,
943+
expected_ty,
944+
provided_ty,
945+
provided_args[*provided_idx],
946+
is_method,
947+
);
948+
938949
// Call out where the function is defined
939950
self.label_fn_like(
940951
&mut err,

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+66-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::method::probe;
88
use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
99
use core::cmp::min;
1010
use core::iter;
11-
use hir::def_id::{LocalDefId};
11+
use hir::def_id::LocalDefId;
1212
use rustc_ast::util::parser::{ExprPrecedence, PREC_UNAMBIGUOUS};
1313
use rustc_data_structures::packed::Pu128;
1414
use rustc_errors::{Applicability, Diag, MultiSpan};
@@ -1429,6 +1429,71 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14291429
true
14301430
}
14311431

1432+
// Suggest to change `Option<&Vec<T>>::unwrap_or(&[])` to `Option::map_or(&[], |v| v)`.
1433+
#[instrument(level = "trace", skip(self, err, provided_expr))]
1434+
pub(crate) fn suggest_deref_unwrap_or(
1435+
&self,
1436+
err: &mut Diag<'_>,
1437+
error_span: Span,
1438+
callee_ty: Option<Ty<'tcx>>,
1439+
call_ident: Option<Ident>,
1440+
expected_ty: Ty<'tcx>,
1441+
provided_ty: Ty<'tcx>,
1442+
provided_expr: &Expr<'tcx>,
1443+
is_method: bool,
1444+
) {
1445+
if !is_method {
1446+
return;
1447+
}
1448+
let Some(callee_ty) = callee_ty else {
1449+
return;
1450+
};
1451+
let ty::Adt(callee_adt, _) = callee_ty.peel_refs().kind() else {
1452+
return;
1453+
};
1454+
if !self.tcx.is_diagnostic_item(sym::Option, callee_adt.did()) {
1455+
return;
1456+
}
1457+
1458+
if call_ident.map_or(true, |ident| ident.name != sym::unwrap_or) {
1459+
return;
1460+
}
1461+
1462+
let ty::Ref(_, peeled, _mutability) = provided_ty.kind() else {
1463+
return;
1464+
};
1465+
1466+
// NOTE: Can we reuse `suggest_deref_or_ref`?
1467+
1468+
// Create an dummy type `&[_]` so that both &[] and `&Vec<T>` can coerce to it.
1469+
let dummy_ty = if let ty::Array(elem_ty, size) = peeled.kind()
1470+
&& let ty::Infer(_) = elem_ty.kind()
1471+
&& size.try_eval_target_usize(self.tcx, self.param_env) == Some(0)
1472+
{
1473+
let slice = Ty::new_slice(self.tcx, *elem_ty);
1474+
Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, slice)
1475+
} else {
1476+
provided_ty
1477+
};
1478+
1479+
if !self.can_coerce(expected_ty, dummy_ty) {
1480+
return;
1481+
}
1482+
let (provided_snip, applicability) =
1483+
match self.tcx.sess.source_map().span_to_snippet(provided_expr.span) {
1484+
Ok(snip) => (snip, Applicability::MachineApplicable),
1485+
Err(_) => ("/* _ */".to_owned(), Applicability::MaybeIncorrect),
1486+
};
1487+
let sugg = &format!("map_or({provided_snip}, |v| v)");
1488+
err.span_suggestion_verbose(
1489+
error_span,
1490+
"use `Option::map_or` to deref inner value of `Option`",
1491+
sugg,
1492+
applicability,
1493+
);
1494+
return;
1495+
}
1496+
14321497
/// Suggest wrapping the block in square brackets instead of curly braces
14331498
/// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
14341499
pub(crate) fn suggest_block_to_brackets(

tests/ui/mismatched_types/issue-127545-option-of-ref.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ LL | arg.unwrap_or(&[])
3232
| this argument influences the return type of `unwrap_or`
3333
note: method defined here
3434
--> $SRC_DIR/core/src/option.rs:LL:COL
35+
help: use `Option::map_or` to deref inner value of `Option`
36+
|
37+
LL | arg.map_or(&[], |v| v)
38+
| ~~~~~~~~~~~~~~~~~~
3539

3640
error[E0308]: mismatched types
3741
--> $DIR/issue-127545-option-of-ref.rs:13:19
@@ -52,6 +56,10 @@ LL | arg.unwrap_or(v)
5256
| this argument influences the return type of `unwrap_or`
5357
note: method defined here
5458
--> $SRC_DIR/core/src/option.rs:LL:COL
59+
help: use `Option::map_or` to deref inner value of `Option`
60+
|
61+
LL | arg.map_or(v, |v| v)
62+
| ~~~~~~~~~~~~~~~~
5563

5664
error: aborting due to 3 previous errors
5765

0 commit comments

Comments
 (0)