1
Fork 0

suggest both immutable and mutable trait implementations

This commit is contained in:
Takayuki Maeda 2021-09-26 16:16:04 +09:00
parent 4da89a180f
commit 0661c2de24
3 changed files with 70 additions and 32 deletions

View file

@ -714,22 +714,28 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
let mut_substs = self.tcx.mk_substs_trait(mut_borrowed_found_ty, &[]); let mut_substs = self.tcx.mk_substs_trait(mut_borrowed_found_ty, &[]);
// Try to apply the original trait binding obligation by borrowing. // Try to apply the original trait binding obligation by borrowing.
let mut try_borrowing = |new_trait_ref: ty::TraitRef<'tcx>, let mut try_borrowing = |new_imm_trait_ref: ty::TraitRef<'tcx>,
new_mut_trait_ref: ty::TraitRef<'tcx>,
expected_trait_ref: ty::TraitRef<'tcx>, expected_trait_ref: ty::TraitRef<'tcx>,
mtbl: bool,
blacklist: &[DefId]| blacklist: &[DefId]|
-> bool { -> bool {
if blacklist.contains(&expected_trait_ref.def_id) { if blacklist.contains(&expected_trait_ref.def_id) {
return false; return false;
} }
let new_obligation = Obligation::new( let imm_result = self.predicate_must_hold_modulo_regions(&Obligation::new(
ObligationCause::dummy(), ObligationCause::dummy(),
param_env, param_env,
ty::Binder::dummy(new_trait_ref).without_const().to_predicate(self.tcx), ty::Binder::dummy(new_imm_trait_ref).without_const().to_predicate(self.tcx),
); ));
if self.predicate_must_hold_modulo_regions(&new_obligation) { let mut_result = self.predicate_must_hold_modulo_regions(&Obligation::new(
ObligationCause::dummy(),
param_env,
ty::Binder::dummy(new_mut_trait_ref).without_const().to_predicate(self.tcx),
));
if imm_result || mut_result {
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
// We have a very specific type of error, where just borrowing this argument // We have a very specific type of error, where just borrowing this argument
// might solve the problem. In cases like this, the important part is the // might solve the problem. In cases like this, the important part is the
@ -773,16 +779,25 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
// } // }
// ``` // ```
if imm_result && mut_result {
err.span_suggestions(
span,
"consider borrowing here",
[format!("&{}", snippet), format!("&mut {}", snippet)].into_iter(),
Applicability::MaybeIncorrect,
);
} else {
err.span_suggestion( err.span_suggestion(
span, span,
&format!( &format!(
"consider{} borrowing here", "consider{} borrowing here",
if mtbl { " mutably" } else { "" } if mut_result { " mutably" } else { "" }
), ),
format!("&{}{}", if mtbl { "mut " } else { "" }, snippet), format!("&{}{}", if mut_result { "mut " } else { "" }, snippet),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
} }
}
return true; return true;
} }
} }
@ -795,29 +810,16 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
ty::TraitRef::new(obligation.parent_trait_ref.def_id(), imm_substs); ty::TraitRef::new(obligation.parent_trait_ref.def_id(), imm_substs);
let new_mut_trait_ref = let new_mut_trait_ref =
ty::TraitRef::new(obligation.parent_trait_ref.def_id(), mut_substs); ty::TraitRef::new(obligation.parent_trait_ref.def_id(), mut_substs);
if try_borrowing(new_imm_trait_ref, expected_trait_ref, false, &[]) { return try_borrowing(new_imm_trait_ref, new_mut_trait_ref, expected_trait_ref, &[]);
return true;
} else {
return try_borrowing(new_mut_trait_ref, expected_trait_ref, true, &[]);
}
} else if let ObligationCauseCode::BindingObligation(_, _) } else if let ObligationCauseCode::BindingObligation(_, _)
| ObligationCauseCode::ItemObligation(_) = &*code | ObligationCauseCode::ItemObligation(_) = &*code
{ {
if try_borrowing(
ty::TraitRef::new(trait_ref.def_id, imm_substs),
trait_ref,
false,
&never_suggest_borrow[..],
) {
return true;
} else {
return try_borrowing( return try_borrowing(
ty::TraitRef::new(trait_ref.def_id, imm_substs),
ty::TraitRef::new(trait_ref.def_id, mut_substs), ty::TraitRef::new(trait_ref.def_id, mut_substs),
trait_ref, trait_ref,
true,
&never_suggest_borrow[..], &never_suggest_borrow[..],
); );
}
} else { } else {
false false
} }

View file

@ -0,0 +1,13 @@
trait Trait {}
struct S;
impl Trait for &S {}
impl Trait for &mut S {}
fn foo<X: Trait>(_: X) {}
fn main() {
let s = S;
foo(s); //~ ERROR the trait bound `S: Trait` is not satisfied
}

View file

@ -0,0 +1,23 @@
error[E0277]: the trait bound `S: Trait` is not satisfied
--> $DIR/suggest-both-imm-and-mut-trait-implementation.rs:12:9
|
LL | foo(s);
| --- ^ expected an implementor of trait `Trait`
| |
| required by a bound introduced by this call
|
note: required by a bound in `foo`
--> $DIR/suggest-both-imm-and-mut-trait-implementation.rs:8:11
|
LL | fn foo<X: Trait>(_: X) {}
| ^^^^^ required by this bound in `foo`
help: consider borrowing here
|
LL | foo(&s);
| ~~
LL | foo(&mut s);
| ~~~~~~
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.