Skip to content

Commit 64ea639

Browse files
committed
Auto merge of #68689 - estebank:where-clause-sugg-missing-fn, r=varkor
When suggesting associated fn with type parameters, include in the structured suggestion Address #50734. ``` error[E0046]: not all trait items implemented, missing: `foo`, `bar`, `baz` --> file.rs:14:1 | 14 | impl TraitA<()> for S { | ^^^^^^^^^^^^^^^^^^^^^ missing `foo`, `bar`, `baz` in implementation | = help: implement the missing item: `fn foo<T>(_: T) -> Self where T: TraitB, TraitB::Item = A { unimplemented!() }` = help: implement the missing item: `fn bar<T>(_: T) -> Self { unimplemented!() }` = help: implement the missing item: `fn baz<T>(_: T) -> Self where T: TraitB, <T as TraitB>::Item: std::marker::Copy { unimplemented!() }` ``` It doesn't work well for associated types with `ty::Predicate::Projection`s as we need to resugar `T: Trait, Trait::Assoc = K` → `T: Trait<Assoc = K>`.
2 parents f8d830b + 3cdd7ae commit 64ea639

11 files changed

+203
-11
lines changed

src/librustc_typeck/check/mod.rs

+82-3
Original file line numberDiff line numberDiff line change
@@ -2167,8 +2167,77 @@ fn missing_items_err(
21672167
err.emit();
21682168
}
21692169

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

22002269
let unsafety = sig.unsafety.prefix_str();
2270+
let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates);
2271+
22012272
// FIXME: this is not entirely correct, as the lifetimes from borrowed params will
22022273
// not be present in the `fn` definition, not will we account for renamed
22032274
// lifetimes between the `impl` and the `trait`, but this should be good enough to
22042275
// fill in a significant portion of the missing code, and other subsequent
22052276
// suggestions can help the user fix the code.
2206-
format!("{}fn {}({}){} {{ unimplemented!() }}", unsafety, ident, args, output)
2277+
format!(
2278+
"{}fn {}{}({}){}{} {{ todo!() }}",
2279+
unsafety, ident, generics, args, output, where_clauses
2280+
)
22072281
}
22082282

22092283
/// Return placeholder code for the given associated item.
@@ -2216,7 +2290,12 @@ fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String {
22162290
// late-bound regions, and we don't want method signatures to show up
22172291
// `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
22182292
// regions just fine, showing `fn(&MyType)`.
2219-
fn_sig_suggestion(tcx.fn_sig(assoc.def_id).skip_binder(), assoc.ident)
2293+
fn_sig_suggestion(
2294+
tcx,
2295+
tcx.fn_sig(assoc.def_id).skip_binder(),
2296+
assoc.ident,
2297+
tcx.predicates_of(assoc.def_id),
2298+
)
22202299
}
22212300
ty::AssocKind::Type => format!("type {} = Type;", assoc.ident),
22222301
// FIXME(type_alias_impl_trait): we should print bounds here too.

src/test/ui/impl-trait/trait_type.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ error[E0046]: not all trait items implemented, missing: `fmt`
2929
LL | impl std::fmt::Display for MyType4 {}
3030
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `fmt` in implementation
3131
|
32-
= help: implement the missing item: `fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { unimplemented!() }`
32+
= help: implement the missing item: `fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { todo!() }`
3333

3434
error: aborting due to 4 previous errors
3535

src/test/ui/issues/issue-3344.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0046]: not all trait items implemented, missing: `partial_cmp`
44
LL | impl PartialOrd for Thing {
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^ missing `partial_cmp` in implementation
66
|
7-
= help: implement the missing item: `fn partial_cmp(&self, _: &Rhs) -> std::option::Option<std::cmp::Ordering> { unimplemented!() }`
7+
= help: implement the missing item: `fn partial_cmp(&self, _: &Rhs) -> std::option::Option<std::cmp::Ordering> { todo!() }`
88

99
error: aborting due to previous error
1010

src/test/ui/missing/missing-items/m2.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | impl m1::X for X {
66
|
77
= help: implement the missing item: `const CONSTANT: u32 = 42;`
88
= help: implement the missing item: `type Type = Type;`
9-
= help: implement the missing item: `fn method(&self, _: std::string::String) -> <Self as m1::X>::Type { unimplemented!() }`
9+
= help: implement the missing item: `fn method(&self, _: std::string::String) -> <Self as m1::X>::Type { todo!() }`
1010

1111
error: aborting due to previous error
1212

src/test/ui/span/impl-wrong-item-for-trait.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ error[E0046]: not all trait items implemented, missing: `fmt`
6464
LL | impl Debug for FooTypeForMethod {
6565
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `fmt` in implementation
6666
|
67-
= help: implement the missing item: `fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { unimplemented!() }`
67+
= help: implement the missing item: `fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { todo!() }`
6868

6969
error: aborting due to 8 previous errors
7070

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 { todo!() }
17+
fn bar<T>(_: T) -> Self { todo!() }
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 { todo!() }`
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`.

src/test/ui/suggestions/missing-trait-item.fixed

+4-4
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ trait T {
77

88
mod foo {
99
use super::T;
10-
impl T for () { fn bar(&self, _: &usize, _: &usize) -> usize { unimplemented!() }
11-
unsafe fn foo(_: &usize, _: &usize) -> usize { unimplemented!() }
10+
impl T for () { fn bar(&self, _: &usize, _: &usize) -> usize { todo!() }
11+
unsafe fn foo(_: &usize, _: &usize) -> usize { todo!() }
1212
} //~ ERROR not all trait items
1313

1414
impl T for usize { //~ ERROR not all trait items
15-
fn bar(&self, _: &usize, _: &usize) -> usize { unimplemented!() }
16-
unsafe fn foo(_: &usize, _: &usize) -> usize { unimplemented!() }
15+
fn bar(&self, _: &usize, _: &usize) -> usize { todo!() }
16+
unsafe fn foo(_: &usize, _: &usize) -> usize { todo!() }
1717
}
1818
}
1919

0 commit comments

Comments
 (0)