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>`.
This commit is contained in:
bors 2020-02-09 07:11:56 +00:00
commit 64ea639c12
11 changed files with 203 additions and 11 deletions

View File

@ -2167,8 +2167,77 @@ fn missing_items_err(
err.emit();
}
/// Resugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions.
fn bounds_from_generic_predicates(
tcx: TyCtxt<'_>,
predicates: ty::GenericPredicates<'_>,
) -> (String, String) {
let mut types: FxHashMap<Ty<'_>, Vec<DefId>> = FxHashMap::default();
let mut projections = vec![];
for (predicate, _) in predicates.predicates {
debug!("predicate {:?}", predicate);
match predicate {
ty::Predicate::Trait(trait_predicate, _) => {
let entry = types.entry(trait_predicate.skip_binder().self_ty()).or_default();
let def_id = trait_predicate.skip_binder().def_id();
if Some(def_id) != tcx.lang_items().sized_trait() {
// Type params are `Sized` by default, do not add that restriction to the list
// if it is a positive requirement.
entry.push(trait_predicate.skip_binder().def_id());
}
}
ty::Predicate::Projection(projection_pred) => {
projections.push(projection_pred);
}
_ => {}
}
}
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![];
for (ty, bounds) in types {
for bound in &bounds {
where_clauses.push(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.item_def_id), p.ty));
}
let where_clauses = if where_clauses.is_empty() {
String::new()
} else {
format!(" where {}", where_clauses.join(", "))
};
(generics, where_clauses)
}
/// Return placeholder code for the given function.
fn fn_sig_suggestion(sig: &ty::FnSig<'_>, ident: Ident) -> String {
fn fn_sig_suggestion(
tcx: TyCtxt<'_>,
sig: &ty::FnSig<'_>,
ident: Ident,
predicates: ty::GenericPredicates<'_>,
) -> String {
let args = sig
.inputs()
.iter()
@ -2198,12 +2267,17 @@ fn fn_sig_suggestion(sig: &ty::FnSig<'_>, ident: Ident) -> String {
let output = if !output.is_unit() { format!(" -> {:?}", output) } else { String::new() };
let unsafety = sig.unsafety.prefix_str();
let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates);
// FIXME: this is not entirely correct, as the lifetimes from borrowed params will
// not be present in the `fn` definition, not will we account for renamed
// lifetimes between the `impl` and the `trait`, but this should be good enough to
// fill in a significant portion of the missing code, and other subsequent
// suggestions can help the user fix the code.
format!("{}fn {}({}){} {{ unimplemented!() }}", unsafety, ident, args, output)
format!(
"{}fn {}{}({}){}{} {{ todo!() }}",
unsafety, ident, generics, args, output, where_clauses
)
}
/// Return placeholder code for the given associated item.
@ -2216,7 +2290,12 @@ fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String {
// late-bound regions, and we don't want method signatures to show up
// `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
// regions just fine, showing `fn(&MyType)`.
fn_sig_suggestion(tcx.fn_sig(assoc.def_id).skip_binder(), assoc.ident)
fn_sig_suggestion(
tcx,
tcx.fn_sig(assoc.def_id).skip_binder(),
assoc.ident,
tcx.predicates_of(assoc.def_id),
)
}
ty::AssocKind::Type => format!("type {} = Type;", assoc.ident),
// FIXME(type_alias_impl_trait): we should print bounds here too.

View File

