From f874f6768caa8b59b99feffc708e868328129fd5 Mon Sep 17 00:00:00 2001
From: clubby789 <jamie@hill-daniel.co.uk>
Date: Fri, 3 Feb 2023 11:10:24 +0000
Subject: [PATCH] Fix suggestion for coercing Option<&String> to Option<&str>

---
 .../locales/en-US/hir_typeck.ftl              |  2 +
 .../src/fn_ctxt/suggestions.rs                | 13 +++--
 tests/ui/typeck/issue-89856.fixed             | 18 +++++++
 tests/ui/typeck/issue-89856.rs                | 14 ++++-
 tests/ui/typeck/issue-89856.stderr            | 54 ++++++++++++++++---
 5 files changed, 88 insertions(+), 13 deletions(-)
 create mode 100644 tests/ui/typeck/issue-89856.fixed

diff --git a/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl b/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl
index 0f13d29d0fc..05ac8db0db8 100644
--- a/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/hir_typeck.ftl
@@ -61,3 +61,5 @@ hir_typeck_lang_start_incorrect_ret_ty = the return type of the `start` lang ite
 hir_typeck_help_set_edition_cargo = set `edition = "{$edition}"` in `Cargo.toml`
 hir_typeck_help_set_edition_standalone = pass `--edition {$edition}` to `rustc`
 hir_typeck_note_edition_guide = for more on editions, read https://doc.rust-lang.org/edition-guide
+
+hir_typeck_convert_to_str = try converting the passed type into a `&str`
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 6046e55c65c..3f433a0928c 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -3,7 +3,7 @@ use super::FnCtxt;
 use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
 use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
 use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
-use rustc_errors::{Applicability, Diagnostic, MultiSpan};
+use rustc_errors::{fluent, Applicability, Diagnostic, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind};
 use rustc_hir::lang_items::LangItem;
@@ -414,11 +414,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 if let ty::Adt(adt, _) = peeled.kind()
                     && Some(adt.did()) == self.tcx.lang_items().string()
                 {
+                    let sugg = if ref_cnt == 0 {
+                        ".as_deref()"
+                    } else {
+                        ".map(|x| x.as_str())"
+                    };
                     err.span_suggestion_verbose(
                         expr.span.shrink_to_hi(),
-                        "try converting the passed type into a `&str`",
-                        format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)),
-                        Applicability::MaybeIncorrect,
+                        fluent::hir_typeck_convert_to_str,
+                        sugg,
+                        Applicability::MachineApplicable,
                     );
                     return true;
                 }
diff --git a/tests/ui/typeck/issue-89856.fixed b/tests/ui/typeck/issue-89856.fixed
new file mode 100644
index 00000000000..3e1a006efa0
--- /dev/null
+++ b/tests/ui/typeck/issue-89856.fixed
@@ -0,0 +1,18 @@
+// run-rustfix
+
+fn take_str_maybe(_: Option<&str>) { }
+fn main() {
+    let string = String::from("Hello, world");
+
+    let option: Option<String> = Some(string.clone());
+    take_str_maybe(option.as_deref());
+    //~^ ERROR: mismatched types [E0308]
+
+    let option_ref = Some(&string);
+    take_str_maybe(option_ref.map(|x| x.as_str()));
+    //~^ ERROR: mismatched types [E0308]
+
+    let option_ref_ref = option_ref.as_ref();
+    take_str_maybe(option_ref_ref.map(|x| x.as_str()));
+    //~^ ERROR: mismatched types [E0308]
+}
diff --git a/tests/ui/typeck/issue-89856.rs b/tests/ui/typeck/issue-89856.rs
index b021e349e35..cfe6e19b303 100644
--- a/tests/ui/typeck/issue-89856.rs
+++ b/tests/ui/typeck/issue-89856.rs
@@ -1,8 +1,18 @@
-fn take_str_maybe(x: Option<&str>) -> Option<&str> { None }
+// run-rustfix
 
+fn take_str_maybe(_: Option<&str>) { }
 fn main() {
     let string = String::from("Hello, world");
-    let option = Some(&string);
+
+    let option: Option<String> = Some(string.clone());
     take_str_maybe(option);
     //~^ ERROR: mismatched types [E0308]
+
+    let option_ref = Some(&string);
+    take_str_maybe(option_ref);
+    //~^ ERROR: mismatched types [E0308]
+
+    let option_ref_ref = option_ref.as_ref();
+    take_str_maybe(option_ref_ref);
+    //~^ ERROR: mismatched types [E0308]
 }
diff --git a/tests/ui/typeck/issue-89856.stderr b/tests/ui/typeck/issue-89856.stderr
index 6b9cbe52c25..bd76f172468 100644
--- a/tests/ui/typeck/issue-89856.stderr
+++ b/tests/ui/typeck/issue-89856.stderr
@@ -1,23 +1,63 @@
 error[E0308]: mismatched types
-  --> $DIR/issue-89856.rs:6:20
+  --> $DIR/issue-89856.rs:8:20
    |
 LL |     take_str_maybe(option);
-   |     -------------- ^^^^^^ expected `Option<&str>`, found `Option<&String>`
+   |     -------------- ^^^^^^ expected `Option<&str>`, found `Option<String>`
+   |     |
+   |     arguments to this function are incorrect
+   |
+   = note: expected enum `Option<&str>`
+              found enum `Option<String>`
+note: function defined here
+  --> $DIR/issue-89856.rs:3:4
+   |
+LL | fn take_str_maybe(_: Option<&str>) { }
+   |    ^^^^^^^^^^^^^^ ---------------
+help: try converting the passed type into a `&str`
+   |
+LL |     take_str_maybe(option.as_deref());
+   |                          +++++++++++
+
+error[E0308]: mismatched types
+  --> $DIR/issue-89856.rs:12:20
+   |
+LL |     take_str_maybe(option_ref);
+   |     -------------- ^^^^^^^^^^ expected `Option<&str>`, found `Option<&String>`
    |     |
    |     arguments to this function are incorrect
    |
    = note: expected enum `Option<&str>`
               found enum `Option<&String>`
 note: function defined here
-  --> $DIR/issue-89856.rs:1:4
+  --> $DIR/issue-89856.rs:3:4
    |
-LL | fn take_str_maybe(x: Option<&str>) -> Option<&str> { None }
+LL | fn take_str_maybe(_: Option<&str>) { }
    |    ^^^^^^^^^^^^^^ ---------------
 help: try converting the passed type into a `&str`
    |
-LL |     take_str_maybe(option.map(|x| &**x));
-   |                          ++++++++++++++
+LL |     take_str_maybe(option_ref.map(|x| x.as_str()));
+   |                              ++++++++++++++++++++
 
-error: aborting due to previous error
+error[E0308]: mismatched types
+  --> $DIR/issue-89856.rs:16:20
+   |
+LL |     take_str_maybe(option_ref_ref);
+   |     -------------- ^^^^^^^^^^^^^^ expected `Option<&str>`, found `Option<&&String>`
+   |     |
+   |     arguments to this function are incorrect
+   |
+   = note: expected enum `Option<&str>`
+              found enum `Option<&&String>`
+note: function defined here
+  --> $DIR/issue-89856.rs:3:4
+   |
+LL | fn take_str_maybe(_: Option<&str>) { }
+   |    ^^^^^^^^^^^^^^ ---------------
+help: try converting the passed type into a `&str`
+   |
+LL |     take_str_maybe(option_ref_ref.map(|x| x.as_str()));
+   |                                  ++++++++++++++++++++
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0308`.