From 319dd6f139377259ceca7db35069b382446ee3bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 30 Jan 2020 19:01:31 -0800 Subject: [PATCH] When suggesting associated fn with type parameters, include in the structured suggestion --- src/librustc_typeck/check/mod.rs | 85 ++++++++++++++++++- ...sing-assoc-fn-applicable-suggestions.fixed | 21 +++++ ...missing-assoc-fn-applicable-suggestions.rs | 18 ++++ ...ing-assoc-fn-applicable-suggestions.stderr | 16 ++++ src/test/ui/suggestions/missing-assoc-fn.rs | 22 +++++ .../ui/suggestions/missing-assoc-fn.stderr | 36 ++++++++ 6 files changed, 195 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.fixed create mode 100644 src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.rs create mode 100644 src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.stderr create mode 100644 src/test/ui/suggestions/missing-assoc-fn.rs create mode 100644 src/test/ui/suggestions/missing-assoc-fn.stderr diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index d0275429747b..678f837db96f 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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, Vec> = 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::Bar>(_: T) where T: Trait, ::Bar: Other {} + _ => None, + }) + .collect::>() + .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` + 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() @@ -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. @@ -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. diff --git a/src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.fixed b/src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.fixed new file mode 100644 index 000000000000..00a712e5722a --- /dev/null +++ b/src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.fixed @@ -0,0 +1,21 @@ +// run-rustfix +trait TraitB { + type Item; +} + +trait TraitA { + type Type; + fn bar(_: T) -> Self; + fn baz(_: T) -> Self where T: TraitB, ::Item: Copy; +} + +struct S; +struct Type; + +impl TraitA<()> for S { //~ ERROR not all trait items implemented +fn baz(_: T) -> Self where T: TraitB, ::Item: std::marker::Copy { unimplemented!() } +fn bar(_: T) -> Self { unimplemented!() } +type Type = Type; +} + +fn main() {} diff --git a/src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.rs b/src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.rs new file mode 100644 index 000000000000..c80ede1b2be2 --- /dev/null +++ b/src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.rs @@ -0,0 +1,18 @@ +// run-rustfix +trait TraitB { + type Item; +} + +trait TraitA { + type Type; + fn bar(_: T) -> Self; + fn baz(_: T) -> Self where T: TraitB, ::Item: Copy; +} + +struct S; +struct Type; + +impl TraitA<()> for S { //~ ERROR not all trait items implemented +} + +fn main() {} diff --git a/src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.stderr b/src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.stderr new file mode 100644 index 000000000000..ee29a56f3f8c --- /dev/null +++ b/src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.stderr @@ -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) -> Self; + | ------------------------ `bar` from trait +LL | fn baz(_: T) -> Self where T: 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`. diff --git a/src/test/ui/suggestions/missing-assoc-fn.rs b/src/test/ui/suggestions/missing-assoc-fn.rs new file mode 100644 index 000000000000..9af8e5a939d6 --- /dev/null +++ b/src/test/ui/suggestions/missing-assoc-fn.rs @@ -0,0 +1,22 @@ +trait TraitB { + type Item; +} + +trait TraitA { + fn foo>(_: T) -> Self; + fn bar(_: T) -> Self; + fn baz(_: T) -> Self where T: TraitB, ::Item: Copy; + fn bat>(_: 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() {} diff --git a/src/test/ui/suggestions/missing-assoc-fn.stderr b/src/test/ui/suggestions/missing-assoc-fn.stderr new file mode 100644 index 000000000000..bed8bffe38d7 --- /dev/null +++ b/src/test/ui/suggestions/missing-assoc-fn.stderr @@ -0,0 +1,36 @@ +error[E0658]: associated type bounds are unstable + --> $DIR/missing-assoc-fn.rs:9:22 + | +LL | fn bat>(_: 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) -> Self; + | ------------------------------------------ `foo` from trait +LL | fn bar(_: T) -> Self; + | ------------------------ `bar` from trait +LL | fn baz(_: T) -> Self where T: TraitB, ::Item: Copy; + | ------------------------------------------------------------------- `baz` from trait +LL | fn bat>(_: 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) -> 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`.