about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2025-02-18 20:29:10 +0000
committerEsteban Küber <esteban@kuber.com.ar>2025-02-20 18:15:39 +0000
commite565eeed78d0e59e475dce34b0833bbf8f84871b (patch)
tree1c290b3048e8afdd32dc25ab62fbaf08cbc9eb73
parent28b83ee59698ae069f5355b8e03f976406f410f5 (diff)
downloadrust-e565eeed78d0e59e475dce34b0833bbf8f84871b.tar.gz
rust-e565eeed78d0e59e475dce34b0833bbf8f84871b.zip
Tweak E0277 when predicate comes indirectly from `?`
When a `?` operation requires an `Into` conversion with additional bounds (like having a concrete error but wanting to convert to a trait object), we handle it speficically and provide the same kind of information we give other `?` related errors.

```
error[E0277]: `?` couldn't convert the error: `E: std::error::Error` is not satisfied
  --> $DIR/bad-question-mark-on-trait-object.rs:5:13
   |
LL | fn foo() -> Result<(), Box<dyn std::error::Error>> {
   |             -------------------------------------- required `E: std::error::Error` because of this
LL |     Ok(bar()?)
   |             ^ the trait `std::error::Error` is not implemented for `E`
   |
   = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
   = note: required for `Box<dyn std::error::Error>` to implement `From<E>`
```

Avoid talking about `FromResidual` when other more relevant information is being given, particularly from `rust_on_unimplemented`.
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs68
-rw-r--r--tests/ui/async-await/issue-84841.stderr2
-rw-r--r--tests/ui/async-await/try-on-option-in-async.stderr6
-rw-r--r--tests/ui/return/return-from-residual-sugg-issue-125997.stderr6
-rw-r--r--tests/ui/try-trait/bad-interconversion.stderr12
-rw-r--r--tests/ui/try-trait/bad-question-mark-on-trait-object.rs26
-rw-r--r--tests/ui/try-trait/bad-question-mark-on-trait-object.stderr26
-rw-r--r--tests/ui/try-trait/option-to-result.stderr6
-rw-r--r--tests/ui/try-trait/try-on-option-diagnostics.stderr8
-rw-r--r--tests/ui/try-trait/try-on-option.stderr5
-rw-r--r--tests/ui/try-trait/try-operator-on-main.stderr3
11 files changed, 103 insertions, 65 deletions
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index a8ee4d61e65..133d8a88de3 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -192,19 +192,38 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
                         let have_alt_message = message.is_some() || label.is_some();
                         let is_try_conversion = self.is_try_conversion(span, main_trait_predicate.def_id());
+                        let is_question_mark = matches!(
+                            root_obligation.cause.code().peel_derives(),
+                            ObligationCauseCode::QuestionMark,
+                        ) && !(
+                            self.tcx.is_diagnostic_item(sym::FromResidual, main_trait_predicate.def_id())
+                                || self.tcx.is_lang_item(main_trait_predicate.def_id(), LangItem::Try)
+                        );
                         let is_unsize =
                             self.tcx.is_lang_item(leaf_trait_predicate.def_id(), LangItem::Unsize);
+                        let question_mark_message = "the question mark operation (`?`) implicitly \
+                                                     performs a conversion on the error value \
+                                                     using the `From` trait";
                         let (message, notes, append_const_msg) = if is_try_conversion {
+                            // We have a `-> Result<_, E1>` and `gives_E2()?`.
                             (
                                 Some(format!(
                                     "`?` couldn't convert the error to `{}`",
                                     main_trait_predicate.skip_binder().self_ty(),
                                 )),
-                                vec![
-                                    "the question mark operation (`?`) implicitly performs a \
-                                     conversion on the error value using the `From` trait"
-                                        .to_owned(),
-                                ],
+                                vec![question_mark_message.to_owned()],
+                                Some(AppendConstMessage::Default),
+                            )
+                        } else if is_question_mark {
+                            // Similar to the case above, but in this case the conversion is for a
+                            // trait object: `-> Result<_, Box<dyn Error>` and `gives_E()?` when
+                            // `E: Error` isn't met.
+                            (
+                                Some(format!(
+                                    "`?` couldn't convert the error: `{main_trait_predicate}` is \
+                                     not satisfied",
+                                )),
+                                vec![question_mark_message.to_owned()],
                                 Some(AppendConstMessage::Default),
                             )
                         } else {
@@ -220,8 +239,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             &mut long_ty_file,
                         );
 
