Skip to content

Commit e477cf9

Browse files
committed
Suggest coercion of Result using ?
Fix #47560.
1 parent 19423b5 commit e477cf9

File tree

4 files changed

+113
-1
lines changed

4 files changed

+113
-1
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use rustc_hir::intravisit::{self, Visitor};
4545
use rustc_hir::Expr;
4646
use rustc_hir_analysis::astconv::AstConv;
4747
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
48-
use rustc_infer::infer::{Coercion, InferOk, InferResult};
48+
use rustc_infer::infer::{Coercion, InferOk, InferResult, TyCtxtInferExt};
4949
use rustc_infer::traits::Obligation;
5050
use rustc_middle::lint::in_external_macro;
5151
use rustc_middle::ty::adjustment::{
@@ -1565,6 +1565,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
15651565
&& let hir::ExprKind::Loop(loop_blk, ..) = expression.kind {
15661566
intravisit::walk_block(& mut visitor, loop_blk);
15671567
}
1568+
if let Some(expr) = expression {
1569+
self.note_result_coercion(fcx, &mut err, expr, expected, found);
1570+
}
15681571
}
15691572
ObligationCauseCode::ReturnValue(id) => {
15701573
err = self.report_return_mismatched_types(
@@ -1581,6 +1584,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
15811584
let id = fcx.tcx.hir().parent_id(id);
15821585
unsized_return = self.is_return_ty_unsized(fcx, id);
15831586
}
1587+
if let Some(expr) = expression {
1588+
self.note_result_coercion(fcx, &mut err, expr, expected, found);
1589+
}
15841590
}
15851591
_ => {
15861592
err = fcx.err_ctxt().report_mismatched_types(
@@ -1619,6 +1625,47 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
16191625
}
16201626
}
16211627
}
1628+
1629+
fn note_result_coercion(
1630+
&self,
1631+
fcx: &FnCtxt<'_, 'tcx>,
1632+
err: &mut Diagnostic,
1633+
expr: &hir::Expr<'tcx>,
1634+
expected: Ty<'tcx>,
1635+
found: Ty<'tcx>,
1636+
) {
1637+
let ty::Adt(e, substs_e) = expected.kind() else { return; };
1638+
let ty::Adt(f, substs_f) = found.kind() else { return; };
1639+
if e.did() != f.did() {
1640+
return;
1641+
}
1642+
if Some(e.did()) != fcx.tcx.get_diagnostic_item(sym::Result) {
1643+
return;
1644+
}
1645+
let e = substs_e.type_at(1);
1646+
let f = substs_f.type_at(1);
1647+
if fcx
1648+
.tcx
1649+
.infer_ctxt()
1650+
.build()
1651+
.type_implements_trait(
1652+
fcx.tcx.get_diagnostic_item(sym::Into).unwrap(),
1653+
[fcx.tcx.erase_regions(f), fcx.tcx.erase_regions(e)],
1654+
fcx.param_env,
1655+
)
1656+
.must_apply_modulo_regions()
1657+
{
1658+
err.multipart_suggestion(
1659+
"you can rely on the implicit conversion that `?` does to transform the error type",
1660+
vec![
1661+
(expr.span.shrink_to_lo(), "Ok(".to_string()),
1662+
(expr.span.shrink_to_hi(), "?)".to_string()),
1663+
],
1664+
Applicability::MaybeIncorrect,
1665+
);
1666+
}
1667+
}
1668+
16221669
fn note_unreachable_loop_return(
16231670
&self,
16241671
err: &mut Diagnostic,
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// run-rustfix
2+
struct A;
3+
struct B;
4+
impl From<A> for B {
5+
fn from(_: A) -> Self { B }
6+
}
7+
fn foo1(x: Result<(), A>) -> Result<(), B> {
8+
Ok(x?) //~ ERROR mismatched types
9+
}
10+
fn foo2(x: Result<(), A>) -> Result<(), B> {
11+
return Ok(x?); //~ ERROR mismatched types
12+
}
13+
fn main() {
14+
let _ = foo1(Ok(()));
15+
let _ = foo2(Ok(()));
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// run-rustfix
2+
struct A;
3+
struct B;
4+
impl From<A> for B {
5+
fn from(_: A) -> Self { B }
6+
}
7+
fn foo1(x: Result<(), A>) -> Result<(), B> {
8+
x //~ ERROR mismatched types
9+
}
10+
fn foo2(x: Result<(), A>) -> Result<(), B> {
11+
return x; //~ ERROR mismatched types
12+
}
13+
fn main() {
14+
let _ = foo1(Ok(()));
15+
let _ = foo2(Ok(()));
16+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/coerce-result-return-value.rs:8:5
3+
|
4+
LL | fn foo1(x: Result<(), A>) -> Result<(), B> {
5+
| ------------- expected `Result<(), B>` because of return type
6+
LL | x
7+
| ^ expected struct `B`, found struct `A`
8+
|
9+
= note: expected enum `Result<_, B>`
10+
found enum `Result<_, A>`
11+
help: you can rely on the implicit conversion that `?` does to transform the error type
12+
|
13+
LL | Ok(x?)
14+
| +++ ++
15+
16+
error[E0308]: mismatched types
17+
--> $DIR/coerce-result-return-value.rs:11:12
18+
|
19+
LL | fn foo2(x: Result<(), A>) -> Result<(), B> {
20+
| ------------- expected `Result<(), B>` because of return type
21+
LL | return x;
22+
| ^ expected struct `B`, found struct `A`
23+
|
24+
= note: expected enum `Result<_, B>`
25+
found enum `Result<_, A>`
26+
help: you can rely on the implicit conversion that `?` does to transform the error type
27+
|
28+
LL | return Ok(x?);
29+
| +++ ++
30+
31+
error: aborting due to 2 previous errors
32+
33+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)