From a4a2fc0af33ef5a4e1211c5d3e4f0eeca02322f8 Mon Sep 17 00:00:00 2001 From: Ohad Ravid Date: Wed, 18 Dec 2019 20:35:18 +0100 Subject: [PATCH] Suggest associated type when the specified one cannot be found --- src/librustc_typeck/astconv.rs | 103 +++++++++++++++++++-------- src/test/ui/span/type-binding.stderr | 2 +- 2 files changed, 73 insertions(+), 32 deletions(-) diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index af978d5095e..290f86d626e 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -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 @@ -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, - ¶m_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())), + ¶m_name.as_str(), + assoc_name, + span, + ) } - // Checks that `bounds` contains exactly one element and reports appropriate - // errors otherwise. fn one_bound_for_assoc_type(&self, - mut bounds: I, + all_candidates: impl Fn() -> I, ty_param_name: &str, assoc_name: ast::Ident, span: Span) -> Result, ErrorReported> where I: Iterator> { - 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 `{}`", @@ -1606,6 +1601,50 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { return Ok(bound); } + fn complain_about_assoc_type_not_found(&self, + all_candidates: impl Fn() -> I, + ty_param_name: &str, + assoc_name: ast::Ident, + span: Span) + where I: Iterator> { + 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 @@ -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)) => { diff --git a/src/test/ui/span/type-binding.stderr b/src/test/ui/span/type-binding.stderr index 421af67d19c..c3e95455532 100644 --- a/src/test/ui/span/type-binding.stderr +++ b/src/test/ui/span/type-binding.stderr @@ -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) {} - | ^^^^^^^^^^^ associated type `Trget` not found + | ^^^^^^^^^^^ help: there is an associated type with a similar name: `Target` error: aborting due to previous error