about summary refs log tree commit diff
diff options
context:
space:
mode:
authorthreadexception <hannes.gaumann@outlook.de>2021-11-06 10:31:46 +0100
committerthreadexception <hannes.gaumann@outlook.de>2021-11-13 15:17:44 +0100
commit829a5288ec5e33f10e6f5fe2dc891a037d125e6c (patch)
tree9e9e6b90ad9a6559a656793349dfd257f5106d03
parent0d1754e8bf6942b4c1d24d7c923438782129ba5a (diff)
downloadrust-829a5288ec5e33f10e6f5fe2dc891a037d125e6c.tar.gz
rust-829a5288ec5e33f10e6f5fe2dc891a037d125e6c.zip
Implement diagnostic for String conversion
Co-authored-by: Esteban Kuber <estebank@users.noreply.github.com>
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs97
-rw-r--r--src/test/ui/typeck/issue-89856.rs8
-rw-r--r--src/test/ui/typeck/issue-89856.stderr16
3 files changed, 85 insertions, 36 deletions
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
index dcc635a1f00..6c7d3a0c9c0 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
@@ -14,6 +14,7 @@ use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Binder, Ty};
 use rustc_span::symbol::{kw, sym};
 
+use rustc_middle::ty::subst::GenericArgKind;
 use std::iter;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -232,48 +233,72 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let is_struct_pat_shorthand_field =
                 self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span);
             let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
-            if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
-                let mut suggestions = iter::zip(iter::repeat(&expr_text), &methods)
-                    .filter_map(|(receiver, method)| {
-                        let method_call = format!(".{}()", method.ident);
-                        if receiver.ends_with(&method_call) {
-                            None // do not suggest code that is already there (#53348)
-                        } else {
-                            let method_call_list = [".to_vec()", ".to_string()"];
-                            let mut sugg = if receiver.ends_with(".clone()")
-                                && method_call_list.contains(&method_call.as_str())
-                            {
-                                let max_len = receiver.rfind('.').unwrap();
-                                vec![(
-                                    expr.span,
-                                    format!("{}{}", &receiver[..max_len], method_call),
-                                )]
+            if !methods.is_empty() {
+                if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
+                    let mut suggestions = iter::zip(iter::repeat(&expr_text), &methods)
+                        .filter_map(|(receiver, method)| {
+                            let method_call = format!(".{}()", method.ident);
+                            if receiver.ends_with(&method_call) {
+                                None // do not suggest code that is already there (#53348)
                             } else {
-                                if expr.precedence().order() < ExprPrecedence::MethodCall.order() {
-                                    vec![
-                                        (expr.span.shrink_to_lo(), "(".to_string()),
-                                        (expr.span.shrink_to_hi(), format!("){}", method_call)),
-                                    ]
+                                let method_call_list = [".to_vec()", ".to_string()"];
+                                let mut sugg = if receiver.ends_with(".clone()")
+                                    && method_call_list.contains(&method_call.as_str())
+                                {
+                                    let max_len = receiver.rfind('.').unwrap();
+                                    vec![(
+                                        expr.span,
+                                        format!("{}{}", &receiver[..max_len], method_call),
+                                    )]
                                 } else {
-                                    vec![(expr.span.shrink_to_hi(), method_call)]
+                                    if expr.precedence().order()
+                                        < ExprPrecedence::MethodCall.order()
+                                    {
+                                        vec![
+                                            (expr.span.shrink_to_lo(), "(".to_string()),
+                                            (expr.span.shrink_to_hi(), format!("){}", method_call)),
+                                        ]
+                                    } else {
+                                        vec![(expr.span.shrink_to_hi(), method_call)]
+                                    }
+                                };
+                                if is_struct_pat_shorthand_field {
+                                    sugg.insert(
+                                        0,
+                                        (expr.span.shrink_to_lo(), format!("{}: ", receiver)),
+                                    );
                                 }
-                            };
-                            if is_struct_pat_shorthand_field {
-                                sugg.insert(
-                                    0,
-                                    (expr.span.shrink_to_lo(), format!("{}: ", receiver)),
+                                Some(sugg)
+                            }
+                        })
+                        .peekable();
+                    if suggestions.peek().is_some() {
+                        err.multipart_suggestions(
+                            "try using a conversion method",
+                            suggestions,
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                }
+            } else if found.to_string().starts_with("Option<")
+                && expected.to_string() == "Option<&str>"
+            {
+                if let ty::Adt(_def, subst) = found.kind() {
+                    if subst.len() != 0 {
+                        if let GenericArgKind::Type(ty) = subst[0].unpack() {
+                            let peeled = ty.peel_refs().to_string();
+                            if peeled == "String" {
+                                let ref_cnt = ty.to_string().len() - peeled.len();
+                                let result = format!(".map(|x| &*{}x)", "*".repeat(ref_cnt));
+                                err.span_suggestion_verbose(
+                                    expr.span.shrink_to_hi(),
+                                    "try converting the passed type into a `&str`",
+                                    result,
+                                    Applicability::MaybeIncorrect,
                                 );
                             }
-                            Some(sugg)
                         }
-                    })
-                    .peekable();
-                if suggestions.peek().is_some() {
-                    err.multipart_suggestions(
-                        "try using a conversion method",
-                        suggestions,
-                        Applicability::MaybeIncorrect,
-                    );
+                    }
                 }
             }
         }
diff --git a/src/test/ui/typeck/issue-89856.rs b/src/test/ui/typeck/issue-89856.rs
new file mode 100644
index 00000000000..b021e349e35
--- /dev/null
+++ b/src/test/ui/typeck/issue-89856.rs
@@ -0,0 +1,8 @@
+fn take_str_maybe(x: Option<&str>) -> Option<&str> { None }
+
+fn main() {
+    let string = String::from("Hello, world");
+    let option = Some(&string);
+    take_str_maybe(option);
+    //~^ ERROR: mismatched types [E0308]
+}
diff --git a/src/test/ui/typeck/issue-89856.stderr b/src/test/ui/typeck/issue-89856.stderr
new file mode 100644
index 00000000000..4cb46a34a07
--- /dev/null
+++ b/src/test/ui/typeck/issue-89856.stderr
@@ -0,0 +1,16 @@
+error[E0308]: mismatched types
+  --> $DIR/issue-89856.rs:6:20
+   |
+LL |     take_str_maybe(option);
+   |                    ^^^^^^ expected `str`, found struct `String`
+   |
+   = note: expected enum `Option<&str>`
+              found enum `Option<&String>`
+help: try converting the passed type into a `&str`
+   |
+LL |     take_str_maybe(option.map(|x| &**x));
+   |                          ++++++++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.