Suggest borrowing if a trait implementation is found for &/&mut <type>
This commit is contained in:
parent
94ecdfd115
commit
48d07d1326
6 changed files with 154 additions and 17 deletions
|
@ -686,17 +686,42 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Blacklist traits for which it would be nonsensical to suggest borrowing.
|
||||||
|
// For instance, immutable references are always Copy, so suggesting to
|
||||||
|
// borrow would always succeed, but it's probably not what the user wanted.
|
||||||
|
let blacklist: Vec<_> = [
|
||||||
|
LangItem::Copy,
|
||||||
|
LangItem::Clone,
|
||||||
|
LangItem::Pin,
|
||||||
|
LangItem::Unpin,
|
||||||
|
LangItem::Sized,
|
||||||
|
LangItem::Send,
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.filter_map(|lang_item| self.tcx.lang_items().require(*lang_item).ok())
|
||||||
|
.collect();
|
||||||
|
|
||||||
let span = obligation.cause.span;
|
let span = obligation.cause.span;
|
||||||
let param_env = obligation.param_env;
|
let param_env = obligation.param_env;
|
||||||
let trait_ref = trait_ref.skip_binder();
|
let trait_ref = trait_ref.skip_binder();
|
||||||
|
|
||||||
if let ObligationCauseCode::ImplDerivedObligation(obligation) = &obligation.cause.code {
|
let found_ty = trait_ref.self_ty();
|
||||||
// Try to apply the original trait binding obligation by borrowing.
|
let found_ty_str = found_ty.to_string();
|
||||||
let self_ty = trait_ref.self_ty();
|
let imm_borrowed_found_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, found_ty);
|
||||||
let found = self_ty.to_string();
|
let imm_substs = self.tcx.mk_substs_trait(imm_borrowed_found_ty, &[]);
|
||||||
let new_self_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, self_ty);
|
let mut_borrowed_found_ty = self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, found_ty);
|
||||||
let substs = self.tcx.mk_substs_trait(new_self_ty, &[]);
|
let mut_substs = self.tcx.mk_substs_trait(mut_borrowed_found_ty, &[]);
|
||||||
let new_trait_ref = ty::TraitRef::new(obligation.parent_trait_ref.def_id(), substs);
|
|
||||||
|
// Try to apply the original trait binding obligation by borrowing.
|
||||||
|
let mut try_borrowing = |new_trait_ref: ty::TraitRef<'tcx>,
|
||||||
|
expected_trait_ref: ty::TraitRef<'tcx>,
|
||||||
|
mtbl: bool,
|
||||||
|
blacklist: &[DefId]|
|
||||||
|
-> bool {
|
||||||
|
if blacklist.contains(&expected_trait_ref.def_id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
let new_obligation = Obligation::new(
|
let new_obligation = Obligation::new(
|
||||||
ObligationCause::dummy(),
|
ObligationCause::dummy(),
|
||||||
param_env,
|
param_env,
|
||||||
|
@ -713,8 +738,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
"the trait bound `{}: {}` is not satisfied",
|
"the trait bound `{}: {}` is not satisfied",
|
||||||
found,
|
found_ty_str,
|
||||||
obligation.parent_trait_ref.skip_binder().print_only_trait_path(),
|
expected_trait_ref.print_only_trait_path(),
|
||||||
);
|
);
|
||||||
if has_custom_message {
|
if has_custom_message {
|
||||||
err.note(&msg);
|
err.note(&msg);
|
||||||
|
@ -730,7 +755,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
span,
|
span,
|
||||||
&format!(
|
&format!(
|
||||||
"expected an implementor of trait `{}`",
|
"expected an implementor of trait `{}`",
|
||||||
obligation.parent_trait_ref.skip_binder().print_only_trait_path(),
|
expected_trait_ref.print_only_trait_path(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -745,16 +770,52 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
err.span_suggestion(
|
err.span_suggestion(
|
||||||
span,
|
span,
|
||||||
"consider borrowing here",
|
&format!(
|
||||||
format!("&{}", snippet),
|
"consider borrowing{} here",
|
||||||
|
if mtbl { " mutably" } else { "" }
|
||||||
|
),
|
||||||
|
format!("&{}{}", if mtbl { "mut " } else { "" }, snippet),
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let ObligationCauseCode::ImplDerivedObligation(obligation) = &obligation.cause.code {
|
||||||
|
let expected_trait_ref = obligation.parent_trait_ref.skip_binder();
|
||||||
|
let new_imm_trait_ref =
|
||||||
|
ty::TraitRef::new(obligation.parent_trait_ref.def_id(), imm_substs);
|
||||||
|
let new_mut_trait_ref =
|
||||||
|
ty::TraitRef::new(obligation.parent_trait_ref.def_id(), mut_substs);
|
||||||
|
if try_borrowing(new_imm_trait_ref, expected_trait_ref, false, &[]) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return try_borrowing(new_mut_trait_ref, expected_trait_ref, true, &[]);
|
||||||
|
}
|
||||||
|
} else if let ObligationCauseCode::BindingObligation(_, _)
|
||||||
|
| ObligationCauseCode::ItemObligation(_) = &obligation.cause.code
|
||||||
|
{
|
||||||
|
if try_borrowing(
|
||||||
|
ty::TraitRef::new(trait_ref.def_id, imm_substs),
|
||||||
|
trait_ref,
|
||||||
|
false,
|
||||||
|
&blacklist[..],
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return try_borrowing(
|
||||||
|
ty::TraitRef::new(trait_ref.def_id, mut_substs),
|
||||||
|
trait_ref,
|
||||||
|
true,
|
||||||
|
&blacklist[..],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`,
|
/// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`,
|
||||||
|
|
|
@ -21,10 +21,10 @@ LL | fn foo<X: Trait>(_: X) {}
|
||||||
| ----- required by this bound in `foo`
|
| ----- required by this bound in `foo`
|
||||||
...
|
...
|
||||||
LL | foo(s);
|
LL | foo(s);
|
||||||
| ^ the trait `Trait` is not implemented for `S`
|
| ^
|
||||||
|
|
| |
|
||||||
= help: the following implementations were found:
|
| expected an implementor of trait `Trait`
|
||||||
<&'a mut S as Trait>
|
| help: consider borrowing mutably here: `&mut s`
|
||||||
|
|
||||||
error: aborting due to 2 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
|
13
src/test/ui/suggestions/issue-84973-2.rs
Normal file
13
src/test/ui/suggestions/issue-84973-2.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
// A slight variation of issue-84973.rs. Here, a mutable borrow is
|
||||||
|
// required (and the obligation kind is different).
|
||||||
|
|
||||||
|
trait Tr {}
|
||||||
|
impl Tr for &mut i32 {}
|
||||||
|
|
||||||
|
fn foo<T: Tr>(i: T) {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let a: i32 = 32;
|
||||||
|
foo(a);
|
||||||
|
//~^ ERROR: the trait bound `i32: Tr` is not satisfied [E0277]
|
||||||
|
}
|
15
src/test/ui/suggestions/issue-84973-2.stderr
Normal file
15
src/test/ui/suggestions/issue-84973-2.stderr
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
error[E0277]: the trait bound `i32: Tr` is not satisfied
|
||||||
|
--> $DIR/issue-84973-2.rs:11:9
|
||||||
|
|
|
||||||
|
LL | fn foo<T: Tr>(i: T) {}
|
||||||
|
| -- required by this bound in `foo`
|
||||||
|
...
|
||||||
|
LL | foo(a);
|
||||||
|
| ^
|
||||||
|
| |
|
||||||
|
| expected an implementor of trait `Tr`
|
||||||
|
| help: consider borrowing mutably here: `&mut a`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
33
src/test/ui/suggestions/issue-84973.rs
Normal file
33
src/test/ui/suggestions/issue-84973.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
// Checks whether borrowing is suggested when a trait bound is not satisfied
|
||||||
|
// for found type `T`, but is for `&/&mut T`.
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let f = Fancy{};
|
||||||
|
let o = Other::new(f);
|
||||||
|
//~^ ERROR: the trait bound `Fancy: SomeTrait` is not satisfied [E0277]
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Fancy {}
|
||||||
|
|
||||||
|
impl <'a> SomeTrait for &'a Fancy {
|
||||||
|
}
|
||||||
|
|
||||||
|
trait SomeTrait {}
|
||||||
|
|
||||||
|
struct Other<'a, G> {
|
||||||
|
a: &'a str,
|
||||||
|
g: G,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Broadly copied from https://docs.rs/petgraph/0.5.1/src/petgraph/dot.rs.html#70
|
||||||
|
impl<'a, G> Other<'a, G>
|
||||||
|
where
|
||||||
|
G: SomeTrait,
|
||||||
|
{
|
||||||
|
pub fn new(g: G) -> Self {
|
||||||
|
Other {
|
||||||
|
a: "hi",
|
||||||
|
g: g,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
src/test/ui/suggestions/issue-84973.stderr
Normal file
15
src/test/ui/suggestions/issue-84973.stderr
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
error[E0277]: the trait bound `Fancy: SomeTrait` is not satisfied
|
||||||
|
--> $DIR/issue-84973.rs:6:24
|
||||||
|
|
|
||||||
|
LL | let o = Other::new(f);
|
||||||
|
| ^
|
||||||
|
| |
|
||||||
|
| expected an implementor of trait `SomeTrait`
|
||||||
|
| help: consider borrowing here: `&f`
|
||||||
|
...
|
||||||
|
LL | pub fn new(g: G) -> Self {
|
||||||
|
| ------------------------ required by `Other::<'a, G>::new`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
Loading…
Add table
Add a link
Reference in a new issue