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:
commit
64ea639c12
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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() {}
|
@ -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() {}
|
@ -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`.
|
22
src/test/ui/suggestions/missing-assoc-fn.rs
Normal file
22
src/test/ui/suggestions/missing-assoc-fn.rs
Normal 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() {}
|
36
src/test/ui/suggestions/missing-assoc-fn.stderr
Normal file
36
src/test/ui/suggestions/missing-assoc-fn.stderr
Normal 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`.
|
@ -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!() }
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user