diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index d0275429747b6..60c6da64c5724 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 let's 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 {}{}({}){}{} {{ todo!() }}", + 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/impl-trait/trait_type.stderr b/src/test/ui/impl-trait/trait_type.stderr index d95b62e469e8c..748bc639a03c2 100644 --- a/src/test/ui/impl-trait/trait_type.stderr +++ b/src/test/ui/impl-trait/trait_type.stderr @@ -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 diff --git a/src/test/ui/issues/issue-3344.stderr b/src/test/ui/issues/issue-3344.stderr index 271fbb6c87426..723e03d452f30 100644 --- a/src/test/ui/issues/issue-3344.stderr +++ b/src/test/ui/issues/issue-3344.stderr @@ -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 { unimplemented!() }` + = help: implement the missing item: `fn partial_cmp(&self, _: &Rhs) -> std::option::Option { todo!() }` error: aborting due to previous error diff --git a/src/test/ui/missing/missing-items/m2.stderr b/src/test/ui/missing/missing-items/m2.stderr index f8243528d72cf..094782099f6ed 100644 --- a/src/test/ui/missing/missing-items/m2.stderr +++ b/src/test/ui/missing/missing-items/m2.stderr @@ -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) -> ::Type { unimplemented!() }` + = help: implement the missing item: `fn method(&self, _: std::string::String) -> ::Type { todo!() }` error: aborting due to previous error diff --git a/src/test/ui/span/impl-wrong-item-for-trait.stderr b/src/test/ui/span/impl-wrong-item-for-trait.stderr index f23f421edc7c5..cda191522a05f 100644 --- a/src/test/ui/span/impl-wrong-item-for-trait.stderr +++ b/src/test/ui/span/impl-wrong-item-for-trait.stderr @@ -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 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 0000000000000..64a1aeae3e040 --- /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 { todo!() } +fn bar(_: T) -> Self { todo!() } +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 0000000000000..c80ede1b2be23 --- /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 0000000000000..ee29a56f3f8c5 --- /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 0000000000000..9af8e5a939d65 --- /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 0000000000000..e4183a45d272a --- /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 { 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`. diff --git a/src/test/ui/suggestions/missing-trait-item.fixed b/src/test/ui/suggestions/missing-trait-item.fixed index 42f579a665e5f..a1cf359ec9124 100644 --- a/src/test/ui/suggestions/missing-trait-item.fixed +++ b/src/test/ui/suggestions/missing-trait-item.fixed @@ -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!() } } }