diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index bbf7b81a2cc..752e3f79d4a 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -45,7 +45,7 @@ use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::Expr;
use rustc_hir_analysis::astconv::AstConv;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use rustc_infer::infer::{Coercion, InferOk, InferResult};
+use rustc_infer::infer::{Coercion, InferOk, InferResult, TyCtxtInferExt};
use rustc_infer::traits::Obligation;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::adjustment::{
@@ -1565,6 +1565,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
&& let hir::ExprKind::Loop(loop_blk, ..) = expression.kind {
intravisit::walk_block(& mut visitor, loop_blk);
}
+ if let Some(expr) = expression {
+ self.note_result_coercion(fcx, &mut err, expr, expected, found);
+ }
}
ObligationCauseCode::ReturnValue(id) => {
err = self.report_return_mismatched_types(
@@ -1581,6 +1584,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
let id = fcx.tcx.hir().parent_id(id);
unsized_return = self.is_return_ty_unsized(fcx, id);
}
+ if let Some(expr) = expression {
+ self.note_result_coercion(fcx, &mut err, expr, expected, found);
+ }
}
_ => {
err = fcx.err_ctxt().report_mismatched_types(
@@ -1619,6 +1625,47 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
}
}
}
+
+ fn note_result_coercion(
+ &self,
+ fcx: &FnCtxt<'_, 'tcx>,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'tcx>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ ) {
+ let ty::Adt(e, substs_e) = expected.kind() else { return; };
+ let ty::Adt(f, substs_f) = found.kind() else { return; };
+ if e.did() != f.did() {
+ return;
+ }
+ if Some(e.did()) != fcx.tcx.get_diagnostic_item(sym::Result) {
+ return;
+ }
+ let e = substs_e.type_at(1);
+ let f = substs_f.type_at(1);
+ if fcx
+ .tcx
+ .infer_ctxt()
+ .build()
+ .type_implements_trait(
+ fcx.tcx.get_diagnostic_item(sym::Into).unwrap(),
+ [fcx.tcx.erase_regions(f), fcx.tcx.erase_regions(e)],
+ fcx.param_env,
+ )
+ .must_apply_modulo_regions()
+ {
+ err.multipart_suggestion(
+ "you can rely on the implicit conversion that `?` does to transform the error type",
+ vec![
+ (expr.span.shrink_to_lo(), "Ok(".to_string()),
+ (expr.span.shrink_to_hi(), "?)".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+
fn note_unreachable_loop_return(
&self,
err: &mut Diagnostic,
diff --git a/tests/ui/type/type-check/coerce-result-return-value.fixed b/tests/ui/type/type-check/coerce-result-return-value.fixed
new file mode 100644
index 00000000000..91066262303
--- /dev/null
+++ b/tests/ui/type/type-check/coerce-result-return-value.fixed
@@ -0,0 +1,16 @@
+// run-rustfix
+struct A;
+struct B;
+impl From for B {
+ fn from(_: A) -> Self { B }
+}
+fn foo1(x: Result<(), A>) -> Result<(), B> {
+ Ok(x?) //~ ERROR mismatched types
+}
+fn foo2(x: Result<(), A>) -> Result<(), B> {
+ return Ok(x?); //~ ERROR mismatched types
+}
+fn main() {
+ let _ = foo1(Ok(()));
+ let _ = foo2(Ok(()));
+}
diff --git a/tests/ui/type/type-check/coerce-result-return-value.rs b/tests/ui/type/type-check/coerce-result-return-value.rs
new file mode 100644
index 00000000000..9a71376f462
--- /dev/null
+++ b/tests/ui/type/type-check/coerce-result-return-value.rs
@@ -0,0 +1,16 @@
+// run-rustfix
+struct A;
+struct B;
+impl From for B {
+ fn from(_: A) -> Self { B }
+}
+fn foo1(x: Result<(), A>) -> Result<(), B> {
+ x //~ ERROR mismatched types
+}
+fn foo2(x: Result<(), A>) -> Result<(), B> {
+ return x; //~ ERROR mismatched types
+}
+fn main() {
+ let _ = foo1(Ok(()));
+ let _ = foo2(Ok(()));
+}
diff --git a/tests/ui/type/type-check/coerce-result-return-value.stderr b/tests/ui/type/type-check/coerce-result-return-value.stderr
new file mode 100644
index 00000000000..7aebc9dcc7a
--- /dev/null
+++ b/tests/ui/type/type-check/coerce-result-return-value.stderr
@@ -0,0 +1,33 @@
+error[E0308]: mismatched types
+ --> $DIR/coerce-result-return-value.rs:8:5
+ |
+LL | fn foo1(x: Result<(), A>) -> Result<(), B> {
+ | ------------- expected `Result<(), B>` because of return type
+LL | x
+ | ^ expected struct `B`, found struct `A`
+ |
+ = note: expected enum `Result<_, B>`
+ found enum `Result<_, A>`
+help: you can rely on the implicit conversion that `?` does to transform the error type
+ |
+LL | Ok(x?)
+ | +++ ++
+
+error[E0308]: mismatched types
+ --> $DIR/coerce-result-return-value.rs:11:12
+ |
+LL | fn foo2(x: Result<(), A>) -> Result<(), B> {
+ | ------------- expected `Result<(), B>` because of return type
+LL | return x;
+ | ^ expected struct `B`, found struct `A`
+ |
+ = note: expected enum `Result<_, B>`
+ found enum `Result<_, A>`
+help: you can rely on the implicit conversion that `?` does to transform the error type
+ |
+LL | return Ok(x?);
+ | +++ ++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.