]> git.lizzy.rs Git - rust.git/commitdiff
When suggesting associated fn with type parameters, include in the structured suggestion
authorEsteban Küber <esteban@kuber.com.ar>
Fri, 31 Jan 2020 03:01:31 +0000 (19:01 -0800)
committerEsteban Küber <esteban@kuber.com.ar>
Wed, 5 Feb 2020 02:20:10 +0000 (18:20 -0800)
src/librustc_typeck/check/mod.rs
src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.fixed [new file with mode: 0644]
src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.rs [new file with mode: 0644]
src/test/ui/suggestions/missing-assoc-fn-applicable-suggestions.stderr [new file with mode: 0644]
src/test/ui/suggestions/missing-assoc-fn.rs [new file with mode: 0644]
src/test/ui/suggestions/missing-assoc-fn.stderr [new file with mode: 0644]

index d0275429747b60f00b10fc1c1eadfaee47a41bd0..678f837db96fd6b2113ee242b8a87866b1f4c704 100644 (file)
@@ -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()
@@ -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 (file)
index 0000000..00a712e
--- /dev/null
@@ -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() {}
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 (file)
index 0000000..c80ede1
--- /dev/null
@@ -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() {}
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 (file)
index 0000000..ee29a56
--- /dev/null
@@ -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`.
diff --git a/src/test/ui/suggestions/missing-assoc-fn.rs b/src/test/ui/suggestions/missing-assoc-fn.rs
new file mode 100644 (file)
index 0000000..9af8e5a
--- /dev/null
@@ -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() {}
diff --git a/src/test/ui/suggestions/missing-assoc-fn.stderr b/src/test/ui/suggestions/missing-assoc-fn.stderr
new file mode 100644 (file)
index 0000000..bed8bff
--- /dev/null
@@ -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`.