about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2023-10-07 01:14:43 +0000
committerEsteban Küber <esteban@kuber.com.ar>2023-12-05 22:22:13 +0000
commit53817963ed8796b35f802d87866aea8de5d4caf6 (patch)
tree870b53d8ca8ba58b8e5d719a7e9b877219f14a37
parent6c3879d1f154bb6f18562d29aa30fbc03239c66f (diff)
downloadrust-53817963ed8796b35f802d87866aea8de5d4caf6.tar.gz
rust-53817963ed8796b35f802d87866aea8de5d4caf6.zip
Point at fewer methods in the chain, only those that change the E type
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs76
-rw-r--r--tests/ui/traits/question-mark-result-err-mismatch.rs15
-rw-r--r--tests/ui/traits/question-mark-result-err-mismatch.stderr26
3 files changed, 64 insertions, 53 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index bad74588d0c..58ea7a0edd5 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -1049,39 +1049,25 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         let mut prev_ty = self.resolve_vars_if_possible(
             typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)),
         );
-        let mut annotate_expr = |span: Span, prev_ty: Ty<'tcx>, self_ty: Ty<'tcx>| -> bool {
-            // We always look at the `E` type, because that's the only one affected by `?`. If the
-            // incorrect `Result<T, E>` is because of the `T`, we'll get an E0308 on the whole
-            // expression, after the `?` has "unwrapped" the `T`.
+
+        // We always look at the `E` type, because that's the only one affected by `?`. If the
+        // incorrect `Result<T, E>` is because of the `T`, we'll get an E0308 on the whole
+        // expression, after the `?` has "unwrapped" the `T`.
+        let get_e_type = |prev_ty: Ty<'tcx>| -> Option<Ty<'tcx>> {
             let ty::Adt(def, args) = prev_ty.kind() else {
-                return false;
+                return None;
             };
             let Some(arg) = args.get(1) else {
-                return false;
+                return None;
             };
             if !self.tcx.is_diagnostic_item(sym::Result, def.did()) {
-                return false;
+                return None;
             }
-            let can = if self
-                .infcx
-                .type_implements_trait(
-                    self.tcx.get_diagnostic_item(sym::From).unwrap(),
-                    [self_ty.into(), *arg],
-                    obligation.param_env,
-                )
-                .must_apply_modulo_regions()
-            {
-                "can"
-            } else {
-                "can't"
-            };
-            err.span_label(
-                span,
-                format!("this {can} be annotated with `?` because it has type `{prev_ty}`"),
-            );
-            true
+            Some(arg.as_type()?)
         };
 
+        let mut chain = vec![];
+
         // The following logic is simlar to `point_at_chain`, but that's focused on associated types
         let mut expr = expr;
         while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, span) = expr.kind {
@@ -1089,9 +1075,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             // let foo = bar.iter().map(mapper)?;
             //               ------ -----------
             expr = rcvr_expr;
-            if !annotate_expr(span, prev_ty, self_ty) {
-                break;
-            }
+            chain.push((span, prev_ty));
 
             prev_ty = self.resolve_vars_if_possible(
                 typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)),
@@ -1121,7 +1105,41 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         prev_ty = self.resolve_vars_if_possible(
             typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)),
         );
