diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 88c98fa979e1c..5fa65f33c763c 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -329,41 +329,52 @@ fn bounds_from_generic_predicates<'tcx>( _ => {} } } - let generics = if types.is_empty() { - "".to_string() - } else { - format!( - "<{}>", - types - .keys() - .filter_map(|t| match t.kind() { - ty::Param(_) => Some(t.to_string()), - // Avoid suggesting the following: - // fn foo::Bar>(_: T) where T: Trait, ::Bar: Other {} - _ => None, - }) - .collect::>() - .join(", ") - ) - }; + let mut where_clauses = vec![]; + let mut types_str = vec![]; for (ty, bounds) in types { - where_clauses - .extend(bounds.into_iter().map(|bound| format!("{}: {}", ty, tcx.def_path_str(bound)))); - } - for projection in &projections { - let p = projection.skip_binder(); - // FIXME: this is not currently supported syntax, we should be looking at the `types` and - // insert the associated types where they correspond, but for now let's be "lazy" and - // propose this instead of the following valid resugaring: - // `T: Trait, Trait::Assoc = K` → `T: Trait` - where_clauses.push(format!("{} = {}", tcx.def_path_str(p.projection_ty.def_id), p.term)); + if let ty::Param(_) = ty.kind() { + let mut bounds_str = vec![]; + for bound in bounds { + let mut projections_str = vec![]; + for projection in &projections { + let p = projection.skip_binder(); + let alias_ty = p.projection_ty; + if bound == tcx.parent(alias_ty.def_id) && alias_ty.self_ty() == ty { + let name = tcx.item_name(alias_ty.def_id); + projections_str.push(format!("{} = {}", name, p.term)); + } + } + let bound_def_path = tcx.def_path_str(bound); + if projections_str.is_empty() { + where_clauses.push(format!("{}: {}", ty, bound_def_path)); + } else { + bounds_str.push(format!("{}<{}>", bound_def_path, projections_str.join(", "))); + } + } + if bounds_str.is_empty() { + types_str.push(ty.to_string()); + } else { + types_str.push(format!("{}: {}", ty, bounds_str.join(" + "))); + } + } else { + // Avoid suggesting the following: + // fn foo::Bar>(_: T) where T: Trait, ::Bar: Other {} + where_clauses.extend( + bounds.into_iter().map(|bound| format!("{}: {}", ty, tcx.def_path_str(bound))), + ); + } } + + let generics = + if types_str.is_empty() { "".to_string() } else { format!("<{}>", types_str.join(", ")) }; + let where_clauses = if where_clauses.is_empty() { - String::new() + "".to_string() } else { format!(" where {}", where_clauses.join(", ")) }; + (generics, where_clauses) } diff --git a/tests/ui/suggestions/auxiliary/extern-issue-98562.rs b/tests/ui/suggestions/auxiliary/extern-issue-98562.rs new file mode 100644 index 0000000000000..948e40549c6db --- /dev/null +++ b/tests/ui/suggestions/auxiliary/extern-issue-98562.rs @@ -0,0 +1,26 @@ +pub trait TraitE { + type I3; +} + +pub trait TraitD { + type I3; +} + +pub trait TraitC { + type I1; + type I2; +} + +pub trait TraitB { + type Item; +} + +pub trait TraitA { + fn baz< + U: TraitC + TraitD + TraitE, + V: TraitD + >(_: U, _: V) -> Self + where + U: TraitB, + ::Item: Copy; +} diff --git a/tests/ui/suggestions/issue-98562.rs b/tests/ui/suggestions/issue-98562.rs new file mode 100644 index 0000000000000..de04050d59312 --- /dev/null +++ b/tests/ui/suggestions/issue-98562.rs @@ -0,0 +1,12 @@ +// aux-build:extern-issue-98562.rs + +extern crate extern_issue_98562; +use extern_issue_98562::TraitA; + +struct X; +impl TraitA for X { + //~^ ERROR not all trait items implemented +} +//~^ HELP implement the missing item: `fn baz + TraitD, V: TraitD>(_: U, _: V) -> Self where U: TraitE, U: TraitB, ::Item: Copy { todo!() }` + +fn main() {} diff --git a/tests/ui/suggestions/issue-98562.stderr b/tests/ui/suggestions/issue-98562.stderr new file mode 100644 index 0000000000000..7897fa441a24f --- /dev/null +++ b/tests/ui/suggestions/issue-98562.stderr @@ -0,0 +1,11 @@ +error[E0046]: not all trait items implemented, missing: `baz` + --> $DIR/issue-98562.rs:7:1 + | +LL | impl TraitA for X { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `baz` in implementation + | + = help: implement the missing item: `fn baz + TraitD, V: TraitD>(_: U, _: V) -> Self where U: TraitE, U: TraitB, ::Item: Copy { todo!() }` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0046`. diff --git a/tests/ui/suggestions/missing-assoc-fn.stderr b/tests/ui/suggestions/missing-assoc-fn.stderr index 77fa956287816..84cb6e98553ee 100644 --- a/tests/ui/suggestions/missing-assoc-fn.stderr +++ b/tests/ui/suggestions/missing-assoc-fn.stderr @@ -28,7 +28,7 @@ error[E0046]: not all trait items implemented, missing: `from_iter` LL | impl FromIterator<()> for X { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `from_iter` in implementation | - = help: implement the missing item: `fn from_iter(_: T) -> Self where T: IntoIterator, std::iter::IntoIterator::Item = () { todo!() }` + = help: implement the missing item: `fn from_iter>(_: T) -> Self { todo!() }` error: aborting due to 3 previous errors