Skip to content

Commit

Permalink
When suggesting associated fn with type parameters, include in the st…
Browse files Browse the repository at this point in the history
…ructured suggestion
  • Loading branch information
estebank committed Feb 5, 2020
1 parent 5b0caef commit 319dd6f
Show file tree
Hide file tree
Showing 6 changed files with 195 additions and 3 deletions.
85 changes: 82 additions & 3 deletions src/librustc_typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2157,8 +2157,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 lets 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()
Expand Down Expand Up @@ -2188,12 +2257,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 {}{}({}){}{} {{ unimplemented!() }}",
unsafety, ident, generics, args, output, where_clauses
)
}

/// Return placeholder code for the given associated item.
Expand All @@ -2206,7 +2280,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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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 { unimplemented!() }
fn bar<T>(_: T) -> Self { unimplemented!() }
type Type = Type;
}

fn main() {}
18 changes: 18 additions & 0 deletions src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.rs
Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -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 changes: 22 additions & 0 deletions src/test/ui/suggestions/missing-assoc-fn.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 36 additions & 0 deletions src/test/ui/suggestions/missing-assoc-fn.stderr
Original file line number Diff line number Diff line change
@@ -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 { unimplemented!() }`

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0046, E0658.
For more information about an error, try `rustc --explain E0046`.

0 comments on commit 319dd6f

Please sign in to comment.