Skip to content

Commit cae4a84

Browse files
committed
Auto merge of #127629 - tesuji:suggest-option-ref-unwrap_or, r=estebank
Suggest using `map_or` when `Option<&T>::unwrap_or where T: Deref` fails Fix #127545 Split from #127596 (review)
2 parents 24d2ac0 + bdc9df2 commit cae4a84

File tree

4 files changed

+164
-1
lines changed

4 files changed

+164
-1
lines changed

Diff for: compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+11
Original file line numberDiff line numberDiff line change
@@ -948,6 +948,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
948948
&mut err,
949949
);
950950

951+
self.suggest_deref_unwrap_or(
952+
&mut err,
953+
error_span,
954+
callee_ty,
955+
call_ident,
956+
expected_ty,
957+
provided_ty,
958+
provided_args[*provided_idx],
959+
is_method,
960+
);
961+
951962
// Call out where the function is defined
952963
self.label_fn_like(
953964
&mut err,

Diff for: compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+68
Original file line numberDiff line numberDiff line change
@@ -1429,6 +1429,74 @@ 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+
let adt_name = if self.tcx.is_diagnostic_item(sym::Option, callee_adt.did()) {
1455+
"Option"
1456+
} else if self.tcx.is_diagnostic_item(sym::Result, callee_adt.did()) {
1457+
"Result"
1458+
} else {
1459+
return;
1460+
};
1461+
1462+
let Some(call_ident) = call_ident else {
1463+
return;
1464+
};
1465+
if call_ident.name != sym::unwrap_or {
1466+
return;
1467+
}
1468+
1469+
let ty::Ref(_, peeled, _mutability) = provided_ty.kind() else {
1470+
return;
1471+
};
1472+
1473+
// NOTE: Can we reuse `suggest_deref_or_ref`?
1474+
1475+
// Create an dummy type `&[_]` so that both &[] and `&Vec<T>` can coerce to it.
1476+
let dummy_ty = if let ty::Array(elem_ty, size) = peeled.kind()
1477+
&& let ty::Infer(_) = elem_ty.kind()
1478+
&& size.try_eval_target_usize(self.tcx, self.param_env) == Some(0)
1479+
{
1480+
let slice = Ty::new_slice(self.tcx, *elem_ty);
1481+
Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, slice)
1482+
} else {
1483+
provided_ty
1484+
};
1485+
1486+
if !self.can_coerce(expected_ty, dummy_ty) {
1487+
return;
1488+
}
1489+
let msg = format!("use `{adt_name}::map_or` to deref inner value of `{adt_name}`");
1490+
err.multipart_suggestion_verbose(
1491+
msg,
1492+
vec![
1493+
(call_ident.span, "map_or".to_owned()),
1494+
(provided_expr.span.shrink_to_hi(), ", |v| v".to_owned()),
1495+
],
1496+
Applicability::MachineApplicable,
1497+
);
1498+
}
1499+
14321500
/// Suggest wrapping the block in square brackets instead of curly braces
14331501
/// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
14341502
pub(crate) fn suggest_block_to_brackets(

Diff for: tests/ui/mismatched_types/transforming-option-ref-issue-127545.rs

+12
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,15 @@
44
pub fn foo(arg: Option<&Vec<i32>>) -> Option<&[i32]> {
55
arg //~ ERROR 5:5: 5:8: mismatched types [E0308]
66
}
7+
8+
pub fn bar(arg: Option<&Vec<i32>>) -> &[i32] {
9+
arg.unwrap_or(&[]) //~ ERROR 9:19: 9:22: mismatched types [E0308]
10+
}
11+
12+
pub fn barzz<'a>(arg: Option<&'a Vec<i32>>, v: &'a [i32]) -> &'a [i32] {
13+
arg.unwrap_or(v) //~ ERROR 13:19: 13:20: mismatched types [E0308]
14+
}
15+
16+
pub fn convert_result(arg: Result<&Vec<i32>, ()>) -> &[i32] {
17+
arg.unwrap_or(&[]) //~ ERROR 17:19: 17:22: mismatched types [E0308]
18+
}

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

+73-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,78 @@ help: try using `.map(|v| &**v)` to convert `Option<&Vec<i32>>` to `Option<&[i32
1313
LL | arg.map(|v| &**v)
1414
| ++++++++++++++
1515

16-
error: aborting due to 1 previous error
16+
error[E0308]: mismatched types
17+
--> $DIR/transforming-option-ref-issue-127545.rs:9:19
18+
|
19+
LL | arg.unwrap_or(&[])
20+
| --------- ^^^ expected `&Vec<i32>`, found `&[_; 0]`
21+
| |
22+
| arguments to this method are incorrect
23+
|
24+
= note: expected reference `&Vec<i32>`
25+
found reference `&[_; 0]`
26+
help: the return type of this call is `&[_; 0]` due to the type of the argument passed
27+
--> $DIR/transforming-option-ref-issue-127545.rs:9:5
28+
|
29+
LL | arg.unwrap_or(&[])
30+
| ^^^^^^^^^^^^^^---^
31+
| |
32+
| this argument influences the return type of `unwrap_or`
33+
note: method defined here
34+
--> $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+
| ~~~~~~ +++++++
39+
40+
error[E0308]: mismatched types
41+
--> $DIR/transforming-option-ref-issue-127545.rs:13:19
42+
|
43+
LL | arg.unwrap_or(v)
44+
| --------- ^ expected `&Vec<i32>`, found `&[i32]`
45+
| |
46+
| arguments to this method are incorrect
47+
|
48+
= note: expected reference `&Vec<i32>`
49+
found reference `&'a [i32]`
50+
help: the return type of this call is `&'a [i32]` due to the type of the argument passed
51+
--> $DIR/transforming-option-ref-issue-127545.rs:13:5
52+
|
53+
LL | arg.unwrap_or(v)
54+
| ^^^^^^^^^^^^^^-^
55+
| |
56+
| this argument influences the return type of `unwrap_or`
57+
note: method defined here
58+
--> $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+
| ~~~~~~ +++++++
63+
64+
error[E0308]: mismatched types
65+
--> $DIR/transforming-option-ref-issue-127545.rs:17:19
66+
|
67+
LL | arg.unwrap_or(&[])
68+
| --------- ^^^ expected `&Vec<i32>`, found `&[_; 0]`
69+
| |
70+
| arguments to this method are incorrect
71+
|
72+
= note: expected reference `&Vec<i32>`
73+
found reference `&[_; 0]`
74+
help: the return type of this call is `&[_; 0]` due to the type of the argument passed
75+
--> $DIR/transforming-option-ref-issue-127545.rs:17:5
76+
|
77+
LL | arg.unwrap_or(&[])
78+
| ^^^^^^^^^^^^^^---^
79+
| |
80+
| this argument influences the return type of `unwrap_or`
81+
note: method defined here
82+
--> $SRC_DIR/core/src/result.rs:LL:COL
83+
help: use `Result::map_or` to deref inner value of `Result`
84+
|
85+
LL | arg.map_or(&[], |v| v)
86+
| ~~~~~~ +++++++
87+
88+
error: aborting due to 4 previous errors
1789

1890
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)