Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 46 additions & 27 deletions compiler/rustc_hir_analysis/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,9 @@ use rustc_infer::traits::ObligationCause;
use rustc_middle::query::Providers;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::print::with_types_for_signature;
use rustc_middle::ty::{self, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypingMode};
use rustc_middle::ty::{
self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt, TypingMode,
};
use rustc_middle::{bug, span_bug};
use rustc_session::parse::feature_err;
use rustc_span::def_id::CRATE_DEF_ID;
Expand Down Expand Up @@ -232,8 +234,7 @@ fn missing_items_err(
};

// Obtain the level of indentation ending in `sugg_sp`.
let padding =
tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(|| String::new());
let padding = tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(String::new);
let (mut missing_trait_item, mut missing_trait_item_none, mut missing_trait_item_label) =
(Vec::new(), Vec::new(), Vec::new());

Expand Down Expand Up @@ -330,6 +331,7 @@ fn default_body_is_unstable(
fn bounds_from_generic_predicates<'tcx>(
tcx: TyCtxt<'tcx>,
predicates: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>,
assoc: ty::AssocItem,
) -> (String, String) {
let mut types: FxIndexMap<Ty<'tcx>, Vec<DefId>> = FxIndexMap::default();
let mut projections = vec![];
Expand All @@ -353,34 +355,50 @@ fn bounds_from_generic_predicates<'tcx>(
}

let mut where_clauses = vec![];
let mut types_str = vec![];
for (ty, bounds) in types {
if let ty::Param(_) = ty.kind() {
let mut bounds_str = vec![];
for bound in bounds {
let mut projections_str = vec![];
for projection in &projections {
let p = projection.skip_binder();
if bound == tcx.parent(p.projection_term.def_id)
&& p.projection_term.self_ty() == ty
{
let name = tcx.item_name(p.projection_term.def_id);
projections_str.push(format!("{} = {}", name, p.term));
let generics = tcx.generics_of(assoc.def_id);
let types_str = generics
.own_params
.iter()
.filter(|p| matches!(p.kind, GenericParamDefKind::Type { synthetic: false, .. }))
.map(|p| {
// we just checked that it's a type, so the unwrap can't fail
let ty = tcx.mk_param_from_def(p).as_type().unwrap();
if let Some(bounds) = types.get(&ty) {
let mut bounds_str = vec![];
for bound in bounds.iter().copied() {
let mut projections_str = vec![];
for projection in &projections {
let p = projection.skip_binder();
if bound == tcx.parent(p.projection_term.def_id)
&& p.projection_term.self_ty() == ty
{
let name = tcx.item_name(p.projection_term.def_id);
projections_str.push(format!("{} = {}", name, p.term));
}
}
let bound_def_path = tcx.def_path_str(bound);
if projections_str.is_empty() {
where_clauses.push(format!("{}: {}", ty, bound_def_path));
} else {
bounds_str.push(format!(
"{}<{}>",
bound_def_path,
projections_str.join(", ")
));
}
}
let bound_def_path = tcx.def_path_str(bound);
if projections_str.is_empty() {
where_clauses.push(format!("{}: {}", ty, bound_def_path));
if bounds_str.is_empty() {
ty.to_string()
} else {
bounds_str.push(format!("{}<{}>", bound_def_path, projections_str.join(", ")));
format!("{}: {}", ty, bounds_str.join(" + "))
}
}
if bounds_str.is_empty() {
types_str.push(ty.to_string());
} else {
types_str.push(format!("{}: {}", ty, bounds_str.join(" + ")));
ty.to_string()
}
} else {
})
.collect::<Vec<_>>();
for (ty, bounds) in types.into_iter() {
if !matches!(ty.kind(), ty::Param(_)) {
// Avoid suggesting the following:
// fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {}
where_clauses.extend(
Expand Down Expand Up @@ -472,10 +490,10 @@ fn fn_sig_suggestion<'tcx>(
let output = if !output.is_unit() { format!(" -> {output}") } else { String::new() };

let safety = sig.safety.prefix_str();
let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates);
let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates, assoc);

// 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
// not be present in the `fn` definition, nor 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.
Expand Down Expand Up @@ -511,6 +529,7 @@ fn suggestion_signature<'tcx>(
let (generics, where_clauses) = bounds_from_generic_predicates(
tcx,
tcx.predicates_of(assoc.def_id).instantiate_own(tcx, args),
assoc,
);
format!("type {}{generics} = /* Type */{where_clauses};", assoc.name())
}
Expand Down
12 changes: 12 additions & 0 deletions tests/ui/suggestions/apitit-unimplemented-method.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//@ aux-build:dep.rs

extern crate dep;
use dep::*;

struct Local;
impl Trait for Local {}
//~^ ERROR not all trait items implemented
//~| HELP implement the missing item: `fn foo(_: impl Sized) { todo!() }`
//~| HELP implement the missing item: `fn bar<T>(_: impl Sized) { todo!() }`

fn main() {}
12 changes: 12 additions & 0 deletions tests/ui/suggestions/apitit-unimplemented-method.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
error[E0046]: not all trait items implemented, missing: `foo`, `bar`
--> $DIR/apitit-unimplemented-method.rs:7:1
|
LL | impl Trait for Local {}
| ^^^^^^^^^^^^^^^^^^^^ missing `foo`, `bar` in implementation
|
= help: implement the missing item: `fn foo(_: impl Sized) { todo!() }`
= help: implement the missing item: `fn bar<T>(_: impl Sized) { todo!() }`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0046`.
4 changes: 4 additions & 0 deletions tests/ui/suggestions/auxiliary/dep.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub trait Trait {
fn foo(_: impl Sized);
fn bar<T>(_: impl Sized);
}
Loading