Skip to content

Commit d5a0c7c

Browse files
committed
Auto merge of rust-lang#90645 - terrarier2111:master, r=estebank
Implement diagnostic for String conversion This is my first real contribution to rustc, any feedback is highly appreciated. This should fix rust-lang#89856 Thanks to `@estebank` for guiding me.
2 parents 4205481 + 829a528 commit d5a0c7c

File tree

3 files changed

+85
-36
lines changed

3 files changed

+85
-36
lines changed

compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs

+61-36
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_middle::lint::in_external_macro;
1414
use rustc_middle::ty::{self, Binder, Ty};
1515
use rustc_span::symbol::{kw, sym};
1616

17+
use rustc_middle::ty::subst::GenericArgKind;
1718
use std::iter;
1819

1920
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -232,48 +233,72 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
232233
let is_struct_pat_shorthand_field =
233234
self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span);
234235
let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
235-
if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
236-
let mut suggestions = iter::zip(iter::repeat(&expr_text), &methods)
237-
.filter_map(|(receiver, method)| {
238-
let method_call = format!(".{}()", method.ident);
239-
if receiver.ends_with(&method_call) {
240-
None // do not suggest code that is already there (#53348)
241-
} else {
242-
let method_call_list = [".to_vec()", ".to_string()"];
243-
let mut sugg = if receiver.ends_with(".clone()")
244-
&& method_call_list.contains(&method_call.as_str())
245-
{
246-
let max_len = receiver.rfind('.').unwrap();
247-
vec![(
248-
expr.span,
249-
format!("{}{}", &receiver[..max_len], method_call),
250-
)]
236+
if !methods.is_empty() {
237+
if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
238+
let mut suggestions = iter::zip(iter::repeat(&expr_text), &methods)
239+
.filter_map(|(receiver, method)| {
240+
let method_call = format!(".{}()", method.ident);
241+
if receiver.ends_with(&method_call) {
242+
None // do not suggest code that is already there (#53348)
251243
} else {
252-
if expr.precedence().order() < ExprPrecedence::MethodCall.order() {
253-
vec![
254-
(expr.span.shrink_to_lo(), "(".to_string()),
255-
(expr.span.shrink_to_hi(), format!("){}", method_call)),
256-
]
244+
let method_call_list = [".to_vec()", ".to_string()"];
245+
let mut sugg = if receiver.ends_with(".clone()")
246+
&& method_call_list.contains(&method_call.as_str())
247+
{
248+
let max_len = receiver.rfind('.').unwrap();
249+
vec![(
250+
expr.span,
251+
format!("{}{}", &receiver[..max_len], method_call),
252+
)]
257253
} else {
258-
vec![(expr.span.shrink_to_hi(), method_call)]
254+
if expr.precedence().order()
255+
< ExprPrecedence::MethodCall.order()
256+
{
257+
vec![
258+
(expr.span.shrink_to_lo(), "(".to_string()),
259+
(expr.span.shrink_to_hi(), format!("){}", method_call)),
260+
]
261+
} else {
262+
vec![(expr.span.shrink_to_hi(), method_call)]
263+
}
264+
};
265+
if is_struct_pat_shorthand_field {
266+
sugg.insert(
267+
0,
268+
(expr.span.shrink_to_lo(), format!("{}: ", receiver)),
269+
);
259270
}
260-
};
261-
if is_struct_pat_shorthand_field {
262-
sugg.insert(
263-
0,
264-
(expr.span.shrink_to_lo(), format!("{}: ", receiver)),
271+
Some(sugg)
272+
}
273+
})
274+
.peekable();
275+
if suggestions.peek().is_some() {
276+
err.multipart_suggestions(
277+
"try using a conversion method",
278+
suggestions,
279+
Applicability::MaybeIncorrect,
280+
);
281+
}
282+
}
283+
} else if found.to_string().starts_with("Option<")
284+
&& expected.to_string() == "Option<&str>"
285+
{
286+
if let ty::Adt(_def, subst) = found.kind() {
287+
if subst.len() != 0 {
288+
if let GenericArgKind::Type(ty) = subst[0].unpack() {
289+
let peeled = ty.peel_refs().to_string();
290+
if peeled == "String" {
291+
let ref_cnt = ty.to_string().len() - peeled.len();
292+
let result = format!(".map(|x| &*{}x)", "*".repeat(ref_cnt));
293+
err.span_suggestion_verbose(
294+
expr.span.shrink_to_hi(),
295+
"try converting the passed type into a `&str`",
296+
result,
297+
Applicability::MaybeIncorrect,
265298
);
266299
}
267-
Some(sugg)
268300
}
269-
})
270-
.peekable();
271-
if suggestions.peek().is_some() {
272-
err.multipart_suggestions(
273-
"try using a conversion method",
274-
suggestions,
275-
Applicability::MaybeIncorrect,
276-
);
301+
}
277302
}
278303
}
279304
}

src/test/ui/typeck/issue-89856.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
fn take_str_maybe(x: Option<&str>) -> Option<&str> { None }
2+
3+
fn main() {
4+
let string = String::from("Hello, world");
5+
let option = Some(&string);
6+
take_str_maybe(option);
7+
//~^ ERROR: mismatched types [E0308]
8+
}

src/test/ui/typeck/issue-89856.stderr

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-89856.rs:6:20
3+
|
4+
LL | take_str_maybe(option);
5+
| ^^^^^^ expected `str`, found struct `String`
6+
|
7+
= note: expected enum `Option<&str>`
8+
found enum `Option<&String>`
9+
help: try converting the passed type into a `&str`
10+
|
11+
LL | take_str_maybe(option.map(|x| &**x));
12+
| ++++++++++++++
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)