Skip to content

Commit 64b3e4a

Browse files
committed
suggest adding a reference to a trait assoc item
1 parent 75b7e52 commit 64b3e4a

File tree

4 files changed

+156
-85
lines changed

4 files changed

+156
-85
lines changed

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

Lines changed: 101 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
882882
obligation.cause.code()
883883
{
884884
&parent_code
885+
} else if let ObligationCauseCode::ItemObligation(_) = obligation.cause.code() {
886+
obligation.cause.code()
885887
} else if let ExpnKind::Desugaring(DesugaringKind::ForLoop) =
886888
span.ctxt().outer_expn_data().kind
887889
{
@@ -906,102 +908,116 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
906908
let param_env = obligation.param_env;
907909

908910
// Try to apply the original trait binding obligation by borrowing.
909-
let mut try_borrowing =
910-
|old_pred: ty::PolyTraitPredicate<'tcx>, blacklist: &[DefId]| -> bool {
911-
if blacklist.contains(&old_pred.def_id()) {
912-
return false;
913-
}
914-
// We map bounds to `&T` and `&mut T`
915-
let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| {
916-
(
917-
trait_pred,
918-
self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
919-
)
920-
});
921-
let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| {
922-
(
923-
trait_pred,
924-
self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
925-
)
926-
});
911+
let mut try_borrowing = |old_pred: ty::PolyTraitPredicate<'tcx>,
912+
blacklist: &[DefId]|
913+
-> bool {
914+
if blacklist.contains(&old_pred.def_id()) {
915+
return false;
916+
}
917+
// We map bounds to `&T` and `&mut T`
918+
let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| {
919+
(
920+
trait_pred,
921+
self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
922+
)
923+
});
924+
let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| {
925+
(
926+
trait_pred,
927+
self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
928+
)
929+
});
927930

928-
let mk_result = |trait_pred_and_new_ty| {
929-
let obligation =
930-
self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty);
931-
self.predicate_must_hold_modulo_regions(&obligation)
931+
let mk_result = |trait_pred_and_new_ty| {
932+
let obligation =
933+
self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty);
934+
self.predicate_must_hold_modulo_regions(&obligation)
935+
};
936+
let imm_result = mk_result(trait_pred_and_imm_ref);
937+
let mut_result = mk_result(trait_pred_and_mut_ref);
938+
939+
let ref_inner_ty_result =
940+
if let ObligationCauseCode::ItemObligation(_) = obligation.cause.code()
941+
&& let ty::Ref(_, ty, mutability) = old_pred.self_ty().skip_binder().kind()
942+
{
943+
Some((mk_result(old_pred.map_bound(|trait_pred| (trait_pred, *ty))), mutability))
944+
} else {
945+
None
932946
};
933-
let imm_result = mk_result(trait_pred_and_imm_ref);
934-
let mut_result = mk_result(trait_pred_and_mut_ref);
935-
936-
if imm_result || mut_result {
937-
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
938-
// We have a very specific type of error, where just borrowing this argument
939-
// might solve the problem. In cases like this, the important part is the
940-
// original type obligation, not the last one that failed, which is arbitrary.
941-
// Because of this, we modify the error to refer to the original obligation and
942-
// return early in the caller.
943-
944-
let msg = format!("the trait bound `{}` is not satisfied", old_pred);
945-
if has_custom_message {
946-
err.note(&msg);
947-
} else {
948-
err.message =
949-
vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)];
950-
}
951-
if snippet.starts_with('&') {
952-
// This is already a literal borrow and the obligation is failing
953-
// somewhere else in the obligation chain. Do not suggest non-sense.
954-
return false;
955-
}
956-
err.span_label(
957-
span,
958-
&format!(
959-
"expected an implementor of trait `{}`",
960-
old_pred.print_modifiers_and_trait_path(),
961-
),
962-
);
963947

964-
// This if is to prevent a special edge-case
965-
if matches!(
966-
span.ctxt().outer_expn_data().kind,
967-
ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop)
968-
) {
969-
// We don't want a borrowing suggestion on the fields in structs,
970-
// ```
971-
// struct Foo {
972-
// the_foos: Vec<Foo>
973-
// }
974-
// ```
948+
if imm_result || mut_result || ref_inner_ty_result.map_or(false, |(result, _)| result) {
949+
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
950+
// We have a very specific type of error, where just borrowing this argument
951+
// might solve the problem. In cases like this, the important part is the
952+
// original type obligation, not the last one that failed, which is arbitrary.
953+
// Because of this, we modify the error to refer to the original obligation and
954+
// return early in the caller.
955+
956+
let msg = format!("the trait bound `{}` is not satisfied", old_pred);
957+
if has_custom_message {
958+
err.note(&msg);
959+
} else {
960+
err.message =
961+
vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)];
962+
}
963+
if snippet.starts_with('&') {
964+
// This is already a literal borrow and the obligation is failing
965+
// somewhere else in the obligation chain. Do not suggest non-sense.
966+
return false;
967+
}
968+
err.span_label(
969+
span,
970+
&format!(
971+
"expected an implementor of trait `{}`",
972+
old_pred.print_modifiers_and_trait_path(),
973+
),
974+
);
975975