@ -29,7 +29,7 @@ error[E0046]: not all trait items implemented, missing: `fmt`
LL | impl std::fmt::Display for MyType4 {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `fmt` in implementation
|
= help: implement the missing item: `fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { unimplemented!() }`
= help: implement the missing item: `fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { todo!() }`
error: aborting due to 4 previous errors

View File

@ -4,7 +4,7 @@ error[E0046]: not all trait items implemented, missing: `partial_cmp`
LL | impl PartialOrd for Thing {
| ^^^^^^^^^^^^^^^^^^^^^^^^^ missing `partial_cmp` in implementation
|
= help: implement the missing item: `fn partial_cmp(&self, _: &Rhs) -> std::option::Option<std::cmp::Ordering> { unimplemented!() }`
= help: implement the missing item: `fn partial_cmp(&self, _: &Rhs) -> std::option::Option<std::cmp::Ordering> { todo!() }`
error: aborting due to previous error

View File

@ -6,7 +6,7 @@ LL | impl m1::X for X {
|
= help: implement the missing item: `const CONSTANT: u32 = 42;`
= help: implement the missing item: `type Type = Type;`
= help: implement the missing item: `fn method(&self, _: std::string::String) -> <Self as m1::X>::Type { unimplemented!() }`
= help: implement the missing item: `fn method(&self, _: std::string::String) -> <Self as m1::X>::Type { todo!() }`
error: aborting due to previous error

View File

@ -64,7 +64,7 @@ error[E0046]: not all trait items implemented, missing: `fmt`
LL | impl Debug for FooTypeForMethod {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `fmt` in implementation
|
= help: implement the missing item: `fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { unimplemented!() }`
= help: implement the missing item: `fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { todo!() }`
error: aborting due to 8 previous errors

View File

@ -0,0 +1,21 @@
// run-rustfix
trait TraitB {
type Item;
}
trait TraitA<A> {
type Type;
fn bar<T>(_: T) -> Self;
fn baz<T>(_: T) -> Self where T: TraitB, <T as TraitB>::Item: Copy;
}
struct S;
struct Type;
impl TraitA<()> for S { //~ ERROR not all trait items implemented
fn baz<T>(_: T) -> Self where T: TraitB, <T as TraitB>::Item: std::marker::Copy { todo!() }
fn bar<T>(_: T) -> Self { todo!() }
type Type = Type;
}
fn main() {}

View File

@ -0,0 +1,18 @@
// run-rustfix
trait TraitB {
type Item;
}
trait TraitA<A> {
type Type;
fn bar<T>(_: T) -> Self;
fn baz<T>(_: T) -> Self where T: TraitB, <T as TraitB>::Item: Copy;
}
struct S;
struct Type;
impl TraitA<()> for S { //~ ERROR not all trait items implemented
}
fn main() {}

View File

@ -0,0 +1,16 @@
error[E0046]: not all trait items implemented, missing: `Type`, `bar`, `baz`
--> $DIR/missing-assoc-fn-applicable-suggestions.rs:15:1
|
LL | type Type;
| ---------- `Type` from trait
LL | fn bar<T>(_: T) -> Self;
| ------------------------ `bar` from trait
LL | fn baz<T>(_: T) -> Self where T: TraitB, <T as TraitB>::Item: Copy;
| ------------------------------------------------------------------- `baz` from trait
...
LL | impl TraitA<()> for S {
| ^^^^^^^^^^^^^^^^^^^^^ missing `Type`, `bar`, `baz` in implementation
error: aborting due to previous error
For more information about this error, try `rustc --explain E0046`.

View File

@ -0,0 +1,22 @@
trait TraitB {
type Item;
}
trait TraitA<A> {
fn foo<T: TraitB<Item = A>>(_: T) -> Self;
fn bar<T>(_: T) -> Self;
fn baz<T>(_: T) -> Self where T: TraitB, <T as TraitB>::Item: Copy;
fn bat<T: TraitB<Item: Copy>>(_: T) -> Self; //~ ERROR associated type bounds are unstable
}
struct S;
impl TraitA<()> for S { //~ ERROR not all trait items implemented
}
use std::iter::FromIterator;
struct X;
impl FromIterator<()> for X { //~ ERROR not all trait items implemented
}
fn main() {}

View File

@ -0,0 +1,36 @@
error[E0658]: associated type bounds are unstable
--> $DIR/missing-assoc-fn.rs:9:22
|
LL | fn bat<T: TraitB<Item: Copy>>(_: T) -> Self;
| ^^^^^^^^^^
|
= note: for more information, see https://github.com/rust-lang/rust/issues/52662
= help: add `#![feature(associated_type_bounds)]` to the crate attributes to enable
error[E0046]: not all trait items implemented, missing: `foo`, `bar`, `baz`, `bat`
--> $DIR/missing-assoc-fn.rs:14:1
|
LL | fn foo<T: TraitB<Item = A>>(_: T) -> Self;
| ------------------------------------------ `foo` from trait
LL | fn bar<T>(_: T) -> Self;
| ------------------------ `bar` from trait
LL | fn baz<T>(_: T) -> Self where T: TraitB, <T as TraitB>::Item: Copy;
| ------------------------------------------------------------------- `baz` from trait
LL | fn bat<T: TraitB<Item: Copy>>(_: T) -> Self;
| -------------------------------------------- `bat` from trait
...
LL | impl TraitA<()> for S {
| ^^^^^^^^^^^^^^^^^^^^^ missing `foo`, `bar`, `baz`, `bat` in implementation
error[E0046]: not all trait items implemented, missing: `from_iter`
--> $DIR/missing-assoc-fn.rs:19:1
|
LL | impl FromIterator<()> for X {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `from_iter` in implementation
|
= help: implement the missing item: `fn from_iter<T>(_: T) -> Self where T: std::iter::IntoIterator, std::iter::IntoIterator::Item = A { todo!() }`
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0046, E0658.
For more information about an error, try `rustc --explain E0046`.

View File

@ -7,13 +7,13 @@ trait T {
mod foo {
use super::T;
impl T for () { fn bar(&self, _: &usize, _: &usize) -> usize { unimplemented!() }
unsafe fn foo(_: &usize, _: &usize) -> usize { unimplemented!() }
impl T for () { fn bar(&self, _: &usize, _: &usize) -> usize { todo!() }
unsafe fn foo(_: &usize, _: &usize) -> usize { todo!() }
} //~ ERROR not all trait items
impl T for usize { //~ ERROR not all trait items
fn bar(&self, _: &usize, _: &usize) -> usize { unimplemented!() }
unsafe fn foo(_: &usize, _: &usize) -> usize { unimplemented!() }
fn bar(&self, _: &usize, _: &usize) -> usize { todo!() }
unsafe fn foo(_: &usize, _: &usize) -> usize { todo!() }
}
}