Only make suggestion when type is Copy.

This commit makes the suggestion to dereference when a type implements
`Deref` only apply if the dereference would succeed (ie. the type is
`Copy`, otherwise a borrow check error would occur).
This commit is contained in:
David Wood 2019-04-22 10:16:47 +01:00
parent fd95ba3574
commit 7ab1bfd692
No known key found for this signature in database
GPG key ID: 01760B4F9F53F154
9 changed files with 91 additions and 75 deletions

View file

@ -324,8 +324,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
sp, sp,
); );
match (&expected.sty, &checked_ty.sty) { // Check the `expn_info()` to see if this is a macro; if so, it's hard to
(&ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (&exp.sty, &check.sty) { // extract the text and make a good suggestion, so don't bother.
let is_macro = sp.ctxt().outer().expn_info().is_some();
match (&expr.node, &expected.sty, &checked_ty.sty) {
(_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (&exp.sty, &check.sty) {
(&ty::Str, &ty::Array(arr, _)) | (&ty::Str, &ty::Array(arr, _)) |
(&ty::Str, &ty::Slice(arr)) if arr == self.tcx.types.u8 => { (&ty::Str, &ty::Slice(arr)) if arr == self.tcx.types.u8 => {
if let hir::ExprKind::Lit(_) = expr.node { if let hir::ExprKind::Lit(_) = expr.node {
@ -352,7 +356,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
} }
_ => {} _ => {}
}, },
(&ty::Ref(_, _, mutability), _) => { (_, &ty::Ref(_, _, mutability), _) => {
// Check if it can work when put into a ref. For example: // Check if it can work when put into a ref. For example:
// //
// ``` // ```
@ -407,28 +411,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
}); });
} }
} }
} },
(_, &ty::Ref(_, checked, _)) => { (hir::ExprKind::AddrOf(_, ref expr), _, &ty::Ref(_, checked, _)) if {
self.infcx.can_sub(self.param_env, checked, &expected).is_ok() && !is_macro
} => {
// We have `&T`, check if what was expected was `T`. If so, // We have `&T`, check if what was expected was `T`. If so,
// we may want to suggest adding a `*`, or removing // we may want to suggest removing a `&`.
// a `&`.
//
// (But, also check the `expn_info()` to see if this is
// a macro; if so, it's hard to extract the text and make a good
// suggestion, so don't bother.)
if self.infcx.can_sub(self.param_env, checked, &expected).is_ok() &&
sp.ctxt().outer().expn_info().is_none() {
match expr.node {
// Maybe remove `&`?
hir::ExprKind::AddrOf(_, ref expr) => {
if !cm.span_to_filename(expr.span).is_real() { if !cm.span_to_filename(expr.span).is_real() {
if let Ok(code) = cm.span_to_snippet(sp) { if let Ok(code) = cm.span_to_snippet(sp) {
if code.chars().next() == Some('&') { if code.chars().next() == Some('&') {
return Some(( return Some((
sp, sp,
"consider removing the borrow", "consider removing the borrow",
code[1..].to_string()), code[1..].to_string(),
); ));
} }
} }
return None; return None;
@ -436,36 +432,10 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
if let Ok(code) = cm.span_to_snippet(expr.span) { if let Ok(code) = cm.span_to_snippet(expr.span) {
return Some((sp, "consider removing the borrow", code)); return Some((sp, "consider removing the borrow", code));
} }
}
// Maybe add `*`? Only if `T: Copy`.
_ => {
if self.infcx.type_is_copy_modulo_regions(self.param_env,
checked,
sp) {
// do not suggest if the span comes from a macro (#52783)
if let (Ok(code), true) = (
cm.span_to_snippet(sp),
sp == expr.span,
) {
return Some((
sp,
"consider dereferencing the borrow",
if is_struct_pat_shorthand_field {
format!("{}: *{}", code, code)
} else {
format!("*{}", code)
}, },
)); _ if sp == expr.span && !is_macro => {
} // Check for `Deref` implementations by constructing a predicate to
} // prove: `<T as Deref>::Output == U`
}
}
}
}
_ => {
// If neither type is a reference, then check for `Deref` implementations by
// constructing a predicate to prove: `<T as Deref>::Output == U`
let deref_trait = self.tcx.lang_items().deref_trait().unwrap(); let deref_trait = self.tcx.lang_items().deref_trait().unwrap();
let item_def_id = self.tcx.associated_items(deref_trait).next().unwrap().def_id; let item_def_id = self.tcx.associated_items(deref_trait).next().unwrap().def_id;
let predicate = ty::Predicate::Projection(ty::Binder::bind(ty::ProjectionPredicate { let predicate = ty::Predicate::Projection(ty::Binder::bind(ty::ProjectionPredicate {
@ -483,17 +453,28 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
ty: expected, ty: expected,
})); }));
let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate); let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
if self.infcx.predicate_may_hold(&obligation) { let impls_deref = self.infcx.predicate_may_hold(&obligation);
if let (Ok(code), true) = (cm.span_to_snippet(sp), sp == expr.span) {
let msg = if is_struct_pat_shorthand_field { // For a suggestion to make sense, the type would need to be `Copy`.
let is_copy = self.infcx.type_is_copy_modulo_regions(self.param_env, expected, sp);
if is_copy && impls_deref {
if let Ok(code) = cm.span_to_snippet(sp) {
let message = if checked_ty.is_region_ptr() {
"consider dereferencing the borrow"
} else {
"consider dereferencing the type"
};
let suggestion = if is_struct_pat_shorthand_field {
format!("{}: *{}", code, code) format!("{}: *{}", code, code)
} else { } else {
format!("*{}", code) format!("*{}", code)
}; };
return Some((sp, "consider dereferencing the type", msg)); return Some((sp, message, suggestion));
} }
} }
} }
_ => {}
} }
None None
} }

