Skip to content

Commit 4eff852

Browse files
committed
Auto merge of rust-lang#60155 - davidtwco:issue-59819, r=oli-obk
Suggest dereferencing when `Deref` is implemented. Fixes rust-lang#59819. r? @oli-obk cc @estebank
2 parents fe0a415 + 7ab1bfd commit 4eff852

File tree

4 files changed

+175
-55
lines changed

4 files changed

+175
-55
lines changed

src/librustc_typeck/check/demand.rs

+66-55
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::check::FnCtxt;
22
use rustc::infer::InferOk;
3-
use rustc::traits::{ObligationCause, ObligationCauseCode};
3+
use rustc::traits::{self, ObligationCause, ObligationCauseCode};
44

55
use syntax::util::parser::PREC_POSTFIX;
66
use syntax_pos::Span;
@@ -324,8 +324,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
324324
sp,
325325
);
326326

327-
match (&expected.sty, &checked_ty.sty) {
328-
(&ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (&exp.sty, &check.sty) {
327+
// Check the `expn_info()` to see if this is a macro; if so, it's hard to
328+
// extract the text and make a good suggestion, so don't bother.
329+
let is_macro = sp.ctxt().outer().expn_info().is_some();
330+
331+
match (&expr.node, &expected.sty, &checked_ty.sty) {
332+
(_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (&exp.sty, &check.sty) {
329333
(&ty::Str, &ty::Array(arr, _)) |
330334
(&ty::Str, &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
331335
if let hir::ExprKind::Lit(_) = expr.node {
@@ -352,7 +356,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
352356
}
353357
_ => {}
354358
},
355-
(&ty::Ref(_, _, mutability), _) => {
359+
(_, &ty::Ref(_, _, mutability), _) => {
356360
// Check if it can work when put into a ref. For example:
357361
//
358362
// ```
@@ -407,60 +411,67 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
407411
});
408412
}
409413
}
410-
}
411-
(_, &ty::Ref(_, checked, _)) => {
414+
},
415+
(hir::ExprKind::AddrOf(_, ref expr), _, &ty::Ref(_, checked, _)) if {
416+
self.infcx.can_sub(self.param_env, checked, &expected).is_ok() && !is_macro
417+
} => {
412418
// We have `&T`, check if what was expected was `T`. If so,
413-
// we may want to suggest adding a `*`, or removing
414-
// a `&`.
415-
//
416-
// (But, also check the `expn_info()` to see if this is
417-
// a macro; if so, it's hard to extract the text and make a good
418-
// suggestion, so don't bother.)
419-
if self.infcx.can_sub(self.param_env, checked, &expected).is_ok() &&
420-
sp.ctxt().outer().expn_info().is_none() {
421-
match expr.node {
422-
// Maybe remove `&`?
423-
hir::ExprKind::AddrOf(_, ref expr) => {
424-
if !cm.span_to_filename(expr.span).is_real() {
425-
if let Ok(code) = cm.span_to_snippet(sp) {
426-
if code.chars().next() == Some('&') {
427-
return Some((
428-
sp,
429-
"consider removing the borrow",
430-
code[1..].to_string()),
431-
);
432-
}
433-
}
434-
return None;
435-
}
436-
if let Ok(code) = cm.span_to_snippet(expr.span) {
437-
return Some((sp, "consider removing the borrow", code));
438-
}
439-
}
440-
441-
// Maybe add `*`? Only if `T: Copy`.
442-
_ => {
443-
if self.infcx.type_is_copy_modulo_regions(self.param_env,
444-
checked,
445-
sp) {
446-
// do not suggest if the span comes from a macro (#52783)
447-
if let (Ok(code), true) = (
448-
cm.span_to_snippet(sp),
449-
sp == expr.span,
450-
) {
451-
return Some((
452-
sp,
453-
"consider dereferencing the borrow",
454-
if is_struct_pat_shorthand_field {
455-
format!("{}: *{}", code, code)
456-
} else {
457-
format!("*{}", code)
458-
},
459-
));
460-
}
461-
}
419+
// we may want to suggest removing a `&`.
420+
if !cm.span_to_filename(expr.span).is_real() {
421+
if let Ok(code) = cm.span_to_snippet(sp) {
422+
if code.chars().next() == Some('&') {
423+
return Some((
424+
sp,
425+
"consider removing the borrow",
426+
code[1..].to_string(),
427+
));
462428
}
463429
}
430+
return None;
431+
}
432+
if let Ok(code) = cm.span_to_snippet(expr.span) {
433+
return Some((sp, "consider removing the borrow", code));
434+
}
435+
},
436+
_ if sp == expr.span && !is_macro => {
437+
// Check for `Deref` implementations by constructing a predicate to
438+
// prove: `<T as Deref>::Output == U`
439+
let deref_trait = self.tcx.lang_items().deref_trait().unwrap();
440+
let item_def_id = self.tcx.associated_items(deref_trait).next().unwrap().def_id;
441+
let predicate = ty::Predicate::Projection(ty::Binder::bind(ty::ProjectionPredicate {
442+
// `<T as Deref>::Output`
443+
projection_ty: ty::ProjectionTy {
444+
// `T`
445+
substs: self.tcx.mk_substs_trait(
446+
checked_ty,
447+
self.fresh_substs_for_item(sp, item_def_id),
448+
),
449+
// `Deref::Output`
450+
item_def_id,
451+
},
452+
// `U`
453+
ty: expected,
454+
}));
455+
let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
456+
let impls_deref = self.infcx.predicate_may_hold(&obligation);
457+
458+
// For a suggestion to make sense, the type would need to be `Copy`.
459+
let is_copy = self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp);
460+
461+
if is_copy && impls_deref {
462+
if let Ok(code) = cm.span_to_snippet(sp) {
463+
let message = if checked_ty.is_region_ptr() {
464+
"consider dereferencing the borrow"
465+
} else {
466+
"consider dereferencing the type"
467+
};
468+
let suggestion = if is_struct_pat_shorthand_field {
469+
format!("{}: *{}", code, code)
470+
} else {
471+
format!("*{}", code)
472+
};
473+
return Some((sp, message, suggestion));
474+
}
464475
}
465476
}
466477
_ => {}
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// run-rustfix
2+
3+
#![allow(warnings)]
4+
5+
// Test that suggestion to add `*` characters applies to implementations of `Deref` as well as
6+
// references.
7+
8+
struct Foo(i32);
9+
10+
struct Bar(String);
11+
12+
impl std::ops::Deref for Foo {
13+
type Target = i32;
14+
fn deref(&self) -> &Self::Target {
15+
&self.0
16+
}
17+
}
18+
19+
impl std::ops::Deref for Bar {
20+
type Target = String;
21+
fn deref(&self) -> &Self::Target {
22+
&self.0
23+
}
24+
}
25+
26+
fn main() {
27+
let x = Foo(42);
28+
let y: i32 = *x; //~ ERROR mismatched types
29+
let a = &42;
30+
let b: i32 = *a; //~ ERROR mismatched types
31+
32+
// Do not make a suggestion when adding a `*` wouldn't actually fix the issue:
33+
let f = Bar("bar".to_string());
34+
let g: String = f.to_string(); //~ ERROR mismatched types
35+
}
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// run-rustfix
2+
3+
#![allow(warnings)]
4+
5+
// Test that suggestion to add `*` characters applies to implementations of `Deref` as well as
6+
// references.
7+
8+
struct Foo(i32);
9+
10+
struct Bar(String);
11+
12+
impl std::ops::Deref for Foo {
13+
type Target = i32;
14+
fn deref(&self) -> &Self::Target {
15+
&self.0
16+
}
17+
}
18+
19+
impl std::ops::Deref for Bar {
20+
type Target = String;
21+
fn deref(&self) -> &Self::Target {
22+
&self.0
23+
}
24+
}
25+
26+
fn main() {
27+
let x = Foo(42);
28+
let y: i32 = x; //~ ERROR mismatched types
29+
let a = &42;
30+
let b: i32 = a; //~ ERROR mismatched types
31+
32+
// Do not make a suggestion when adding a `*` wouldn't actually fix the issue:
33+
let f = Bar("bar".to_string());
34+
let g: String = f; //~ ERROR mismatched types
35+
}
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-59819.rs:28:18
3+
|
4+
LL | let y: i32 = x;
5+
| ^
6+
| |
7+
| expected i32, found struct `Foo`
8+
| help: consider dereferencing the type: `*x`
9+
|
10+
= note: expected type `i32`
11+
found type `Foo`
12+
13+
error[E0308]: mismatched types
14+
--> $DIR/issue-59819.rs:30:18
15+
|
16+
LL | let b: i32 = a;
17+
| ^
18+
| |
19+
| expected i32, found &{integer}
20+
| help: consider dereferencing the borrow: `*a`
21+
|
22+
= note: expected type `i32`
23+
found type `&{integer}`
24+
25+
error[E0308]: mismatched types
26+
--> $DIR/issue-59819.rs:34:21
27+
|
28+
LL | let g: String = f;
29+
| ^
30+
| |
31+
| expected struct `std::string::String`, found struct `Bar`
32+
| help: try using a conversion method: `f.to_string()`
33+
|
34+
= note: expected type `std::string::String`
35+
found type `Bar`
36+
37+
error: aborting due to 3 previous errors
38+
39+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)