about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs41
-rw-r--r--tests/ui/traits/method-on-unbounded-type-param.stderr16
2 files changed, 35 insertions, 22 deletions
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 6c9501e93fa..de42f011cf4 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -554,6 +554,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     "`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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
         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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // 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 @@ 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(_))
+                        || 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 {
diff --git a/tests/ui/traits/method-on-unbounded-type-param.stderr b/tests/ui/traits/method-on-unbounded-type-param.stderr
index 0b650995de5..f12b4d6cabd 100644
--- a/tests/ui/traits/method-on-unbounded-type-param.stderr
+++ b/tests/ui/traits/method-on-unbounded-type-param.stderr
@@ -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