Skip to content

Commit 57fa0fa

Browse files
committed
Auto merge of rust-lang#112123 - bvanjoi:fix-98562, r=compiler-errors
fix(suggestion): insert projection to associated types Fixes rust-lang#98562 This PR has fixed some help suggestions for unsupported syntax, such as `fn f<T>(_:T) where T: IntoIterator, std::iter::IntoIterator::Item = () {}` to `fn f<T: IntoIterator<Item = ()>>(_T) {}`.
2 parents 7b4d9e1 + 0341fcd commit 57fa0fa

File tree

5 files changed

+89
-29
lines changed

5 files changed

+89
-29
lines changed

compiler/rustc_hir_analysis/src/check/mod.rs

+39-28
Original file line numberDiff line numberDiff line change
@@ -329,41 +329,52 @@ fn bounds_from_generic_predicates<'tcx>(
329329
_ => {}
330330
}
331331
}
332-
let generics = if types.is_empty() {
333-
"".to_string()
334-
} else {
335-
format!(
336-
"<{}>",
337-
types
338-
.keys()
339-
.filter_map(|t| match t.kind() {
340-
ty::Param(_) => Some(t.to_string()),
341-
// Avoid suggesting the following:
342-
// fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {}
343-
_ => None,
344-
})
345-
.collect::<Vec<_>>()
346-
.join(", ")
347-
)
348-
};
332+
349333
let mut where_clauses = vec![];
334+
let mut types_str = vec![];
350335
for (ty, bounds) in types {
351-
where_clauses
352-
.extend(bounds.into_iter().map(|bound| format!("{}: {}", ty, tcx.def_path_str(bound))));
353-
}
354-
for projection in &projections {
355-
let p = projection.skip_binder();
356-
// FIXME: this is not currently supported syntax, we should be looking at the `types` and
357-
// insert the associated types where they correspond, but for now let's be "lazy" and
358-
// propose this instead of the following valid resugaring:
359-
// `T: Trait, Trait::Assoc = K` → `T: Trait<Assoc = K>`
360-
where_clauses.push(format!("{} = {}", tcx.def_path_str(p.projection_ty.def_id), p.term));
336+
if let ty::Param(_) = ty.kind() {
337+
let mut bounds_str = vec![];
338+
for bound in bounds {
339+
let mut projections_str = vec![];
340+
for projection in &projections {
341+
let p = projection.skip_binder();
342+
let alias_ty = p.projection_ty;
343+
if bound == tcx.parent(alias_ty.def_id) && alias_ty.self_ty() == ty {
344+
let name = tcx.item_name(alias_ty.def_id);
345+
projections_str.push(format!("{} = {}", name, p.term));
346+
}
347+
}
348+
let bound_def_path = tcx.def_path_str(bound);
349+
if projections_str.is_empty() {
350+
where_clauses.push(format!("{}: {}", ty, bound_def_path));
351+
} else {
352+
bounds_str.push(format!("{}<{}>", bound_def_path, projections_str.join(", ")));
353+
}
354+
}
355+
if bounds_str.is_empty() {
356+
types_str.push(ty.to_string());
357+
} else {
358+
types_str.push(format!("{}: {}", ty, bounds_str.join(" + ")));
359+
}
360+
} else {
361+
// Avoid suggesting the following:
362+
// fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {}
363+
where_clauses.extend(
364+
bounds.into_iter().map(|bound| format!("{}: {}", ty, tcx.def_path_str(bound))),
365+
);
366+
}
361367
}
368+
369+
let generics =
370+
if types_str.is_empty() { "".to_string() } else { format!("<{}>", types_str.join(", ")) };
371+
362372
let where_clauses = if where_clauses.is_empty() {
363-
String::new()
373+
"".to_string()
364374
} else {
365375
format!(" where {}", where_clauses.join(", "))
366376
};
377+
367378
(generics, where_clauses)
368379
}
369380

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
pub trait TraitE {
2+
type I3;
3+
}
4+
5+
pub trait TraitD {
6+
type I3;
7+
}
8+
9+
pub trait TraitC {
10+
type I1;
11+
type I2;
12+
}
13+
14+
pub trait TraitB {
15+
type Item;
16+
}
17+
18+
pub trait TraitA<G1, G2, G3> {
19+
fn baz<
20+
U: TraitC<I1 = G1, I2 = G2> + TraitD<I3 = G3> + TraitE,
21+
V: TraitD<I3 = G1>
22+
>(_: U, _: V) -> Self
23+
where
24+
U: TraitB,
25+
<U as TraitB>::Item: Copy;
26+
}

tests/ui/suggestions/issue-98562.rs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// aux-build:extern-issue-98562.rs
2+
3+
extern crate extern_issue_98562;
4+
use extern_issue_98562::TraitA;
5+
6+
struct X;
7+
impl TraitA<u8, u16, u32> for X {
8+
//~^ ERROR not all trait items implemented
9+
}
10+
//~^ 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!() }`
11+
12+
fn main() {}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0046]: not all trait items implemented, missing: `baz`
2+
--> $DIR/issue-98562.rs:7:1
3+
|
4+
LL | impl TraitA<u8, u16, u32> for X {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `baz` in implementation
6+
|
7+
= 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!() }`
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0046`.

tests/ui/suggestions/missing-assoc-fn.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ error[E0046]: not all trait items implemented, missing: `from_iter`
2828
LL | impl FromIterator<()> for X {
2929
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `from_iter` in implementation
3030
|
31-
= help: implement the missing item: `fn from_iter<T>(_: T) -> Self where T: IntoIterator, std::iter::IntoIterator::Item = () { todo!() }`
31+
= help: implement the missing item: `fn from_iter<T: IntoIterator<Item = ()>>(_: T) -> Self { todo!() }`
3232

3333
error: aborting due to 3 previous errors
3434

0 commit comments

Comments
 (0)