Skip to content

Commit

Permalink
fix(suggestion): insert projection to associated types
Browse files Browse the repository at this point in the history
  • Loading branch information
bvanjoi committed Sep 29, 2023
1 parent b8536c1 commit b83dfb5
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 29 deletions.
67 changes: 39 additions & 28 deletions compiler/rustc_hir_analysis/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {}
_ => None,
})
.collect::<Vec<_>>()
.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<Assoc = K>`
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<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as 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)
}

Expand Down
26 changes: 26 additions & 0 deletions tests/ui/suggestions/auxiliary/extern-issue-98562.rs
Original file line number Diff line number Diff line change
@@ -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<G1, G2, G3> {
fn baz<
U: TraitC<I1 = G1, I2 = G2> + TraitD<I3 = G3> + TraitE,
V: TraitD<I3 = G1>
>(_: U, _: V) -> Self
where
U: TraitB,
<U as TraitB>::Item: Copy;
}
12 changes: 12 additions & 0 deletions tests/ui/suggestions/issue-98562.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// aux-build:extern-issue-98562.rs

extern crate extern_issue_98562;
use extern_issue_98562::TraitA;

struct X;
impl TraitA<u8, u16, u32> for X {
//~^ ERROR not all trait items implemented
}
//~^ HELP implement the missing item: `fn baz<U: TraitC<I1 = u8, I2 = u16> + TraitD<I3 = u32>, V: TraitD<I3 = u8>>(_: U, _: V) -> Self where U: TraitE, U: TraitB, <U as TraitB>::Item: Copy { todo!() }`

fn main() {}
11 changes: 11 additions & 0 deletions tests/ui/suggestions/issue-98562.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error[E0046]: not all trait items implemented, missing: `baz`
--> $DIR/issue-98562.rs:7:1
|
LL | impl TraitA<u8, u16, u32> for X {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `baz` in implementation
|
= help: implement the missing item: `fn baz<U: TraitC<I1 = u8, I2 = u16> + TraitD<I3 = u32>, V: TraitD<I3 = u8>>(_: U, _: V) -> Self where U: TraitE, U: TraitB, <U as TraitB>::Item: Copy { todo!() }`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0046`.
2 changes: 1 addition & 1 deletion tests/ui/suggestions/missing-assoc-fn.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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>(_: T) -> Self where T: IntoIterator, std::iter::IntoIterator::Item = () { todo!() }`
= help: implement the missing item: `fn from_iter<T: IntoIterator<Item = ()>>(_: T) -> Self { todo!() }`

error: aborting due to 3 previous errors

Expand Down

0 comments on commit b83dfb5

Please sign in to comment.