about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2023-05-30 18:47:50 +0000
committerMichael Goulet <michael@errs.io>2023-06-08 16:30:05 +0000
commitaf54d584b29e6afd7069bfdad071c43c0aa135f5 (patch)
tree144ab1a99ce868ec48ca7d5b3e5990d12c28c3cb
parent522ae84e0344669ee64754595dd5f04eed9f0d1c (diff)
downloadrust-af54d584b29e6afd7069bfdad071c43c0aa135f5.tar.gz
rust-af54d584b29e6afd7069bfdad071c43c0aa135f5.zip
More robust as_ref/as_deref suggestions
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl2
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs15
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs221
-rw-r--r--compiler/rustc_infer/messages.ftl3
-rw-r--r--compiler/rustc_infer/src/errors/mod.rs24
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs1
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/suggest.rs27
-rw-r--r--tests/ui/issues/issue-100605.stderr14
-rw-r--r--tests/ui/let-else/let-else-ref-bindings.stderr16
-rw-r--r--tests/ui/suggestions/as-ref.rs2
-rw-r--r--tests/ui/suggestions/as-ref.stderr32
-rw-r--r--tests/ui/typeck/issue-89856.stderr2
12 files changed, 207 insertions, 152 deletions
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index aab432eee57..4728edd837a 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -25,6 +25,8 @@ hir_typeck_const_select_must_be_fn = this argument must be a function item
 
 hir_typeck_convert_to_str = try converting the passed type into a `&str`
 
+hir_typeck_convert_using_method = try using `{$sugg}` to convert `{$found}` to `{$expected}`
+
 hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private
 
 hir_typeck_expected_default_return_type = expected `()` because of default return type
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 4222205c841..20116fde661 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -327,3 +327,18 @@ pub struct CtorIsPrivate {
     pub span: Span,
     pub def: String,
 }
+
+#[derive(Subdiagnostic)]
+#[suggestion(
+    hir_typeck_convert_using_method,
+    code = "{sugg}",
+    applicability = "machine-applicable",
+    style = "verbose"
+)]
+pub struct SuggestConvertViaMethod<'tcx> {
+    #[primary_span]
+    pub span: Span,
+    pub sugg: &'static str,
+    pub expected: Ty<'tcx>,
+    pub found: Ty<'tcx>,
+}
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index e19b0664461..21a52d3eccc 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -1,6 +1,8 @@
 use super::FnCtxt;
 
-use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing};
+use crate::errors::{
+    AddReturnTypeSuggestion, ExpectedReturnTypeLabel, SuggestBoxing, SuggestConvertViaMethod,
+};
 use crate::fluent_generated as fluent;
 use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
 use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
@@ -275,6 +277,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
     ) -> bool {
         let expr = expr.peel_blocks();
+        let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
+
         if let Some((suggestion, msg, applicability, verbose, annotation)) =
             self.suggest_deref_or_ref(expr, found, expected)
         {
@@ -325,9 +329,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
             }
             return true;
-        } else if self.suggest_else_fn_with_closure(err, expr, found, expected) {
+        }
+
+        if self.suggest_else_fn_with_closure(err, expr, found, expected) {
             return true;
-        } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
+        }
+
+        if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
             && let ty::FnDef(def_id, ..) = *found.kind()
             && let Some(sp) = self.tcx.hir().span_if_local(def_id)
         {
@@ -343,97 +351,142 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 err.span_label(sp, format!("{descr} `{name}` defined here"));
             }
             return true;
