diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index 6a8f7f4ee3513..2e81d595b9491 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -33,8 +33,9 @@ use rustc_middle::ty::print::{
};
use rustc_middle::ty::{
self, AdtKind, GenericArgs, InferTy, IsSuggestable, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable,
- TypeFolder, TypeSuperFoldable, TypeVisitableExt, TypeckResults, Upcast,
- suggest_arbitrary_trait_bound, suggest_constraining_type_param,
+ TypeFolder, TypeSuperFoldable, TypeSuperVisitable, TypeVisitableExt, TypeVisitor,
+ TypeckResults, Upcast, UpcastFrom, suggest_arbitrary_trait_bound,
+ suggest_constraining_type_param,
};
use rustc_middle::{bug, span_bug};
use rustc_span::def_id::LocalDefId;
@@ -260,6 +261,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
_ => (false, None),
};
+ let mut v = ParamFinder { params: vec![] };
+ // Get all type parameters from the predicate. If the predicate references a type parameter
+ // at all, then we can only suggestion a bound on an item that has access to that parameter.
+ v.visit_predicate(UpcastFrom::upcast_from(trait_pred, self.tcx));
+
// FIXME: Add check for trait bound that is already present, particularly `?Sized` so we
// don't suggest `T: Sized + ?Sized`.
loop {
@@ -327,6 +333,40 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
);
return;
}
+
+ hir::Node::TraitItem(hir::TraitItem {
+ generics,
+ kind: hir::TraitItemKind::Fn(fn_sig, ..),
+ ..
+ })
+ | hir::Node::ImplItem(hir::ImplItem {
+ generics,
+ kind: hir::ImplItemKind::Fn(fn_sig, ..),
+ ..
+ }) if projection.is_none()
+ && !param_ty
+ && generics.params.iter().any(|param| {
+ v.params.iter().any(|p| p.name == param.name.ident().name)
+ }) =>
+ {
+ // This associated function has a generic type parameter that matches a
+ // parameter from the trait predicate, which means that we should suggest
+ // constraining the complex type here, and not at the trait/impl level (if
+ // it doesn't have that type parameter). This can be something like
+ // `Type: From`.
+ suggest_restriction(
+ self.tcx,
+ body_id,
+ generics,
+ "the type",
+ err,
+ Some(fn_sig),
+ projection,
+ trait_pred,
+ None,
+ );
+ }
+
hir::Node::Item(hir::Item {
kind:
hir::ItemKind::Trait(_, _, generics, ..)
@@ -425,7 +465,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
| hir::ItemKind::Const(_, generics, _)
| hir::ItemKind::TraitAlias(generics, _),
..
- }) if !param_ty => {
+ }) if !param_ty
+ && (generics.params.iter().any(|param| {
+ v.params.iter().any(|p| p.name == param.name.ident().name)
+ }) || v.params.is_empty()) =>
+ {
// Missing generic type parameter bound.
if suggest_arbitrary_trait_bound(
self.tcx,
@@ -5439,6 +5483,23 @@ fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec) {
(ty, refs)
}
+/// Look for type parameters.
+struct ParamFinder {
+ params: Vec,
+}
+
+impl<'tcx> TypeVisitor> for ParamFinder {
+ fn visit_ty(&mut self, t: Ty<'tcx>) {
+ match t.kind() {
+ ty::Param(param) if param.name != kw::SelfUpper => {
+ self.params.push(*param);
+ }
+ _ => {}
+ }
+ t.super_visit_with(self)
+ }
+}
+
/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting
/// `param: ?Sized` would be a valid constraint.
struct FindTypeParam {
diff --git a/tests/ui/methods/suggest-restriction-involving-type-param.fixed b/tests/ui/methods/suggest-restriction-involving-type-param.fixed
new file mode 100644
index 0000000000000..066a491f07533
--- /dev/null
+++ b/tests/ui/methods/suggest-restriction-involving-type-param.fixed
@@ -0,0 +1,31 @@
+//@ run-rustfix
+
+#[derive(Clone)]
+struct A {
+ x: String
+}
+
+struct B {
+ x: String
+}
+
+impl From for B {
+ fn from(a: A) -> Self {
+ B { x: a.x }
+ }
+}
+
+impl B {
+ pub fn from_many + Clone>(v: Vec) -> Self where B: From {
+ B { x: v.iter().map(|e| B::from(e.clone()).x).collect::>().join(" ") }
+ //~^ ERROR the trait bound `B: From` is not satisfied
+ }
+}
+
+fn main() {
+ let _b: B = B { x: "foobar".to_string() };
+ let a: A = A { x: "frob".to_string() };
+ let ab: B = a.into();
+ println!("Hello, {}!", ab.x);
+ let _c: B = B::from_many(vec![A { x: "x".to_string() }]);
+}
diff --git a/tests/ui/methods/suggest-restriction-involving-type-param.rs b/tests/ui/methods/suggest-restriction-involving-type-param.rs
new file mode 100644
index 0000000000000..748224044d9fd
--- /dev/null
+++ b/tests/ui/methods/suggest-restriction-involving-type-param.rs
@@ -0,0 +1,31 @@
+//@ run-rustfix
+
+#[derive(Clone)]
+struct A {
+ x: String
+}
+
+struct B {
+ x: String
+}
+
+impl From for B {
+ fn from(a: A) -> Self {
+ B { x: a.x }
+ }
+}
+
+impl B {
+ pub fn from_many + Clone>(v: Vec) -> Self {
+ B { x: v.iter().map(|e| B::from(e.clone()).x).collect::>().join(" ") }
+ //~^ ERROR the trait bound `B: From` is not satisfied
+ }
+}
+
+fn main() {
+ let _b: B = B { x: "foobar".to_string() };
+ let a: A = A { x: "frob".to_string() };
+ let ab: B = a.into();
+ println!("Hello, {}!", ab.x);
+ let _c: B = B::from_many(vec![A { x: "x".to_string() }]);
+}
diff --git a/tests/ui/methods/suggest-restriction-involving-type-param.stderr b/tests/ui/methods/suggest-restriction-involving-type-param.stderr
new file mode 100644
index 0000000000000..a59acbb822534
--- /dev/null
+++ b/tests/ui/methods/suggest-restriction-involving-type-param.stderr
@@ -0,0 +1,14 @@
+error[E0277]: the trait bound `B: From` is not satisfied
+ --> $DIR/suggest-restriction-involving-type-param.rs:20:33
+ |
+LL | B { x: v.iter().map(|e| B::from(e.clone()).x).collect::>().join(" ") }
+ | ^ the trait `From` is not implemented for `B`
+ |
+help: consider further restricting the type
+ |
+LL | pub fn from_many + Clone>(v: Vec) -> Self where B: From {
+ | ++++++++++++++++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.