Skip to content

Commit 319dd6f

Browse files
committed
When suggesting associated fn with type parameters, include in the structured suggestion
1 parent 5b0caef commit 319dd6f

6 files changed

+195
-3
lines changed

src/librustc_typeck/check/mod.rs

+82-3
Original file line numberDiff line numberDiff line change
@@ -2157,8 +2157,77 @@ fn missing_items_err(
21572157
err.emit();
21582158
}
21592159

2160+
/// Resugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions.
2161+
fn bounds_from_generic_predicates(
2162+
tcx: TyCtxt<'_>,
2163+
predicates: ty::GenericPredicates<'_>,
2164+
) -> (String, String) {
2165+
let mut types: FxHashMap<Ty<'_>, Vec<DefId>> = FxHashMap::default();
2166+
let mut projections = vec![];
2167+
for (predicate, _) in predicates.predicates {
2168+
debug!("predicate {:?}", predicate);
2169+
match predicate {
2170+
ty::Predicate::Trait(trait_predicate, _) => {
2171+
let entry = types.entry(trait_predicate.skip_binder().self_ty()).or_default();
2172+
let def_id = trait_predicate.skip_binder().def_id();
2173+
if Some(def_id) != tcx.lang_items().sized_trait() {
2174+
// Type params are `Sized` by default, do not add that restriction to the list
2175+
// if it is a positive requirement.
2176+
entry.push(trait_predicate.skip_binder().def_id());
2177+
}
2178+
}
2179+
ty::Predicate::Projection(projection_pred) => {
2180+
projections.push(projection_pred);
2181+
}
2182+
_ => {}
2183+
}
2184+
}
2185+
let generics = if types.is_empty() {
2186+
"".to_string()
2187+
} else {
2188+
format!(
2189+
"<{}>",
2190+
types
2191+
.keys()
2192+
.filter_map(|t| match t.kind {
2193+
ty::Param(_) => Some(t.to_string()),
2194+
// Avoid suggesting the following:
2195+
// fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {}
2196+
_ => None,
2197+
})
2198+
.collect::<Vec<_>>()
2199+
.join(", ")
2200+
)
2201+
};
2202+
let mut where_clauses = vec![];
2203+
for (ty, bounds) in types {
2204+
for bound in &bounds {
2205+
where_clauses.push(format!("{}: {}", ty, tcx.def_path_str(*bound)));
2206+
}
2207+
}
2208+
for projection in &projections {
2209+
let p = projection.skip_binder();
2210+
// FIXME: this is not currently supported syntax, we should be looking at the `types` and
2211+
// insert the associated types where they correspond, but for now lets be "lazy" and
2212+
// propose this instead of the following valid resugaring:
2213+
// `T: Trait, Trait::Assoc = K` → `T: Trait<Assoc = K>`
2214+
where_clauses.push(format!("{} = {}", tcx.def_path_str(p.projection_ty.item_def_id), p.ty));
2215+
}
2216+
let where_clauses = if where_clauses.is_empty() {
2217+
String::new()
2218+
} else {
2219+
format!(" where {}", where_clauses.join(", "))
2220+
};
2221+
(generics, where_clauses)
2222+
}
2223+
21602224
/// Return placeholder code for the given function.
2161-
fn fn_sig_suggestion(sig: &ty::FnSig<'_>, ident: Ident) -> String {
2225+
fn fn_sig_suggestion(
2226+
tcx: TyCtxt<'_>,
2227+
sig: &ty::FnSig<'_>,
2228+
ident: Ident,
2229+
predicates: ty::GenericPredicates<'_>,
2230+
) -> String {
21622231
let args = sig
21632232
.inputs()
21642233
.iter()
@@ -2188,12 +2257,17 @@ fn fn_sig_suggestion(sig: &ty::FnSig<'_>, ident: Ident) -> String {
21882257
let output = if !output.is_unit() { format!(" -> {:?}", output) } else { String::new() };
21892258

21902259
let unsafety = sig.unsafety.prefix_str();
2260+
let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates);
2261+
21912262
// FIXME: this is not entirely correct, as the lifetimes from borrowed params will
21922263
// not be present in the `fn` definition, not will we account for renamed
21932264
// lifetimes between the `impl` and the `trait`, but this should be good enough to
21942265
// fill in a significant portion of the missing code, and other subsequent
21952266
// suggestions can help the user fix the code.
2196-
format!("{}fn {}({}){} {{ unimplemented!() }}", unsafety, ident, args, output)
2267+
format!(
2268+
"{}fn {}{}({}){}{} {{ unimplemented!() }}",
2269+
unsafety, ident, generics, args, output, where_clauses
2270+
)
21972271
}
21982272

21992273
/// Return placeholder code for the given associated item.
@@ -2206,7 +2280,12 @@ fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String {
22062280
// late-bound regions, and we don't want method signatures to show up
22072281
// `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
22082282
// regions just fine, showing `fn(&MyType)`.
2209-
fn_sig_suggestion(tcx.fn_sig(assoc.def_id).skip_binder(), assoc.ident)
2283+
fn_sig_suggestion(
2284+
tcx,
2285+
tcx.fn_sig(assoc.def_id).skip_binder(),
2286+
assoc.ident,
2287+
tcx.predicates_of(assoc.def_id),
2288+
)
22102289
}
22112290
ty::AssocKind::Type => format!("type {} = Type;", assoc.ident),
22122291
// FIXME(type_alias_impl_trait): we should print bounds here too.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// run-rustfix
2+
trait TraitB {
3+
type Item;
4+
}
5+
6+
trait TraitA<A> {
7+
type Type;
8+
fn bar<T>(_: T) -> Self;
9+
fn baz<T>(_: T) -> Self where T: TraitB, <T as TraitB>::Item: Copy;
10+
}
11+
12+
struct S;
13+
struct Type;
14+
15+
impl TraitA<()> for S { //~ ERROR not all trait items implemented
16+
fn baz<T>(_: T) -> Self where T: TraitB, <T as TraitB>::Item: std::marker::Copy { unimplemented!() }
17+
fn bar<T>(_: T) -> Self { unimplemented!() }
18+
type Type = Type;
19+
}
20+
21+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// run-rustfix
2+
trait TraitB {
3+
type Item;
4+
}
5+
6+
trait TraitA<A> {
7+
type Type;
8+
fn bar<T>(_: T) -> Self;
9+
fn baz<T>(_: T) -> Self where T: TraitB, <T as TraitB>::Item: Copy;
10+
}
11+
12+
struct S;
13+
struct Type;
14+
15+
impl TraitA<()> for S { //~ ERROR not all trait items implemented
16+
}
17+
18+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
error[E0046]: not all trait items implemented, missing: `Type`, `bar`, `baz`
2+
--> $DIR/missing-assoc-fn-applicable-suggestions.rs:15:1
3+
|
4+
LL | type Type;
5+
| ---------- `Type` from trait
6+
LL | fn bar<T>(_: T) -> Self;
7+
| ------------------------ `bar` from trait
8+
LL | fn baz<T>(_: T) -> Self where T: TraitB, <T as TraitB>::Item: Copy;
9+
| ------------------------------------------------------------------- `baz` from trait
10+
...
11+
LL | impl TraitA<()> for S {
12+
| ^^^^^^^^^^^^^^^^^^^^^ missing `Type`, `bar`, `baz` in implementation
13+
14+
error: aborting due to previous error
15+
16+
For more information about this error, try `rustc --explain E0046`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
trait TraitB {
2+
type Item;
3+
}
4+
5+
trait TraitA<A> {
6+
fn foo<T: TraitB<Item = A>>(_: T) -> Self;
7+
fn bar<T>(_: T) -> Self;
8+
fn baz<T>(_: T) -> Self where T: TraitB, <T as TraitB>::Item: Copy;
9+
fn bat<T: TraitB<Item: Copy>>(_: T) -> Self; //~ ERROR associated type bounds are unstable
10+
}
11+
12+
struct S;
13+
14+
impl TraitA<()> for S { //~ ERROR not all trait items implemented
15+
}
16+
17+
use std::iter::FromIterator;
18+
struct X;
19+
impl FromIterator<()> for X { //~ ERROR not all trait items implemented
20+
}
21+
22+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
error[E0658]: associated type bounds are unstable
2+
--> $DIR/missing-assoc-fn.rs:9:22
3+
|
4+
LL | fn bat<T: TraitB<Item: Copy>>(_: T) -> Self;
5+
| ^^^^^^^^^^
6+
|
7+
= note: for more information, see https://github.com/rust-lang/rust/issues/52662
8+
= help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
9+
10+
error[E0046]: not all trait items implemented, missing: `foo`, `bar`, `baz`, `bat`
11+
--> $DIR/missing-assoc-fn.rs:14:1
12+
|
13+
LL | fn foo<T: TraitB<Item = A>>(_: T) -> Self;
14+
| ------------------------------------------ `foo` from trait
15+
LL | fn bar<T>(_: T) -> Self;
16+
| ------------------------ `bar` from trait
17+
LL | fn baz<T>(_: T) -> Self where T: TraitB, <T as TraitB>::Item: Copy;
18+
| ------------------------------------------------------------------- `baz` from trait
19+
LL | fn bat<T: TraitB<Item: Copy>>(_: T) -> Self;
20+
| -------------------------------------------- `bat` from trait
21+
...
22+
LL | impl TraitA<()> for S {
23+
| ^^^^^^^^^^^^^^^^^^^^^ missing `foo`, `bar`, `baz`, `bat` in implementation
24+
25+
error[E0046]: not all trait items implemented, missing: `from_iter`
26+
--> $DIR/missing-assoc-fn.rs:19:1
27+
|
28+
LL | impl FromIterator<()> for X {
29+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `from_iter` in implementation
30+
|
31+
= help: implement the missing item: `fn from_iter<T>(_: T) -> Self where T: std::iter::IntoIterator, std::iter::IntoIterator::Item = A { unimplemented!() }`
32+
33+
error: aborting due to 3 previous errors
34+
35+
Some errors have detailed explanations: E0046, E0658.
36+
For more information about an error, try `rustc --explain E0046`.

0 commit comments

Comments
 (0)