-        } else if self.suggest_cast(err, expr, found, expected, expected_ty_expr) {
+        }
+
+        if self.suggest_cast(err, expr, found, expected, expected_ty_expr) {
             return true;
-        } else {
-            let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
-            if !methods.is_empty() {
-                let mut suggestions = methods.iter()
-                    .filter_map(|conversion_method| {
-                        let receiver_method_ident = expr.method_ident();
-                        if let Some(method_ident) = receiver_method_ident
-                            && method_ident.name == conversion_method.name
-                        {
-                            return None // do not suggest code that is already there (#53348)
-                        }
+        }
 
-                        let method_call_list = [sym::to_vec, sym::to_string];
-                        let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind
-                            && receiver_method.ident.name == sym::clone
-                            && method_call_list.contains(&conversion_method.name)
-                            // If receiver is `.clone()` and found type has one of those methods,
-                            // we guess that the user wants to convert from a slice type (`&[]` or `&str`)
-                            // to an owned type (`Vec` or `String`). These conversions clone internally,
-                            // so we remove the user's `clone` call.
-                        {
-                            vec![(
-                                receiver_method.ident.span,
-                                conversion_method.name.to_string()
-                            )]
-                        } else if expr.precedence().order()
-                            < ExprPrecedence::MethodCall.order()
-                        {
-                            vec![
-                                (expr.span.shrink_to_lo(), "(".to_string()),
-                                (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)),
-                            ]
-                        } else {
-                            vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))]
-                        };
-                        let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr);
-                        if let Some(name) = struct_pat_shorthand_field {
-                            sugg.insert(
-                                0,
-                                (expr.span.shrink_to_lo(), format!("{}: ", name)),
-                            );
-                        }
-                        Some(sugg)
-                    })
-                    .peekable();
-                if suggestions.peek().is_some() {
-                    err.multipart_suggestions(
-                        "try using a conversion method",
-                        suggestions,
-                        Applicability::MaybeIncorrect,
-                    );
-                    return true;
-                }
-            } else if let ty::Adt(found_adt, found_substs) = found.kind()
-                && self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
-                && let ty::Adt(expected_adt, expected_substs) = expected.kind()
-                && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did())
-                && let ty::Ref(_, inner_ty, _) = expected_substs.type_at(0).kind()
-                && inner_ty.is_str()
-            {
-                let ty = found_substs.type_at(0);
-                let mut peeled = ty;
-                let mut ref_cnt = 0;
-                while let ty::Ref(_, inner, _) = peeled.kind() {
-                    peeled = *inner;
-                    ref_cnt += 1;
-                }
-                if let ty::Adt(adt, _) = peeled.kind()
-                    && Some(adt.did()) == self.tcx.lang_items().string()
-                {
-                    let sugg = if ref_cnt == 0 {
-                        ".as_deref()"
+        if !methods.is_empty() {
+            let mut suggestions = methods
+                .iter()
+                .filter_map(|conversion_method| {
+                    let receiver_method_ident = expr.method_ident();
+                    if let Some(method_ident) = receiver_method_ident
+                        && method_ident.name == conversion_method.name
+                    {
+                        return None // do not suggest code that is already there (#53348)
+                    }
+
+                    let method_call_list = [sym::to_vec, sym::to_string];
+                    let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind
+                        && receiver_method.ident.name == sym::clone
+                        && method_call_list.contains(&conversion_method.name)
+                        // If receiver is `.clone()` and found type has one of those methods,
+                        // we guess that the user wants to convert from a slice type (`&[]` or `&str`)
+                        // to an owned type (`Vec` or `String`). These conversions clone internally,
+                        // so we remove the user's `clone` call.
+                    {
+                        vec![(
+                            receiver_method.ident.span,
+                            conversion_method.name.to_string()
+                        )]
+                    } else if expr.precedence().order()
+                        < ExprPrecedence::MethodCall.order()
+                    {
+                        vec![
+                            (expr.span.shrink_to_lo(), "(".to_string()),
+                            (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)),
+                        ]
                     } else {
-                        ".map(|x| x.as_str())"
+                        vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))]
                     };
-                    err.span_suggestion_verbose(
-                        expr.span.shrink_to_hi(),
-                        fluent::hir_typeck_convert_to_str,
-                        sugg,
-                        Applicability::MachineApplicable,
-                    );
-                    return true;
-                }
+                    let struct_pat_shorthand_field =
+                        self.maybe_get_struct_pattern_shorthand_field(expr);
+                    if let Some(name) = struct_pat_shorthand_field {
+                        sugg.insert(0, (expr.span.shrink_to_lo(), format!("{}: ", name)));
+                    }
+                    Some(sugg)
+                })
+                .peekable();
+            if suggestions.peek().is_some() {
+                err.multipart_suggestions(
+                    "try using a conversion method",
+                    suggestions,
+                    Applicability::MaybeIncorrect,
+                );
+                return true;
+            }
+        }
+
+        if let Some((found_ty_inner, expected_ty_inner, error_tys)) =
+            self.deconstruct_option_or_result(found, expected)
+            && let ty::Ref(_, peeled, hir::Mutability::Not) = *expected_ty_inner.kind()
+        {
+            // Check that given `Result<_, E>`, our expected ty is `Result<_, &E>`
+            let error_tys_equate_as_ref = error_tys.map_or(true, |(found, expected)| {
+                self.can_eq(self.param_env, self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, found), expected)
+            });
+            // FIXME: This could/should be extended to suggest `as_mut` and `as_deref_mut`,
+            // but those checks need to be a bit more delicate and the benefit is diminishing.
+            if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref {
+                err.subdiagnostic(SuggestConvertViaMethod {
+                    span: expr.span.shrink_to_hi(),
+                    sugg: ".as_ref()",
+                    expected,
+                    found,
+                });
+                return true;
+            } else if let Some((deref_ty, _)) =
+                self.autoderef(expr.span, found_ty_inner).silence_errors().nth(1)
+                && self.can_eq(self.param_env, deref_ty, peeled)
+                && error_tys_equate_as_ref
+            {
+                err.subdiagnostic(SuggestConvertViaMethod {
+                    span: expr.span.shrink_to_hi(),
+                    sugg: ".as_deref()",
+                    expected,
+                    found,
+                });
+                return true;
+            } else if let ty::Adt(adt, _) = found_ty_inner.peel_refs().kind()
+                && Some(adt.did()) == self.tcx.lang_items().string()
+                && peeled.is_str()
+                && error_tys.map_or(true, |(found, expected)| {
+                    self.can_eq(self.param_env, found, expected)
+                })
+            {
+                err.span_suggestion_verbose(
+                    expr.span.shrink_to_hi(),
+                    fluent::hir_typeck_convert_to_str,
+                    ".map(|x| x.as_str())",
+                    Applicability::MachineApplicable,
+                );
+                return true;
             }
         }
 
         false
     }
 