View file

@ -5,7 +5,7 @@ LL | x = box x;
| ^^^^^ | ^^^^^
| | | |
| cyclic type of infinite size | cyclic type of infinite size
| help: consider dereferencing the type: `*box x` | help: try using a conversion method: `box x.to_string()`
error[E0055]: reached the recursion limit while auto-dereferencing `Foo` error[E0055]: reached the recursion limit while auto-dereferencing `Foo`
--> $DIR/infinite-autoderef.rs:25:5 --> $DIR/infinite-autoderef.rs:25:5

View file

@ -5,7 +5,7 @@ LL | f = box g;
| ^^^^^ | ^^^^^
| | | |
| cyclic type of infinite size | cyclic type of infinite size
| help: consider dereferencing the type: `*box g` | help: try using a conversion method: `box g.to_string()`
error: aborting due to previous error error: aborting due to previous error

View file

@ -5,7 +5,7 @@ LL | f = box f;
| ^^^^^ | ^^^^^
| | | |
| cyclic type of infinite size | cyclic type of infinite size
| help: consider dereferencing the type: `*box f` | help: try using a conversion method: `box f.to_string()`
error: aborting due to previous error error: aborting due to previous error

View file

@ -44,7 +44,7 @@ LL | f = box f;
| ^^^^^ | ^^^^^
| | | |
| cyclic type of infinite size | cyclic type of infinite size
| help: consider dereferencing the type: `*box f` | help: try using a conversion method: `box f.to_string()`
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/coerce-suggestions.rs:21:9 --> $DIR/coerce-suggestions.rs:21:9

View file

@ -7,9 +7,18 @@
struct Foo(i32); struct Foo(i32);
struct Bar(String);
impl std::ops::Deref for Foo { impl std::ops::Deref for Foo {
type Target = i32; type Target = i32;
fn deref(&self) -> &i32 { fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::Deref for Bar {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }
} }
@ -19,4 +28,8 @@ fn main() {
let y: i32 = *x; //~ ERROR mismatched types let y: i32 = *x; //~ ERROR mismatched types
let a = &42; let a = &42;
let b: i32 = *a; //~ ERROR mismatched types let b: i32 = *a; //~ ERROR mismatched types
// Do not make a suggestion when adding a `*` wouldn't actually fix the issue:
let f = Bar("bar".to_string());
let g: String = f.to_string(); //~ ERROR mismatched types
} }

View file

@ -7,9 +7,18 @@
struct Foo(i32); struct Foo(i32);
struct Bar(String);
impl std::ops::Deref for Foo { impl std::ops::Deref for Foo {
type Target = i32; type Target = i32;
fn deref(&self) -> &i32 { fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::ops::Deref for Bar {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0 &self.0
} }
} }
@ -19,4 +28,8 @@ fn main() {
let y: i32 = x; //~ ERROR mismatched types let y: i32 = x; //~ ERROR mismatched types
let a = &42; let a = &42;
let b: i32 = a; //~ ERROR mismatched types let b: i32 = a; //~ ERROR mismatched types
// Do not make a suggestion when adding a `*` wouldn't actually fix the issue:
let f = Bar("bar".to_string());
let g: String = f; //~ ERROR mismatched types
} }

View file

@ -1,5 +1,5 @@
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/issue-59819.rs:19:18 --> $DIR/issue-59819.rs:28:18
| |
LL | let y: i32 = x; LL | let y: i32 = x;
| ^ | ^
@ -11,7 +11,7 @@ LL | let y: i32 = x;
found type `Foo` found type `Foo`
error[E0308]: mismatched types error[E0308]: mismatched types
--> $DIR/issue-59819.rs:21:18 --> $DIR/issue-59819.rs:30:18
| |
LL | let b: i32 = a; LL | let b: i32 = a;
| ^ | ^
@ -22,6 +22,18 @@ LL | let b: i32 = a;
= note: expected type `i32` = note: expected type `i32`
found type `&{integer}` found type `&{integer}`
error: aborting due to 2 previous errors error[E0308]: mismatched types
--> $DIR/issue-59819.rs:34:21
|
LL | let g: String = f;
| ^
| |
| expected struct `std::string::String`, found struct `Bar`
| help: try using a conversion method: `f.to_string()`
|
= note: expected type `std::string::String`
found type `Bar`
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0308`. For more information about this error, try `rustc --explain E0308`.

View file

@ -2,10 +2,7 @@ error[E0308]: mismatched types
--> $DIR/terr-sorts.rs:10:14 --> $DIR/terr-sorts.rs:10:14
| |
LL | want_foo(b); LL | want_foo(b);
| ^ | ^ expected struct `Foo`, found struct `std::boxed::Box`
| |
| expected struct `Foo`, found struct `std::boxed::Box`
| help: consider dereferencing the type: `*b`
| |
= note: expected type `Foo` = note: expected type `Foo`
found type `std::boxed::Box<Foo>` found type `std::boxed::Box<Foo>`