Skip to content

Commit

Permalink
Account for non-overlapping unmet trait bounds in suggestion
Browse files Browse the repository at this point in the history
When a method not found on a type parameter could have been provided by any of multiple traits, suggest each trait individually, instead of a single suggestion to restrict the type parameter with *all* of them.

Before:

```
error[E0599]: the method `cmp` exists for reference `&T`, but its trait bounds were not satisfied
  --> $DIR/method-on-unbounded-type-param.rs:5:10
   |
LL |     (&a).cmp(&b)
   |          ^^^ method cannot be called on `&T` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `T: Ord`
           which is required by `&T: Ord`
           `&T: Iterator`
           which is required by `&mut &T: Iterator`
           `T: Iterator`
           which is required by `&mut T: Iterator`
help: consider restricting the type parameters to satisfy the trait bounds
   |
LL | fn g<T>(a: T, b: T) -> std::cmp::Ordering where T: Iterator, T: Ord {
   |                                           +++++++++++++++++++++++++
```

After:

```
error[E0599]: the method `cmp` exists for reference `&T`, but its trait bounds were not satisfied
  --> $DIR/method-on-unbounded-type-param.rs:5:10
   |
LL |     (&a).cmp(&b)
   |          ^^^ method cannot be called on `&T` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `T: Ord`
           which is required by `&T: Ord`
           `&T: Iterator`
           which is required by `&mut &T: Iterator`
           `T: Iterator`
           which is required by `&mut T: Iterator`
   = help: items from traits can only be used if the type parameter is bounded by the trait
help: the following traits define an item `cmp`, perhaps you need to restrict type parameter `T` with one of them:
   |
LL | fn g<T: Ord>(a: T, b: T) -> std::cmp::Ordering {
   |       +++++
LL | fn g<T: Iterator>(a: T, b: T) -> std::cmp::Ordering {
   |       ++++++++++
```

Fix rust-lang#108428.
  • Loading branch information
estebank committed Jan 30, 2024
1 parent 11dbd30 commit 9f0a002
Show file tree
Hide file tree
Showing 4 changed files with 36 additions and 30 deletions.
42 changes: 24 additions & 18 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

let mut bound_spans = vec![];
let mut restrict_type_params = false;
let mut suggested_derive = false;
let mut unsatisfied_bounds = false;
if item_name.name == sym::count && self.is_slice_ty(rcvr_ty, span) {
let msg = "consider using `len` instead";
Expand Down Expand Up @@ -932,20 +933,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.enumerate()
.collect::<Vec<(usize, String)>>();

for ((span, add_where_or_comma), obligations) in type_params.into_iter() {
restrict_type_params = true;
// #74886: Sort here so that the output is always the same.
let obligations = obligations.into_sorted_stable_ord();
err.span_suggestion_verbose(
span,
format!(
"consider restricting the type parameter{s} to satisfy the \
trait bound{s}",
s = pluralize!(obligations.len())
),
format!("{} {}", add_where_or_comma, obligations.join(", ")),
Applicability::MaybeIncorrect,
);
info!("type_params {:#?}", type_params);

if !matches!(rcvr_ty.peel_refs().kind(), ty::Param(_)) {
for ((span, add_where_or_comma), obligations) in type_params.into_iter() {
restrict_type_params = true;
// #74886: Sort here so that the output is always the same.
let obligations = obligations.into_sorted_stable_ord();
err.span_suggestion_verbose(
span,
format!(
"consider restricting the type parameter{s} to satisfy the trait \
bound{s}",
s = pluralize!(obligations.len())
),
format!("{} {}", add_where_or_comma, obligations.join(", ")),
Applicability::MaybeIncorrect,
);
}
}

bound_list.sort_by(|(_, a), (_, b)| a.cmp(b)); // Sort alphabetically.
Expand Down Expand Up @@ -993,7 +998,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"the following trait bounds were not satisfied:\n{bound_list}"
));
}
self.suggest_derive(&mut err, unsatisfied_predicates);
suggested_derive = self.suggest_derive(&mut err, unsatisfied_predicates);