-        annotate_expr(expr.span, prev_ty, self_ty);
+        chain.push((expr.span, prev_ty));
+
+        let mut prev = None;
+        for (span, err_ty) in chain.into_iter().rev() {
+            let err_ty = get_e_type(err_ty);
+            let err_ty = match (err_ty, prev) {
+                (Some(err_ty), Some(prev)) if !self.can_eq(obligation.param_env, err_ty, prev) => {
+                    err_ty
+                }
+                (Some(err_ty), None) => err_ty,
+                _ => {
+                    prev = err_ty;
+                    continue;
+                }
+            };
+            if self
+                .infcx
+                .type_implements_trait(
+                    self.tcx.get_diagnostic_item(sym::From).unwrap(),
+                    [self_ty, err_ty],
+                    obligation.param_env,
+                )
+                .must_apply_modulo_regions()
+            {
+                err.span_label(span, format!("this has type `Result<_, {err_ty}>`"));
+            } else {
+                err.span_label(
+                span,
+                format!(
+                    "this can't be annotated with `?` because it has type `Result<_, {err_ty}>`",
+                ),
+            );
+            }
+            prev = Some(err_ty);
+        }
     }
 
     fn report_const_param_not_wf(
diff --git a/tests/ui/traits/question-mark-result-err-mismatch.rs b/tests/ui/traits/question-mark-result-err-mismatch.rs
index 7b364580858..e5ccca2e5f7 100644
--- a/tests/ui/traits/question-mark-result-err-mismatch.rs
+++ b/tests/ui/traits/question-mark-result-err-mismatch.rs
@@ -3,18 +3,17 @@ fn foo() -> Result<String, String> { //~ NOTE expected `String` because of this
     let x = test
         .split_whitespace()
         .next()
-        .ok_or_else(|| { //~ NOTE this can be annotated with `?` because it has type `Result<&str, &str>`
+        .ok_or_else(|| { //~ NOTE this has type `Result<_, &str>`
             "Couldn't split the test string"
         });
     let one = x
-        .map(|s| ()) //~ NOTE this can be annotated with `?` because it has type `Result<(), &str>`
-        .map_err(|_| ()) //~ NOTE this can't be annotated with `?` because it has type `Result<(), ()>`
+        .map(|s| ())
+        .map_err(|_| ()) //~ NOTE this can't be annotated with `?` because it has type `Result<_, ()>`
         .map(|()| "")?; //~ ERROR `?` couldn't convert the error to `String`
     //~^ NOTE in this expansion of desugaring of operator `?`
     //~| NOTE in this expansion of desugaring of operator `?`
     //~| NOTE in this expansion of desugaring of operator `?`
     //~| NOTE in this expansion of desugaring of operator `?`
-    //~| NOTE this can't be annotated with `?` because it has type `Result<&str, ()>`
     //~| NOTE the trait `From<()>` is not implemented for `String`
     //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
     //~| NOTE required for `Result<String, String>` to implement `FromResidual<Result<Infallible, ()>>`
@@ -22,15 +21,15 @@ fn foo() -> Result<String, String> { //~ NOTE expected `String` because of this
 }
 
 fn bar() -> Result<(), String> { //~ NOTE expected `String` because of this
-    let x = foo(); //~ NOTE this can be annotated with `?` because it has type `Result<String, String>`
+    let x = foo(); //~ NOTE this has type `Result<_, String>`
     let one = x
-        .map(|s| ()) //~ NOTE this can be annotated with `?` because it has type `Result<(), String>`
+        .map(|s| ())
         .map_err(|_| ())?; //~ ERROR `?` couldn't convert the error to `String`
     //~^ NOTE in this expansion of desugaring of operator `?`
     //~| NOTE in this expansion of desugaring of operator `?`
     //~| NOTE in this expansion of desugaring of operator `?`
     //~| NOTE in this expansion of desugaring of operator `?`
-    //~| NOTE this can't be annotated with `?` because it has type `Result<(), ()>`
+    //~| NOTE this can't be annotated with `?` because it has type `Result<_, ()>`
     //~| NOTE the trait `From<()>` is not implemented for `String`
     //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
     //~| NOTE required for `Result<(), String>` to implement `FromResidual<Result<Infallible, ()>>`
@@ -42,7 +41,7 @@ fn baz() -> Result<String, String> { //~ NOTE expected `String` because of this
     let one = test
         .split_whitespace()
         .next()
-        .ok_or_else(|| { //~ NOTE this can't be annotated with `?` because it has type `Result<&str, ()>`
+        .ok_or_else(|| { //~ NOTE this can't be annotated with `?` because it has type `Result<_, ()>`
             "Couldn't split the test string";
         })?;
     //~^ ERROR `?` couldn't convert the error to `String`
diff --git a/tests/ui/traits/question-mark-result-err-mismatch.stderr b/tests/ui/traits/question-mark-result-err-mismatch.stderr
index f6acbc6dd07..fc3b2e6b46b 100644
--- a/tests/ui/traits/question-mark-result-err-mismatch.stderr
+++ b/tests/ui/traits/question-mark-result-err-mismatch.stderr
@@ -8,16 +8,12 @@ LL |           .ok_or_else(|| {
    |  __________-
 LL | |             "Couldn't split the test string"
 LL | |         });
-   | |__________- this can be annotated with `?` because it has type `Result<&str, &str>`
-LL |       let one = x
-LL |           .map(|s| ())
-   |            ----------- this can be annotated with `?` because it has type `Result<(), &str>`
+   | |__________- this has type `Result<_, &str>`
+...
 LL |           .map_err(|_| ())
-   |            --------------- this can't be annotated with `?` because it has type `Result<(), ()>`
+   |            --------------- this can't be annotated with `?` because it has type `Result<_, ()>`
 LL |           .map(|()| "")?;
-   |            ------------^ the trait `From<()>` is not implemented for `String`
-   |            |
-   |            this can't be annotated with `?` because it has type `Result<&str, ()>`
+   |                        ^ the trait `From<()>` is not implemented for `String`
    |
    = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
    = help: the following other types implement trait `From<T>`:
@@ -30,19 +26,17 @@ LL |           .map(|()| "")?;
    = note: required for `Result<String, String>` to implement `FromResidual<Result<Infallible, ()>>`
 
 error[E0277]: `?` couldn't convert the error to `String`
-  --> $DIR/question-mark-result-err-mismatch.rs:28:25
+  --> $DIR/question-mark-result-err-mismatch.rs:27:25
    |
 LL | fn bar() -> Result<(), String> {
    |             ------------------ expected `String` because of this
 LL |     let x = foo();
-   |             ----- this can be annotated with `?` because it has type `Result<String, String>`
-LL |     let one = x
-LL |         .map(|s| ())
-   |          ----------- this can be annotated with `?` because it has type `Result<(), String>`
+   |             ----- this has type `Result<_, String>`
+...
 LL |         .map_err(|_| ())?;
    |          ---------------^ the trait `From<()>` is not implemented for `String`
    |          |
-   |          this can't be annotated with `?` because it has type `Result<(), ()>`
+   |          this can't be annotated with `?` because it has type `Result<_, ()>`
    |
    = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
    = help: the following other types implement trait `From<T>`:
@@ -55,7 +49,7 @@ LL |         .map_err(|_| ())?;
    = note: required for `Result<(), String>` to implement `FromResidual<Result<Infallible, ()>>`
 
 error[E0277]: `?` couldn't convert the error to `String`
-  --> $DIR/question-mark-result-err-mismatch.rs:47:11
+  --> $DIR/question-mark-result-err-mismatch.rs:46:11
    |
 LL |   fn baz() -> Result<String, String> {
    |               ---------------------- expected `String` because of this
@@ -66,7 +60,7 @@ LL | |             "Couldn't split the test string";
 LL | |         })?;
    | |          -^ the trait `From<()>` is not implemented for `String`
    | |__________|
-   |            this can't be annotated with `?` because it has type `Result<&str, ()>`
+   |            this can't be annotated with `?` because it has type `Result<_, ()>`
    |
    = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
    = help: the following other types implement trait `From<T>`: