Skip to content

Commit

Permalink
Auto merge of #90645 - terrarier2111:master, r=estebank
Browse files Browse the repository at this point in the history
Implement diagnostic for String conversion

This is my first real contribution to rustc, any feedback is highly appreciated.
This should fix #89856

Thanks to `@estebank` for guiding me.
  • Loading branch information
bors committed Nov 15, 2021
2 parents 4205481 + 829a528 commit d5a0c7c
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 36 deletions.
97 changes: 61 additions & 36 deletions compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -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,
);
}
}
}
}
Expand Down
8 changes: 8 additions & 0 deletions src/test/ui/typeck/issue-89856.rs
Original file line number Diff line number Diff line change
@@ -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]
}
16 changes: 16 additions & 0 deletions src/test/ui/typeck/issue-89856.stderr
Original file line number Diff line number Diff line change
@@ -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`.

0 comments on commit d5a0c7c

Please sign in to comment.