976-
if imm_result && mut_result {
977-
err.span_suggestions(
978-
span.shrink_to_lo(),
979-
"consider borrowing here",
980-
["&".to_string(), "&mut ".to_string()].into_iter(),
981-
Applicability::MaybeIncorrect,
982-
);
983-
} else {
984-
err.span_suggestion_verbose(
985-
span.shrink_to_lo(),
986-
&format!(
987-
"consider{} borrowing here",
988-
if mut_result { " mutably" } else { "" }
989-
),
990-
format!("&{}", if mut_result { "mut " } else { "" }),
991-
Applicability::MaybeIncorrect,
992-
);
993-
}
976+
// This if is to prevent a special edge-case
977+
if matches!(
978+
span.ctxt().outer_expn_data().kind,
979+
ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop)
980+
) {
981+
// We don't want a borrowing suggestion on the fields in structs,
982+
// ```
983+
// struct Foo {
984+
// the_foos: Vec<Foo>
985+
// }
986+
// ```
987+
988+
if imm_result && mut_result {
989+
err.span_suggestions(
990+
span.shrink_to_lo(),
991+
"consider borrowing here",
992+
["&".to_string(), "&mut ".to_string()].into_iter(),
993+
Applicability::MaybeIncorrect,
994+
);
995+
} else {
996+
let is_mut = mut_result
997+
|| ref_inner_ty_result.map_or(false, |(_, mutabl)| {
998+
matches!(mutabl, hir::Mutability::Mut)
999+
});
1000+
err.span_suggestion_verbose(
1001+
span.shrink_to_lo(),
1002+
&format!(
1003+
"consider{} borrowing here",
1004+
if is_mut { " mutably" } else { "" }
1005+
),
1006+
format!("&{}", if is_mut { "mut " } else { "" }),
1007+
Applicability::MaybeIncorrect,
1008+
);
9941009
}
995-
return true;
9961010
}
1011+
return true;
9971012
}
998-
return false;
999-
};
1013+
}
1014+
return false;
1015+
};
10001016

10011017
if let ObligationCauseCode::ImplDerivedObligation(cause) = &*code {
10021018
try_borrowing(cause.derived.parent_trait_pred, &[])
10031019
} else if let ObligationCauseCode::BindingObligation(_, _)
1004-
| ObligationCauseCode::ItemObligation(_) = code
1020+
| ObligationCauseCode::ItemObligation(..) = code
10051021
{
10061022
try_borrowing(poly_trait_pred, &never_suggest_borrow)
10071023
} else {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// run-rustfix
2+
#![allow(unused_variables)]
3+
4+
fn foo(foo: &mut usize) {
5+
todo!()
6+
}
7+
8+
fn bar(bar: &usize) {
9+
todo!()
10+
}
11+
12+
fn main() {
13+
foo(&mut Default::default()); //~ the trait bound `&mut usize: Default` is not satisfied
14+
bar(&Default::default()); //~ the trait bound `&usize: Default` is not satisfied
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// run-rustfix
2+
#![allow(unused_variables)]
3+
4+
fn foo(foo: &mut usize) {
5+
todo!()
6+
}
7+
8+
fn bar(bar: &usize) {
9+
todo!()
10+
}
11+
12+
fn main() {
13+
foo(Default::default()); //~ the trait bound `&mut usize: Default` is not satisfied
14+
bar(Default::default()); //~ the trait bound `&usize: Default` is not satisfied
15+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
error[E0277]: the trait bound `&mut usize: Default` is not satisfied
2+
--> $DIR/suggest-adding-reference-to-trait-assoc-item.rs:13:9
3+
|
4+
LL | foo(Default::default());
5+
| ^^^^^^^^^^^^^^^^ expected an implementor of trait `Default`
6+
|
7+
help: consider mutably borrowing here
8+
|
9+
LL | foo(&mut Default::default());
10+
| ++++
11+
12+
error[E0277]: the trait bound `&usize: Default` is not satisfied
13+
--> $DIR/suggest-adding-reference-to-trait-assoc-item.rs:14:9
14+
|
15+
LL | bar(Default::default());
16+
| ^^^^^^^^^^^^^^^^ expected an implementor of trait `Default`
17+
|
18+
help: consider borrowing here
19+
|
20+
LL | bar(&Default::default());
21+
| +
22+
23+
error: aborting due to 2 previous errors
24+
25+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)