about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorOhad Ravid <ohad.rv@gmail.com>2019-12-18 20:35:18 +0100
committerOhad Ravid <ohad.rv@gmail.com>2019-12-18 21:07:32 +0100
commita4a2fc0af33ef5a4e1211c5d3e4f0eeca02322f8 (patch)
treed9f9fceecf215563547000cddc91b439adee2ec4 /src
parent99b89533d4cdf7682ea4054ad0ee36c351d05df1 (diff)
downloadrust-a4a2fc0af33ef5a4e1211c5d3e4f0eeca02322f8.tar.gz
rust-a4a2fc0af33ef5a4e1211c5d3e4f0eeca02322f8.zip
Suggest associated type when the specified one cannot be found
Diffstat (limited to 'src')
-rw-r--r--src/librustc_typeck/astconv.rs103
-rw-r--r--src/test/ui/span/type-binding.stderr2
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,
-                                      &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 `{}`",
@@ -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
@@ -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: 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