]> git.lizzy.rs Git - rust.git/commitdiff
Suggest coercion of `Result` using `?`
authorEsteban Küber <esteban@kuber.com.ar>
Sun, 8 Jan 2023 02:54:59 +0000 (02:54 +0000)
committerEsteban Küber <esteban@kuber.com.ar>
Mon, 23 Jan 2023 14:46:59 +0000 (14:46 +0000)
Fix #47560.

compiler/rustc_hir_typeck/src/coercion.rs
tests/ui/type/type-check/coerce-result-return-value.fixed [new file with mode: 0644]
tests/ui/type/type-check/coerce-result-return-value.rs [new file with mode: 0644]
tests/ui/type/type-check/coerce-result-return-value.stderr [new file with mode: 0644]

index bbf7b81a2cc66ca82ccff36b50b0947c99710b77..752e3f79d4abf2545a00bbd5de280dc54a1f1b15 100644 (file)
@@ -45,7 +45,7 @@
 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 @@ pub(crate) fn coerce_inner<'a>(
                             && 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 @@ pub(crate) fn coerce_inner<'a>(
                             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 @@ pub(crate) fn coerce_inner<'a>(
             }
         }
     }
+
+    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 (file)
index 0000000..9106626
--- /dev/null
@@ -0,0 +1,16 @@
+// run-rustfix
+struct A;
+struct B;
+impl From<A> 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 (file)
index 0000000..9a71376
--- /dev/null
@@ -0,0 +1,16 @@
+// run-rustfix
+struct A;
+struct B;
+impl From<A> 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 (file)
index 0000000..7aebc9d
--- /dev/null
@@ -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`.