+    fn deconstruct_option_or_result(
+        &self,
+        found_ty: Ty<'tcx>,
+        expected_ty: Ty<'tcx>,
+    ) -> Option<(Ty<'tcx>, Ty<'tcx>, Option<(Ty<'tcx>, Ty<'tcx>)>)> {
+        let ty::Adt(found_adt, found_substs) = found_ty.peel_refs().kind() else {
+            return None;
+        };
+        let ty::Adt(expected_adt, expected_substs) = expected_ty.kind() else {
+            return None;
+        };
+        if self.tcx.is_diagnostic_item(sym::Option, found_adt.did())
+            && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did())
+        {
+            Some((found_substs.type_at(0), expected_substs.type_at(0), None))
+        } else if self.tcx.is_diagnostic_item(sym::Result, found_adt.did())
+            && self.tcx.is_diagnostic_item(sym::Result, expected_adt.did())
+        {
+            Some((
+                found_substs.type_at(0),
+                expected_substs.type_at(0),
+                Some((found_substs.type_at(1), expected_substs.type_at(1))),
+            ))
+        } else {
+            None
+        }
+    }
+
     /// When encountering the expected boxed value allocated in the stack, suggest allocating it
     /// in the heap by calling `Box::new()`.
     pub(in super::super) fn suggest_boxing_when_appropriate(
diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl
index f44c4a7c1e3..4d0e7706367 100644
--- a/compiler/rustc_infer/messages.ftl
+++ b/compiler/rustc_infer/messages.ftl
@@ -278,9 +278,6 @@ infer_ril_introduced_by = requirement introduced by this return type
 infer_ril_introduced_here = `'static` requirement introduced here
 infer_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type
 
-infer_sarwa_option = you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`
-infer_sarwa_result = you can convert from `&Result<T, E>` to `Result<&T, &E>` using `.as_ref()`
-
 infer_sbfrit_box_return_expr = if you change the return type to expect trait objects, box the returned expressions
 
 infer_sbfrit_change_return_type = you could change the return type to be a boxed trait object
diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs
index b1e819e83f1..7e1fa08f23a 100644
--- a/compiler/rustc_infer/src/errors/mod.rs
+++ b/compiler/rustc_infer/src/errors/mod.rs
@@ -1247,30 +1247,6 @@ pub struct FnConsiderCasting {
 }
 
 #[derive(Subdiagnostic)]
-pub enum SuggestAsRefWhereAppropriate<'a> {
-    #[suggestion(
-        infer_sarwa_option,
-        code = "{snippet}.as_ref()",
-        applicability = "machine-applicable"
-    )]
-    Option {
-        #[primary_span]
-        span: Span,
-        snippet: &'a str,
-    },
-    #[suggestion(
-        infer_sarwa_result,
-        code = "{snippet}.as_ref()",
-        applicability = "machine-applicable"
-    )]
-    Result {
-        #[primary_span]
-        span: Span,
-        snippet: &'a str,
-    },
-}
-
-#[derive(Subdiagnostic)]
 pub enum SuggestAccessingField<'a> {
     #[suggestion(
         infer_suggest_accessing_field,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index f8a253c8949..ce5b5882e3b 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -1897,7 +1897,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
             if should_suggest_fixes {
                 self.suggest_tuple_pattern(cause, &exp_found, diag);
-                self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
                 self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
                 self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
                 self.suggest_function_pointers(cause, span, &exp_found, diag);
diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
index d885d040707..1422bdc9ea2 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
@@ -13,9 +13,9 @@ use rustc_span::{sym, BytePos, Span};
 
 use crate::errors::{
     ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
-    FunctionPointerSuggestion, SuggestAccessingField, SuggestAsRefWhereAppropriate,
-    SuggestBoxingForReturnImplTrait, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany,
-    SuggestTuplePatternOne, TypeErrorAdditionalDiags,
+    FunctionPointerSuggestion, SuggestAccessingField, SuggestBoxingForReturnImplTrait,
+    SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, SuggestTuplePatternOne,
+    TypeErrorAdditionalDiags,
 };
 
 use super::TypeErrCtxt;
@@ -289,27 +289,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
     }
 
-    /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
-    /// suggests it.
-    pub(super) fn suggest_as_ref_where_appropriate(
-        &self,
-        span: Span,
-        exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
-        diag: &mut Diagnostic,
-    ) {
-        if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
-            && let Some(msg) = self.should_suggest_as_ref_kind(exp_found.expected, exp_found.found)
-        {
-            // HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
-            let snippet = snippet.trim_start_matches('&');
-            let subdiag = match msg {
-                SuggestAsRefKind::Option => SuggestAsRefWhereAppropriate::Option { span, snippet },
-                SuggestAsRefKind::Result => SuggestAsRefWhereAppropriate::Result { span, snippet },
-            };
-            diag.subdiagnostic(subdiag);
-        }
-    }
-
     pub(super) fn suggest_function_pointers(
         &self,
         cause: &ObligationCause<'tcx>,
diff --git a/tests/ui/issues/issue-100605.stderr b/tests/ui/issues/issue-100605.stderr
index be30eef2af4..e4fc5cb367c 100644
--- a/tests/ui/issues/issue-100605.stderr
+++ b/tests/ui/issues/issue-100605.stderr
@@ -13,10 +13,6 @@ note: function defined here
    |
 LL | fn takes_option(_arg: Option<&String>) {}
    |    ^^^^^^^^^^^^ ---------------------
-help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`
-   |
-LL |     takes_option(None.as_ref());
-   |                  ~~~~~~~~~~~~~
 help: consider removing the borrow
    |
 LL -     takes_option(&None);
@@ -27,10 +23,8 @@ error[E0308]: mismatched types
   --> $DIR/issue-100605.rs:8:18
    |
 LL |     takes_option(&res);
-   |     ------------ ^^^^
-   |     |            |
-   |     |            expected `Option<&String>`, found `&Option<String>`
-   |     |            help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `res.as_ref()`
+   |     ------------ ^^^^ expected `Option<&String>`, found `&Option<String>`
+   |     |
    |     arguments to this function are incorrect
    |
    = note:   expected enum `Option<&String>`
@@ -40,6 +34,10 @@ note: function defined here
    |
 LL | fn takes_option(_arg: Option<&String>) {}
    |    ^^^^^^^^^^^^ ---------------------
+help: try using `.as_ref()` to convert `&Option<String>` to `Option<&String>`
+   |
+LL |     takes_option(&res.as_ref());
+   |                      +++++++++
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/let-else/let-else-ref-bindings.stderr b/tests/ui/let-else/let-else-ref-bindings.stderr
index ada1805e725..7093b5fa9af 100644
--- a/tests/ui/let-else/let-else-ref-bindings.stderr
+++ b/tests/ui/let-else/let-else-ref-bindings.stderr
@@ -6,6 +6,10 @@ LL |     let Some(ref a): Option<&[u8]> = some else { return };
    |
    = note: expected enum `Option<&[u8]>`
               found enum `Option<Vec<u8>>`
+help: try using `.as_deref()` to convert `Option<Vec<u8>>` to `Option<&[u8]>`
+   |
+LL |     let Some(ref a): Option<&[u8]> = some.as_deref() else { return };
+   |                                          +++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/let-else-ref-bindings.rs:20:38
@@ -15,6 +19,10 @@ LL |     let Some(ref a): Option<&[u8]> = &some else { return };
    |
    = note:   expected enum `Option<&[u8]>`
            found reference `&Option<Vec<u8>>`
+help: try using `.as_deref()` to convert `&Option<Vec<u8>>` to `Option<&[u8]>`
+   |
+LL |     let Some(ref a): Option<&[u8]> = &some.as_deref() else { return };
+   |                                           +++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/let-else-ref-bindings.rs:24:34
@@ -26,6 +34,10 @@ LL |     let Some(a): Option<&[u8]> = some else { return };
    |
    = note: expected enum `Option<&[u8]>`
               found enum `Option<Vec<u8>>`
+help: try using `.as_deref()` to convert `Option<Vec<u8>>` to `Option<&[u8]>`
+   |
+LL |     let Some(a): Option<&[u8]> = some.as_deref() else { return };
+   |                                      +++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/let-else-ref-bindings.rs:27:34
@@ -37,6 +49,10 @@ LL |     let Some(a): Option<&[u8]> = &some else { return };
    |
    = note:   expected enum `Option<&[u8]>`
            found reference `&Option<Vec<u8>>`
+help: try using `.as_deref()` to convert `&Option<Vec<u8>>` to `Option<&[u8]>`
+   |
+LL |     let Some(a): Option<&[u8]> = &some.as_deref() else { return };
+   |                                       +++++++++++
 
 error[E0308]: mismatched types
   --> $DIR/let-else-ref-bindings.rs:44:46
diff --git a/tests/ui/suggestions/as-ref.rs b/tests/ui/suggestions/as-ref.rs
index a0535344185..0d9790ac229 100644
--- a/tests/ui/suggestions/as-ref.rs
+++ b/tests/ui/suggestions/as-ref.rs
@@ -24,4 +24,6 @@ fn main() {
     let multiple_ref_result = &&Ok(Foo);
     multiple_ref_result.map(|arg| takes_ref(arg)); //~ ERROR mismatched types [E0308]
     multiple_ref_result.and_then(|arg| Ok(takes_ref(arg))); //~ ERROR mismatched types [E0308]
+
+    let _: Result<&usize, _> = &Ok(42); //~ ERROR mismatched types [E0308]
 }
diff --git a/tests/ui/suggestions/as-ref.stderr b/tests/ui/suggestions/as-ref.stderr
index 2147d2d92e3..c5b2bb1260f 100644
--- a/tests/ui/suggestions/as-ref.stderr
+++ b/tests/ui/suggestions/as-ref.stderr
@@ -74,14 +74,16 @@ error[E0308]: mismatched types
   --> $DIR/as-ref.rs:13:29
    |
 LL |     let y: Option<&usize> = x;
-   |            --------------   ^
-   |            |                |
-   |            |                expected `Option<&usize>`, found `&Option<usize>`
-   |            |                help: you can convert from `&Option<T>` to `Option<&T>` using `.as_ref()`: `x.as_ref()`
+   |            --------------   ^ expected `Option<&usize>`, found `&Option<usize>`
+   |            |
    |            expected due to this
    |
    = note:   expected enum `Option<&usize>`
            found reference `&Option<usize>`
+help: try using `.as_ref()` to convert `&Option<usize>` to `Option<&usize>`
+   |
+LL |     let y: Option<&usize> = x.as_ref();
+   |                              +++++++++
 
 error[E0308]: mismatched types
   --> $DIR/as-ref.rs:15:37
@@ -93,10 +95,10 @@ LL |     let y: Result<&usize, &usize> = x;
    |
    = note:   expected enum `Result<&usize, &usize>`
            found reference `&Result<usize, usize>`
-help: you can convert from `&Result<T, E>` to `Result<&T, &E>` using `.as_ref()`
+help: try using `.as_ref()` to convert `&Result<usize, usize>` to `Result<&usize, &usize>`
    |
 LL |     let y: Result<&usize, &usize> = x.as_ref();
-   |                                     ~~~~~~~~~~
+   |                                      +++++++++
 
 error[E0308]: mismatched types
   --> $DIR/as-ref.rs:19:36
@@ -181,6 +183,22 @@ help: consider using `as_ref` instead
 LL |     multiple_ref_result.as_ref().and_then(|arg| Ok(takes_ref(arg)));
    |                         +++++++++
 
-error: aborting due to 11 previous errors
+error[E0308]: mismatched types
+  --> $DIR/as-ref.rs:28:32
+   |
+LL |     let _: Result<&usize, _> = &Ok(42);
+   |            -----------------   ^^^^^^^ expected `Result<&usize, _>`, found `&Result<{integer}, _>`
+   |            |
+   |            expected due to this
+   |
+   = note:   expected enum `Result<&usize, _>`
+           found reference `&Result<{integer}, _>`
+help: try using `.as_ref()` to convert `&Result<{integer}, _>` to `Result<&usize, _>`
+   |
+LL -     let _: Result<&usize, _> = &Ok(42);
+LL +     let _: Result<&usize, _> = Ok(42).as_ref();
+   |
+
+error: aborting due to 12 previous errors
 
 For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/typeck/issue-89856.stderr b/tests/ui/typeck/issue-89856.stderr
index bd76f172468..0db3e67ede0 100644
--- a/tests/ui/typeck/issue-89856.stderr
+++ b/tests/ui/typeck/issue-89856.stderr
@@ -13,7 +13,7 @@ note: function defined here
    |
 LL | fn take_str_maybe(_: Option<&str>) { }
    |    ^^^^^^^^^^^^^^ ---------------
-help: try converting the passed type into a `&str`
+help: try using `.as_deref()` to convert `Option<String>` to `Option<&str>`
    |
 LL |     take_str_maybe(option.as_deref());
    |                          +++++++++++