Skip to content

Commit b5ee095

Browse files
committed
Add suggestion to use Option::map_or over erroneous Option::unwrap_or
1 parent 70a4b54 commit b5ee095

File tree

3 files changed

+84
-0
lines changed

3 files changed

+84
-0
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

+65
Original file line numberDiff line numberDiff line change
@@ -1407,6 +1407,71 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14071407
true
14081408
}
14091409

1410+
// Suggest to change `Option<&Vec<T>>::unwrap_or(&[])` to `Option::map_or(&[], |v| v)`.
1411+
#[instrument(level = "trace", skip(self, err, provided_expr))]
1412+
pub(crate) fn suggest_deref_unwrap_or(
1413+
&self,
1414+
err: &mut Diag<'_>,
1415+
error_span: Span,
1416+
callee_ty: Option<Ty<'tcx>>,
1417+
call_ident: Option<Ident>,
1418+
expected_ty: Ty<'tcx>,
1419+
provided_ty: Ty<'tcx>,
1420+
provided_expr: &Expr<'tcx>,
1421+
is_method: bool,
1422+
) {
1423+
if !is_method {
1424+
return;
1425+
}
1426+
let Some(callee_ty) = callee_ty else {
1427+
return;
1428+
};
1429+
let ty::Adt(callee_adt, _) = callee_ty.peel_refs().kind() else {
1430+
return;
1431+
};
1432+
if !self.tcx.is_diagnostic_item(sym::Option, callee_adt.did()) {
1433+
return;
1434+
}
1435+
1436+
if call_ident.map_or(true, |ident| ident.name != sym::unwrap_or) {
1437+
return;
1438+
}
1439+
1440+
let ty::Ref(_, peeled, _mutability) = provided_ty.kind() else {
1441+
return;
1442+
};
1443+
1444+
// NOTE: Can we reuse `suggest_deref_or_ref`?
1445+
1446+
// Create an dummy type `&[_]` so that both &[] and `&Vec<T>` can coerce to it.
1447+
let dummy_ty = if let ty::Array(elem_ty, size) = peeled.kind()
1448+
&& let ty::Infer(_) = elem_ty.kind()
1449+
&& size.try_eval_target_usize(self.tcx, self.param_env) == Some(0)
1450+
{
1451+
let slice = Ty::new_slice(self.tcx, *elem_ty);
1452+
Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, slice)
1453+
} else {
1454+
provided_ty
1455+
};
1456+
1457+
if !self.can_coerce(expected_ty, dummy_ty) {
1458+
return;
1459+
}
1460+
let (provided_snip, applicability) =
1461+
match self.tcx.sess.source_map().span_to_snippet(provided_expr.span) {
1462+
Ok(snip) => (snip, Applicability::MachineApplicable),
1463+
Err(_) => ("/* _ */".to_owned(), Applicability::MaybeIncorrect),
1464+
};
1465+
let sugg = &format!("map_or({provided_snip}, |v| v)");
1466+
err.span_suggestion_verbose(
1467+
error_span,
1468+
"use `Option::map_or` to deref inner value of `Option`",
1469+
sugg,
1470+
applicability,
1471+
);
1472+
return;
1473+
}
1474+
14101475
/// Suggest wrapping the block in square brackets instead of curly braces
14111476
/// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
14121477
pub(crate) fn suggest_block_to_brackets(

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

+8
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ LL | arg.unwrap_or(&[])
1717
| this argument influences the return type of `unwrap_or`
1818
note: method defined here
1919
--> $SRC_DIR/core/src/option.rs:LL:COL
20+
help: use `Option::map_or` to deref inner value of `Option`
21+
|
22+
LL | arg.map_or(&[], |v| v)
23+
| ~~~~~~~~~~~~~~~~~~
2024

2125
error[E0308]: mismatched types
2226
--> $DIR/transforming-option-ref-issue-127545.rs:9:19
@@ -37,6 +41,10 @@ LL | arg.unwrap_or(v)
3741
| this argument influences the return type of `unwrap_or`
3842
note: method defined here
3943
--> $SRC_DIR/core/src/option.rs:LL:COL
44+
help: use `Option::map_or` to deref inner value of `Option`
45+
|
46+
LL | arg.map_or(v, |v| v)
47+
| ~~~~~~~~~~~~~~~~
4048

4149
error: aborting due to 2 previous errors
4250

0 commit comments

Comments
 (0)