Skip to content

Suggest associated type when the specified one cannot be found #67406

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 19, 2019
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
103 changes: 72 additions & 31 deletions src/librustc_typeck/astconv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1145,11 +1145,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
} else {
// Otherwise, we have to walk through the supertraits to find
// those that do.
let candidates = traits::supertraits(tcx, trait_ref).filter(|r| {
self.trait_defines_associated_type_named(r.def_id(), binding.item_name)
});
self.one_bound_for_assoc_type(
candidates,
|| traits::supertraits(tcx, trait_ref),
&trait_ref.print_only_trait_path().to_string(),
binding.item_name,
binding.span
Expand Down Expand Up @@ -1531,50 +1528,48 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {

debug!("find_bound_for_assoc_item: predicates={:#?}", predicates);

let bounds = predicates.iter().filter_map(|(p, _)| p.to_opt_poly_trait_ref());

// Check that there is exactly one way to find an associated type with the
// correct name.
let suitable_bounds = traits::transitive_bounds(tcx, bounds)
.filter(|b| self.trait_defines_associated_type_named(b.def_id(), assoc_name));

let param_hir_id = tcx.hir().as_local_hir_id(ty_param_def_id).unwrap();
let param_name = tcx.hir().ty_param_name(param_hir_id);
self.one_bound_for_assoc_type(suitable_bounds,
&param_name.as_str(),
assoc_name,
span)
self.one_bound_for_assoc_type(
|| traits::transitive_bounds(tcx, predicates
.iter().filter_map(|(p, _)| p.to_opt_poly_trait_ref())),
&param_name.as_str(),
assoc_name,
span,
)
}

// Checks that `bounds` contains exactly one element and reports appropriate
// errors otherwise.
fn one_bound_for_assoc_type<I>(&self,
mut bounds: I,
all_candidates: impl Fn() -> I,
ty_param_name: &str,
assoc_name: ast::Ident,
span: Span)
-> Result<ty::PolyTraitRef<'tcx>, ErrorReported>
where I: Iterator<Item = ty::PolyTraitRef<'tcx>>
{
let bound = match bounds.next() {
let mut matching_candidates = all_candidates().filter(|r| {
self.trait_defines_associated_type_named(r.def_id(), assoc_name)
});

let bound = match matching_candidates.next() {
Some(bound) => bound,
None => {
struct_span_err!(self.tcx().sess, span, E0220,
"associated type `{}` not found for `{}`",
assoc_name,
ty_param_name)
.span_label(span, format!("associated type `{}` not found", assoc_name))
.emit();
self.complain_about_assoc_type_not_found(
all_candidates,
ty_param_name,
assoc_name,
span
);
return Err(ErrorReported);
}
};

debug!("one_bound_for_assoc_type: bound = {:?}", bound);

if let Some(bound2) = bounds.next() {
if let Some(bound2) = matching_candidates.next() {
debug!("one_bound_for_assoc_type: bound2 = {:?}", bound2);

let bounds = iter::once(bound).chain(iter::once(bound2)).chain(bounds);
let bounds = iter::once(bound).chain(iter::once(bound2)).chain(matching_candidates);
let mut err = struct_span_err!(
self.tcx().sess, span, E0221,
"ambiguous associated type `{}` in bounds of `{}`",
Expand Down Expand Up @@ -1606,6 +1601,50 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
return Ok(bound);
}

fn complain_about_assoc_type_not_found<I>(&self,
all_candidates: impl Fn() -> I,
ty_param_name: &str,
assoc_name: ast::Ident,
span: Span)
where I: Iterator<Item = ty::PolyTraitRef<'tcx>> {
let mut err = struct_span_err!(self.tcx().sess, span, E0220,
"associated type `{}` not found for `{}`",
assoc_name,
ty_param_name);

let all_candidate_names: Vec<_> = all_candidates()
.map(|r| self.tcx().associated_items(r.def_id()))
.flatten()
.filter_map(|item|
if item.kind == ty::AssocKind::Type {
Some(item.ident.name)
} else {
None
}
)
.collect();

if let Some(suggested_name) = find_best_match_for_name(
all_candidate_names.iter(),
&assoc_name.as_str(),
None,
) {
err.span_suggestion(
span,
"there is an associated type with a similar name",
suggested_name.to_string(),
Applicability::MaybeIncorrect,
);
} else {
err.span_label(
span,
format!("associated type `{}` not found", assoc_name)
);
}

err.emit();
}

// Create a type from a path to an associated type.
// For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C`
// and item_segment is the path segment for `D`. We return a type and a def for
Expand Down Expand Up @@ -1660,10 +1699,12 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
}
};

let candidates = traits::supertraits(tcx, ty::Binder::bind(trait_ref))
.filter(|r| self.trait_defines_associated_type_named(r.def_id(), assoc_ident));

self.one_bound_for_assoc_type(candidates, "Self", assoc_ident, span)?
self.one_bound_for_assoc_type(
|| traits::supertraits(tcx, ty::Binder::bind(trait_ref)),
"Self",
assoc_ident,
span
)?
}
(&ty::Param(_), Res::SelfTy(Some(param_did), None)) |
(&ty::Param(_), Res::Def(DefKind::TyParam, param_did)) => {
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/span/type-binding.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ error[E0220]: associated type `Trget` not found for `std::ops::Deref`
--> $DIR/type-binding.rs:6:20
|
LL | fn homura<T: Deref<Trget = i32>>(_: T) {}
| ^^^^^^^^^^^ associated type `Trget` not found
| ^^^^^^^^^^^ help: there is an associated type with a similar name: `Target`

error: aborting due to previous error

Expand Down