-                        let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item(main_trait_predicate.def_id(), LangItem::TransmuteTrait)
-                        {
+                        let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item(
+                            main_trait_predicate.def_id(),
+                            LangItem::TransmuteTrait,
+                        ) {
                             // Recompute the safe transmute reason and use that for the error reporting
                             match self.get_safe_transmute_error_and_reason(
                                 obligation.clone(),
@@ -249,18 +270,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         *err.long_ty_path() = long_ty_file;
 
                         let mut suggested = false;
-                        if is_try_conversion {
+                        if is_try_conversion || is_question_mark {
                             suggested = self.try_conversion_context(&obligation, main_trait_predicate, &mut err);
                         }
 
-                        if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) {
-                            err.span_label(
-                                ret_span,
-                                format!(
-                                    "expected `{}` because of this",
-                                    main_trait_predicate.skip_binder().self_ty()
-                                ),
-                            );
+                        if let Some(ret_span) = self.return_type_span(&obligation) {
+                            if is_try_conversion {
+                                err.span_label(
+                                    ret_span,
+                                    format!(
+                                        "expected `{}` because of this",
+                                        main_trait_predicate.skip_binder().self_ty()
+                                    ),
+                                );
+                            } else if is_question_mark {
+                                err.span_label(ret_span, format!("required `{main_trait_predicate}` because of this"));
+                            }
                         }
 
                         if tcx.is_lang_item(leaf_trait_predicate.def_id(), LangItem::Tuple) {
@@ -302,10 +327,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             // If it has a custom `#[rustc_on_unimplemented]`
                             // error message, let's display it as the label!
                             err.span_label(span, s);
-                            if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_)) {
+                            if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_))
                                 // When the self type is a type param We don't need to "the trait
                                 // `std::marker::Sized` is not implemented for `T`" as we will point
                                 // at the type param with a label to suggest constraining it.
+                                && !self.tcx.is_diagnostic_item(sym::FromResidual, leaf_trait_predicate.def_id())
+                                    // Don't say "the trait `FromResidual<Option<Infallible>>` is
+                                    // not implemented for `Result<T, E>`".
+                            {
                                 err.help(explanation);
                             }
                         } else if let Some(custom_explanation) = safe_transmute_explanation {
@@ -2035,6 +2064,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 return false;
             }
             if let &[cand] = &candidates[..] {
+                if self.tcx.is_diagnostic_item(sym::FromResidual, cand.def_id)
+                    && !self.tcx.features().enabled(sym::try_trait_v2)
+                {
+                    return false;
+                }
                 let (desc, mention_castable) =
                     match (cand.self_ty().kind(), trait_pred.self_ty().skip_binder().kind()) {
                         (ty::FnPtr(..), ty::FnDef(..)) => {
diff --git a/tests/ui/async-await/issue-84841.stderr b/tests/ui/async-await/issue-84841.stderr
index 69c1c882d60..0d008477310 100644
--- a/tests/ui/async-await/issue-84841.stderr
+++ b/tests/ui/async-await/issue-84841.stderr
@@ -17,8 +17,6 @@ LL | |     test()?;
 ...  |
 LL | | }
    | |_- this function should return `Result` or `Option` to accept `?`
-   |
-   = help: the trait `FromResidual<_>` is not implemented for `()`
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/async-await/try-on-option-in-async.stderr b/tests/ui/async-await/try-on-option-in-async.stderr
index 9e0bb42a697..332f4d4ec0c 100644
--- a/tests/ui/async-await/try-on-option-in-async.stderr
+++ b/tests/ui/async-await/try-on-option-in-async.stderr
@@ -6,8 +6,6 @@ LL |     async {
 LL |         let x: Option<u32> = None;
 LL |         x?;
    |          ^ cannot use the `?` operator in an async block that returns `{integer}`
-   |
-   = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `{integer}`
 
 error[E0277]: the `?` operator can only be used in an async closure that returns `Result` or `Option` (or another type that implements `FromResidual`)
   --> $DIR/try-on-option-in-async.rs:16:10
@@ -20,8 +18,6 @@ LL | |         x?;
 LL | |         22_u32
 LL | |     };
    | |_____- this function should return `Result` or `Option` to accept `?`
-   |
-   = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `u32`
 
 error[E0277]: the `?` operator can only be used in an async function that returns `Result` or `Option` (or another type that implements `FromResidual`)
   --> $DIR/try-on-option-in-async.rs:25:6
@@ -34,8 +30,6 @@ LL | |     x?;
 LL | |     22
 LL | | }
    | |_- this function should return `Result` or `Option` to accept `?`
-   |
-   = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `u32`
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/return/return-from-residual-sugg-issue-125997.stderr b/tests/ui/return/return-from-residual-sugg-issue-125997.stderr
index e22f33fd242..877995dfe12 100644
--- a/tests/ui/return/return-from-residual-sugg-issue-125997.stderr
+++ b/tests/ui/return/return-from-residual-sugg-issue-125997.stderr
@@ -6,7 +6,6 @@ LL | fn test1() {
 LL |     let mut _file = File::create("foo.txt")?;
    |                                            ^ cannot use the `?` operator in a function that returns `()`
    |
-   = help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
 help: consider adding return type
    |
 LL ~ fn test1() -> Result<(), Box<dyn std::error::Error>> {
@@ -23,7 +22,6 @@ LL | fn test2() {
 LL |     let mut _file = File::create("foo.txt")?;
    |                                            ^ cannot use the `?` operator in a function that returns `()`
    |
-   = help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
 help: consider adding return type
    |
 LL ~ fn test2() -> Result<(), Box<dyn std::error::Error>> {
@@ -41,7 +39,6 @@ LL |     fn test4(&self) {
 LL |         let mut _file = File::create("foo.txt")?;
    |                                                ^ cannot use the `?` operator in a method that returns `()`
    |
-   = help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
 help: consider adding return type
    |
 LL ~     fn test4(&self) -> Result<(), Box<dyn std::error::Error>> {
@@ -59,7 +56,6 @@ LL |     fn test5(&self) {
 LL |         let mut _file = File::create("foo.txt")?;
    |                                                ^ cannot use the `?` operator in a method that returns `()`
    |
-   = help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
 help: consider adding return type
    |
 LL ~     fn test5(&self) -> Result<(), Box<dyn std::error::Error>> {
@@ -78,7 +74,6 @@ LL | fn main() {
 LL |     let mut _file = File::create("foo.txt")?;
    |                                            ^ cannot use the `?` operator in a function that returns `()`
    |
-   = help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
 help: consider adding return type
    |
 LL ~ fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -99,7 +94,6 @@ LL |             let mut _file = File::create("foo.txt")?;
 LL |     mac!();
    |     ------ in this macro invocation
    |
-   = help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
    = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info)
 help: consider adding return type
    |
diff --git a/tests/ui/try-trait/bad-interconversion.stderr b/tests/ui/try-trait/bad-interconversion.stderr
index 45422be946e..6df05747f32 100644
--- a/tests/ui/try-trait/bad-interconversion.stderr
+++ b/tests/ui/try-trait/bad-interconversion.stderr
@@ -20,9 +20,6 @@ LL | fn option_to_result() -> Result<u64, String> {
    | -------------------------------------------- this function returns a `Result`
 LL |     Some(3)?;
    |            ^ use `.ok_or(...)?` to provide an error compatible with `Result<u64, String>`
-   |
-   = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<u64, String>`
-   = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
 
 error[E0277]: the `?` operator can only be used on `Result`s in a function that returns `Result`
   --> $DIR/bad-interconversion.rs:15:31
@@ -31,9 +28,6 @@ LL | fn control_flow_to_result() -> Result<u64, String> {
    | -------------------------------------------------- this function returns a `Result`
 LL |     Ok(ControlFlow::Break(123)?)
    |                               ^ this `?` produces `ControlFlow<{integer}, Infallible>`, which is incompatible with `Result<u64, String>`
-   |
-   = help: the trait `FromResidual<ControlFlow<{integer}, Infallible>>` is not implemented for `Result<u64, String>`
-   = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
 
 error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option`
   --> $DIR/bad-interconversion.rs:20:22
@@ -42,9 +36,6 @@ LL | fn result_to_option() -> Option<u16> {
    | ------------------------------------ this function returns an `Option`
 LL |     Some(Err("hello")?)
    |                      ^ use `.ok()?` if you want to discard the `Result<Infallible, &str>` error information
-   |
-   = help: the trait `FromResidual<Result<Infallible, &str>>` is not implemented for `Option<u16>`
-   = help: the trait `FromResidual<Option<Infallible>>` is implemented for `Option<T>`
 
 error[E0277]: the `?` operator can only be used on `Option`s in a function that returns `Option`
   --> $DIR/bad-interconversion.rs:25:33
@@ -53,9 +44,6 @@ LL | fn control_flow_to_option() -> Option<u64> {
    | ------------------------------------------ this function returns an `Option`
 LL |     Some(ControlFlow::Break(123)?)
    |                                 ^ this `?` produces `ControlFlow<{integer}, Infallible>`, which is incompatible with `Option<u64>`
-   |
-   = help: the trait `FromResidual<ControlFlow<{integer}, Infallible>>` is not implemented for `Option<u64>`
-   = help: the trait `FromResidual<Option<Infallible>>` is implemented for `Option<T>`
 
 error[E0277]: the `?` operator can only be used on `ControlFlow`s in a function that returns `ControlFlow`
   --> $DIR/bad-interconversion.rs:30:39
diff --git a/tests/ui/try-trait/bad-question-mark-on-trait-object.rs b/tests/ui/try-trait/bad-question-mark-on-trait-object.rs
new file mode 100644
index 00000000000..f319610cb3a
--- /dev/null
+++ b/tests/ui/try-trait/bad-question-mark-on-trait-object.rs
@@ -0,0 +1,26 @@
+struct E;
+struct X;
+
+fn foo() -> Result<(), Box<dyn std::error::Error>> { //~ NOTE required `E: std::error::Error` because of this
+    Ok(bar()?)
+    //~^ ERROR `?` couldn't convert the error: `E: std::error::Error` is not satisfied
+    //~| NOTE the trait `std::error::Error` is not implemented for `E`
+    //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
+    //~| NOTE required for `Box<dyn std::error::Error>` to implement `From<E>`
+    //~| NOTE in this expansion
+    //~| NOTE in this expansion
+    //~| NOTE in this expansion
+}
+fn bat() -> Result<(), X> { //~ NOTE expected `X` because of this
+    Ok(bar()?)
+    //~^ ERROR `?` couldn't convert the error to `X`
+    //~| NOTE the trait `From<E>` is not implemented for `X`
+    //~| NOTE this can't be annotated with `?` because it has type `Result<_, E>`
+    //~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
+    //~| NOTE in this expansion
+    //~| NOTE in this expansion
+}
+fn bar() -> Result<(), E> {
+    Err(E)
+}
+fn main() {}
diff --git a/tests/ui/try-trait/bad-question-mark-on-trait-object.stderr b/tests/ui/try-trait/bad-question-mark-on-trait-object.stderr
new file mode 100644
index 00000000000..4e7cd1e2889
--- /dev/null
+++ b/tests/ui/try-trait/bad-question-mark-on-trait-object.stderr
@@ -0,0 +1,26 @@
+error[E0277]: `?` couldn't convert the error: `E: std::error::Error` is not satisfied
+  --> $DIR/bad-question-mark-on-trait-object.rs:5:13
+   |
+LL | fn foo() -> Result<(), Box<dyn std::error::Error>> {
+   |             -------------------------------------- required `E: std::error::Error` because of this
+LL |     Ok(bar()?)
+   |             ^ the trait `std::error::Error` is not implemented for `E`
+   |
+   = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
+   = note: required for `Box<dyn std::error::Error>` to implement `From<E>`
+
+error[E0277]: `?` couldn't convert the error to `X`
+  --> $DIR/bad-question-mark-on-trait-object.rs:15:13
+   |
+LL | fn bat() -> Result<(), X> {
+   |             ------------- expected `X` because of this
+LL |     Ok(bar()?)
+   |        -----^ the trait `From<E>` is not implemented for `X`
+   |        |
+   |        this can't be annotated with `?` because it has type `Result<_, E>`
+   |
+   = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/try-trait/option-to-result.stderr b/tests/ui/try-trait/option-to-result.stderr
index 1a5a925f92f..8a4c4707942 100644
--- a/tests/ui/try-trait/option-to-result.stderr
+++ b/tests/ui/try-trait/option-to-result.stderr
@@ -6,9 +6,6 @@ LL | fn test_result() -> Result<(),()> {
 LL |     let a:Option<()> = Some(());
 LL |     a?;
    |      ^ use `.ok_or(...)?` to provide an error compatible with `Result<(), ()>`
-   |
-   = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<(), ()>`
-   = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
 
 error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option`
   --> $DIR/option-to-result.rs:11:6
@@ -18,9 +15,6 @@ LL | fn test_option() -> Option<i32>{
 LL |     let a:Result<i32, i32> = Ok(5);
 LL |     a?;
    |      ^ use `.ok()?` if you want to discard the `Result<Infallible, i32>` error information
-   |
-   = help: the trait `FromResidual<Result<Infallible, i32>>` is not implemented for `Option<i32>`
-   = help: the trait `FromResidual<Option<Infallible>>` is implemented for `Option<T>`
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/try-trait/try-on-option-diagnostics.stderr b/tests/ui/try-trait/try-on-option-diagnostics.stderr
index 9ee540c79fd..08675e242a3 100644
--- a/tests/ui/try-trait/try-on-option-diagnostics.stderr
+++ b/tests/ui/try-trait/try-on-option-diagnostics.stderr
@@ -6,8 +6,6 @@ LL | fn a_function() -> u32 {
 LL |     let x: Option<u32> = None;
 LL |     x?;
    |      ^ cannot use the `?` operator in a function that returns `u32`
-   |
-   = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `u32`
 
 error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `FromResidual`)
   --> $DIR/try-on-option-diagnostics.rs:14:10
@@ -17,8 +15,6 @@ LL |     let a_closure = || {
 LL |         let x: Option<u32> = None;
 LL |         x?;
    |          ^ cannot use the `?` operator in a closure that returns `{integer}`
-   |
-   = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `{integer}`
 
 error[E0277]: the `?` operator can only be used in a method that returns `Result` or `Option` (or another type that implements `FromResidual`)
   --> $DIR/try-on-option-diagnostics.rs:26:14
@@ -28,8 +24,6 @@ LL |         fn a_method() {
 LL |             let x: Option<u32> = None;
 LL |             x?;
    |              ^ cannot use the `?` operator in a method that returns `()`
-   |
-   = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `()`
 
 error[E0277]: the `?` operator can only be used in a trait method that returns `Result` or `Option` (or another type that implements `FromResidual`)
   --> $DIR/try-on-option-diagnostics.rs:39:14
@@ -39,8 +33,6 @@ LL |         fn a_trait_method() {
 LL |             let x: Option<u32> = None;
 LL |             x?;
    |              ^ cannot use the `?` operator in a trait method that returns `()`
-   |
-   = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `()`
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/try-trait/try-on-option.stderr b/tests/ui/try-trait/try-on-option.stderr
index 15d0b28ddc1..aeb519086d8 100644
--- a/tests/ui/try-trait/try-on-option.stderr
+++ b/tests/ui/try-trait/try-on-option.stderr
@@ -6,9 +6,6 @@ LL | fn foo() -> Result<u32, ()> {
 LL |     let x: Option<u32> = None;
 LL |     x?;
    |      ^ use `.ok_or(...)?` to provide an error compatible with `Result<u32, ()>`
-   |
-   = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<u32, ()>`
-   = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
 
 error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
   --> $DIR/try-on-option.rs:11:6
@@ -18,8 +15,6 @@ LL | fn bar() -> u32 {
 LL |     let x: Option<u32> = None;
 LL |     x?;
    |      ^ cannot use the `?` operator in a function that returns `u32`
-   |
-   = help: the trait `FromResidual<Option<Infallible>>` is not implemented for `u32`
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/try-trait/try-operator-on-main.stderr b/tests/ui/try-trait/try-operator-on-main.stderr
index 311e8076ed4..9c2526442ab 100644
--- a/tests/ui/try-trait/try-operator-on-main.stderr
+++ b/tests/ui/try-trait/try-operator-on-main.stderr
@@ -7,7 +7,6 @@ LL |     // error for a `Try` type on a non-`Try` fn
 LL |     std::fs::File::open("foo")?;
    |                               ^ cannot use the `?` operator in a function that returns `()`
    |
-   = help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
 help: consider adding return type
    |
 LL ~ fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -33,8 +32,6 @@ LL | fn main() {
 ...
 LL |     ()?;
    |       ^ cannot use the `?` operator in a function that returns `()`
-   |
-   = help: the trait `FromResidual<_>` is not implemented for `()`
 
 error[E0277]: the trait bound `(): Try` is not satisfied
   --> $DIR/try-operator-on-main.rs:14:25