unsatisfied_bounds = true;
}
Expand Down Expand Up @@ -1187,7 +1192,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.span_label(span, msg);
}

if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params {
if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params || suggested_derive {
} else {
self.suggest_traits_to_import(
&mut err,
Expand Down Expand Up @@ -2447,7 +2452,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Option<ty::Predicate<'tcx>>,
Option<ObligationCause<'tcx>>,
)],
) {
) -> bool {
let mut derives = self.note_predicate_source_and_get_derives(err, unsatisfied_predicates);
derives.sort();
derives.dedup();
Expand All @@ -2472,6 +2477,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Applicability::MaybeIncorrect,
);
}
!derives_grouped.is_empty()
}

fn note_derefed_ty_has_method(
Expand Down Expand Up @@ -2943,7 +2949,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
item.visibility(self.tcx).is_public() || info.def_id.is_local()
})
.is_some()
&& (matches!(rcvr_ty.kind(), ty::Param(_))
&& (matches!(rcvr_ty.peel_refs().kind(), ty::Param(_))
|| unsatisfied_predicates.iter().all(|(p, _, _)| {
match p.kind().skip_binder() {
// Hide traits if they are present in predicates as they can be fixed without
Expand Down
3 changes: 0 additions & 3 deletions tests/ui/box/unit/unique-pinned-nocopy.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@ LL | let _j = i.clone();
= note: the following trait bounds were not satisfied:
`R: Clone`
which is required by `Box<R>: Clone`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `Clone`
help: consider annotating `R` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
Expand Down
3 changes: 0 additions & 3 deletions tests/ui/derives/derive-assoc-type-not-impl.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ note: trait bound `NotClone: Clone` was not satisfied
|
LL | #[derive(Clone)]
| ^^^^^ unsatisfied trait bound introduced in this `derive` macro
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `Clone`
help: consider annotating `NotClone` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
Expand Down
18 changes: 12 additions & 6 deletions tests/ui/traits/method-on-unbounded-type-param.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@ LL | (&a).cmp(&b)
which is required by `&mut &T: Iterator`
`T: Iterator`
which is required by `&mut T: Iterator`
help: consider restricting the type parameters to satisfy the trait bounds
= help: items from traits can only be used if the type parameter is bounded by the trait
help: the following traits define an item `cmp`, perhaps you need to restrict type parameter `T` with one of them:
|
LL | fn g<T>(a: T, b: T) -> std::cmp::Ordering where T: Iterator, T: Ord {
| +++++++++++++++++++++++++
LL | fn g<T: Ord>(a: T, b: T) -> std::cmp::Ordering {
| +++++
LL | fn g<T: Iterator>(a: T, b: T) -> std::cmp::Ordering {
| ++++++++++

error[E0599]: the method `cmp` exists for reference `&T`, but its trait bounds were not satisfied
--> $DIR/method-on-unbounded-type-param.rs:8:7
Expand All @@ -45,10 +48,13 @@ LL | a.cmp(&b)
which is required by `&mut &T: Iterator`
`T: Iterator`
which is required by `&mut T: Iterator`
help: consider restricting the type parameters to satisfy the trait bounds
= help: items from traits can only be used if the type parameter is bounded by the trait
help: the following traits define an item `cmp`, perhaps you need to restrict type parameter `T` with one of them:
|
LL | fn h<T>(a: &T, b: T) -> std::cmp::Ordering where T: Iterator, T: Ord {
| +++++++++++++++++++++++++
LL | fn h<T: Ord>(a: &T, b: T) -> std::cmp::Ordering {
| +++++
LL | fn h<T: Iterator>(a: &T, b: T) -> std::cmp::Ordering {
| ++++++++++

error[E0599]: the method `cmp` exists for struct `Box<dyn T>`, but its trait bounds were not satisfied
--> $DIR/method-on-unbounded-type-param.rs:14:7
Expand Down

0 comments on commit 9f0a002

Please sign in to comment.