From e477cf9475540bf7f5f71940b29fc065cb988982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 8 Jan 2023 02:54:59 +0000 Subject: [PATCH] Suggest coercion of `Result` using `?` Fix #47560. --- compiler/rustc_hir_typeck/src/coercion.rs | 49 ++++++++++++++++++- .../coerce-result-return-value.fixed | 16 ++++++ .../type-check/coerce-result-return-value.rs | 16 ++++++ .../coerce-result-return-value.stderr | 33 +++++++++++++ 4 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 tests/ui/type/type-check/coerce-result-return-value.fixed create mode 100644 tests/ui/type/type-check/coerce-result-return-value.rs create mode 100644 tests/ui/type/type-check/coerce-result-return-value.stderr diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index bbf7b81a2cc66..752e3f79d4abf 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 0000000000000..91066262303ec --- /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 0000000000000..9a71376f462dc --- /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 0000000000000..7aebc9dcc7ad8 --- /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`.