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`.