Account for unbounded type param receiver in suggestions

When encountering

```rust
fn f<T>(a: T, b: T) -> std::cmp::Ordering {
    a.cmp(&b) //~ ERROR E0599
}
```

output

```
error[E0599]: no method named `cmp` found for type parameter `T` in the current scope
  --> $DIR/method-on-unbounded-type-param.rs:2:7
   |
LL | fn f<T>(a: T, b: T) -> std::cmp::Ordering {
   |      - method `cmp` not found for this type parameter
LL |     a.cmp(&b)
   |       ^^^ method cannot be called on `T` due to unsatisfied 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 f<T: Ord>(a: T, b: T) -> std::cmp::Ordering {
   |       +++++
LL | fn f<T: Iterator>(a: T, b: T) -> std::cmp::Ordering {
   |       ++++++++++
```

Fix #120186.
This commit is contained in:
Esteban Küber 2024-01-26 19:02:05 +00:00
parent d34b0fa495
commit 20b1c2aafc
2 changed files with 35 additions and 22 deletions

View file

@ -554,6 +554,17 @@ pub fn report_no_match_method_error(
"`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement"
));
}
} else if !unsatisfied_predicates.is_empty() && matches!(rcvr_ty.kind(), ty::Param(_)) {
// We special case the situation where we are looking for `_` in
// `<TypeParam as _>::method` because otherwise the machinery will look for blanket
// implementations that have unsatisfied trait bounds to suggest, leading us to claim
// things like "we're looking for a trait with method `cmp`, both `Iterator` and `Ord`
// have one, in order to implement `Ord` you need to restrict `TypeParam: FnPtr` so
// that `impl<T: FnPtr> Ord for T` can apply", which is not what we want. We have a type
// parameter, we want to directly say "`Ord::cmp` and `Iterator::cmp` exist, restrict
// `TypeParam: Ord` or `TypeParam: Iterator`"". That is done further down when calling
// `self.suggest_traits_to_import`, so we ignore the `unsatisfied_predicates`
// suggestions.
} else if !unsatisfied_predicates.is_empty() {
let mut type_params = FxIndexMap::default();
@ -1325,7 +1336,7 @@ trait bound{s}",
}
}
self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_name, expected);
return Some(err);
Some(err)
}
fn note_candidates_on_method_error(
@ -2918,19 +2929,7 @@ fn suggest_traits_to_import(
// this isn't perfect (that is, there are cases when
// implementing a trait would be legal but is rejected
// here).
unsatisfied_predicates.iter().all(|(p, _, _)| {
match p.kind().skip_binder() {
// Hide traits if they are present in predicates as they can be fixed without
// having to implement them.
ty::PredicateKind::Clause(ty::ClauseKind::Trait(t)) => {
t.def_id() == info.def_id
}
ty::PredicateKind::Clause(ty::ClauseKind::Projection(p)) => {
p.projection_ty.def_id == info.def_id
}
_ => false,
}
}) && (type_is_local || info.def_id.is_local())
(type_is_local || info.def_id.is_local())
&& !self.tcx.trait_is_auto(info.def_id)
&& self
.associated_value(info.def_id, item_name)
@ -2978,6 +2977,20 @@ fn suggest_traits_to_import(
item.visibility(self.tcx).is_public() || info.def_id.is_local()
})
.is_some()
&& (matches!(rcvr_ty.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
// having to implement them.
ty::PredicateKind::Clause(ty::ClauseKind::Trait(t)) => {
t.def_id() == info.def_id
}
ty::PredicateKind::Clause(ty::ClauseKind::Projection(p)) => {
p.projection_ty.def_id == info.def_id
}
_ => false,
}
}))
})
.collect::<Vec<_>>();
for span in &arbitrary_rcvr {

View file

@ -1,18 +1,18 @@
error[E0599]: `T` is not an iterator
error[E0599]: no method named `cmp` found for type parameter `T` in the current scope
--> $DIR/method-on-unbounded-type-param.rs:2:7
|
LL | fn f<T>(a: T, b: T) -> std::cmp::Ordering {
| - method `cmp` not found for this type parameter
LL | a.cmp(&b)
| ^^^ `T` is not an iterator
| ^^^ method cannot be called on `T` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`T: Iterator`
which is required by `&mut T: Iterator`
help: consider restricting the type parameter to satisfy the trait bound
= 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 f<T>(a: T, b: T) -> std::cmp::Ordering where T: Iterator {
| +++++++++++++++++
LL | fn f<T: Ord>(a: T, b: T) -> std::cmp::Ordering {
| +++++
LL | fn f<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:5:10