about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-11-23 11:26:48 +0000
committerbors <bors@rust-lang.org>2018-11-23 11:26:48 +0000
commitaecbcd1ce24373a5077a4ea96f17cc2b7c8bb5eb (patch)
tree9d5029637dfe08c6ac6167209619f626af3f6d3e /src
parent6a2d1b4e15d5de90f8c36181b1d429da658adfd2 (diff)
parent510f836d2378bc9d7ec48e3c39ca83006aadb197 (diff)
downloadrust-aecbcd1ce24373a5077a4ea96f17cc2b7c8bb5eb.tar.gz
rust-aecbcd1ce24373a5077a4ea96f17cc2b7c8bb5eb.zip
Auto merge of #55808 - estebank:type-arguments, r=petrochenkov
Suggest correct syntax when writing type arg instead of assoc type

- When confusing an associated type with a type argument, suggest the appropriate syntax. Given `Iterator<isize>`, suggest `Iterator<Item = isize>`.
- When encountering multiple missing associated types, emit only one diagnostic.
- Point at associated type def span for context.
- Point at each extra type argument.

Follow up to #48288, fix #20977.
Diffstat (limited to 'src')
-rw-r--r--src/librustc_typeck/astconv.rs181
-rw-r--r--src/librustc_typeck/collect.rs11
-rw-r--r--src/librustc_typeck/lib.rs2
-rw-r--r--src/test/compile-fail/issue-23595-1.rs4
-rw-r--r--src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.stderr5
-rw-r--r--src/test/ui/associated-types/associated-types-incomplete-object.rs3
-rw-r--r--src/test/ui/associated-types/associated-types-incomplete-object.stderr30
-rw-r--r--src/test/ui/error-codes/E0107.rs3
-rw-r--r--src/test/ui/error-codes/E0107.stderr6
-rw-r--r--src/test/ui/error-codes/E0191.stderr5
-rw-r--r--src/test/ui/error-codes/E0220.stderr5
-rw-r--r--src/test/ui/issues/issue-19482.stderr5
-rw-r--r--src/test/ui/issues/issue-21950.stderr2
-rw-r--r--src/test/ui/issues/issue-22434.stderr5
-rw-r--r--src/test/ui/issues/issue-22560.stderr2
-rw-r--r--src/test/ui/issues/issue-23024.stderr2
-rw-r--r--src/test/ui/issues/issue-28344.stderr4
-rw-r--r--src/test/ui/suggestions/use-type-argument-instead-of-assoc-type.rs8
-rw-r--r--src/test/ui/suggestions/use-type-argument-instead-of-assoc-type.stderr31
-rw-r--r--src/test/ui/traits/trait-alias-object.stderr2
20 files changed, 222 insertions, 94 deletions
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index d388d756438..a8164c85e82 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -182,7 +182,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
         item_segment: &hir::PathSegment)
         -> &'tcx Substs<'tcx>
     {
-        let (substs, assoc_bindings) = item_segment.with_generic_args(|generic_args| {
+        let (substs, assoc_bindings, _) = item_segment.with_generic_args(|generic_args| {
             self.create_substs_for_ast_path(
                 span,
                 def_id,
@@ -256,7 +256,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
             },
             def.parent.is_none() && def.has_self, // `has_self`
             seg.infer_types || suppress_mismatch, // `infer_types`
-        )
+        ).0
     }
 
     /// Check that the correct number of generic arguments have been provided.
@@ -269,7 +269,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
         position: GenericArgPosition,
         has_self: bool,
         infer_types: bool,
-    ) -> bool {
+    ) -> (bool, Option<Vec<Span>>) {
         // At this stage we are guaranteed that the generic arguments are in the correct order, e.g.
         // that lifetimes will proceed types. So it suffices to check the number of each generic
         // arguments in order to validate them with respect to the generic parameters.
@@ -303,13 +303,13 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
                     let mut err = tcx.sess.struct_span_err(span, msg);
                     err.span_note(span_late, note);
                     err.emit();
-                    return true;
+                    return (true, None);
                 } else {
                     let mut multispan = MultiSpan::from_span(span);
                     multispan.push_span_label(span_late, note.to_string());
                     tcx.lint_node(lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS,
                                   args.args[0].id(), multispan, msg);
-                    return false;
+                    return (false, None);
                 }
             }
         }
@@ -323,7 +323,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
             // For kinds without defaults (i.e. lifetimes), `required == permitted`.
             // For other kinds (i.e. types), `permitted` may be greater than `required`.
             if required <= provided && provided <= permitted {
-                return false;
+                return (false, None);
             }
 
             // Unfortunately lifetime and type parameter mismatches are typically styled
@@ -338,33 +338,28 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
                 (required, "")
             };
 
-            let mut span = span;
-            let label = if required == permitted && provided > permitted {
-                let diff = provided - permitted;
-                if diff == 1 {
-                    // In the case when the user has provided too many arguments,
-                    // we want to point to the first unexpected argument.
-                    let first_superfluous_arg: &GenericArg = &args.args[offset + permitted];
-                    span = first_superfluous_arg.span();
-                }
-                format!(
-                    "{}unexpected {} argument{}",
-                    if diff != 1 { format!("{} ", diff) } else { String::new() },
-                    kind,
-                    if diff != 1 { "s" } else { "" },
-                )
+            let mut potential_assoc_types: Option<Vec<Span>> = None;
+            let (spans, label) = if required == permitted && provided > permitted {
+                // In the case when the user has provided too many arguments,
+                // we want to point to the unexpected arguments.
+                let spans: Vec<Span> = args.args[offset+permitted .. offset+provided]
+                        .iter()
+                        .map(|arg| arg.span())
+                        .collect();
+                potential_assoc_types = Some(spans.clone());
+                (spans, format!( "unexpected {} argument", kind))
             } else {
-                format!(
+                (vec![span], format!(
                     "expected {}{} {} argument{}",
                     quantifier,
                     bound,
                     kind,
                     if bound != 1 { "s" } else { "" },
-                )
+                ))
             };
 
-            tcx.sess.struct_span_err_with_code(
-                span,
+            let mut err = tcx.sess.struct_span_err_with_code(
+                spans.clone(),
                 &format!(
                     "wrong number of {} arguments: expected {}{}, found {}",
                     kind,
@@ -373,9 +368,14 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
                     provided,
                 ),
                 DiagnosticId::Error("E0107".into())
-            ).span_label(span, label).emit();
+            );
+            for span in spans {
+                err.span_label(span, label.as_str());
+            }
+            err.emit();
 
-            provided > required // `suppress_error`
+            (provided > required, // `suppress_error`
+             potential_assoc_types)
         };
 
         if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes {
@@ -397,7 +397,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
                 arg_counts.lifetimes,
             )
         } else {
-            false
+            (false, None)
         }
     }
 
@@ -555,7 +555,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
         generic_args: &hir::GenericArgs,
         infer_types: bool,
         self_ty: Option<Ty<'tcx>>)
-        -> (&'tcx Substs<'tcx>, Vec<ConvertedBinding<'tcx>>)
+        -> (&'tcx Substs<'tcx>, Vec<ConvertedBinding<'tcx>>, Option<Vec<Span>>)
     {
         // If the type is parameterized by this region, then replace this
         // region with the current anon region binding (in other words,
@@ -571,7 +571,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
         assert_eq!(generic_params.has_self, self_ty.is_some());
 
         let has_self = generic_params.has_self;
-        Self::check_generic_arg_count(
+        let (_, potential_assoc_types) = Self::check_generic_arg_count(
             self.tcx(),
             span,
             &generic_params,
@@ -676,7 +676,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
         debug!("create_substs_for_ast_path(generic_params={:?}, self_ty={:?}) -> {:?}",
                generic_params, self_ty, substs);
 
-        (substs, assoc_bindings)
+        (substs, assoc_bindings, potential_assoc_types)
     }
 
     /// Instantiates the path for the given trait reference, assuming that it's
@@ -718,7 +718,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
         self_ty: Ty<'tcx>,
         poly_projections: &mut Vec<(ty::PolyProjectionPredicate<'tcx>, Span)>,
         speculative: bool)
-        -> ty::PolyTraitRef<'tcx>
+        -> (ty::PolyTraitRef<'tcx>, Option<Vec<Span>>)
     {
         let trait_def_id = self.trait_def_id(trait_ref);
 
@@ -726,11 +726,12 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
 
         self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1);
 
-        let (substs, assoc_bindings) =
-            self.create_substs_for_ast_trait_ref(trait_ref.path.span,
-                                                 trait_def_id,
-                                                 self_ty,
-                                                 trait_ref.path.segments.last().unwrap());
+        let (substs, assoc_bindings, potential_assoc_types) = self.create_substs_for_ast_trait_ref(
+            trait_ref.path.span,
+            trait_def_id,
+            self_ty,
+            trait_ref.path.segments.last().unwrap(),
+        );
         let poly_trait_ref = ty::Binder::bind(ty::TraitRef::new(trait_def_id, substs));
 
         let mut dup_bindings = FxHashMap::default();
@@ -745,14 +746,14 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
 
         debug!("instantiate_poly_trait_ref({:?}, projections={:?}) -> {:?}",
                trait_ref, poly_projections, poly_trait_ref);
-        poly_trait_ref
+        (poly_trait_ref, potential_assoc_types)
     }
 
     pub fn instantiate_poly_trait_ref(&self,
         poly_trait_ref: &hir::PolyTraitRef,
         self_ty: Ty<'tcx>,
         poly_projections: &mut Vec<(ty::PolyProjectionPredicate<'tcx>, Span)>)
-        -> ty::PolyTraitRef<'tcx>
+        -> (ty::PolyTraitRef<'tcx>, Option<Vec<Span>>)
     {
         self.instantiate_poly_trait_ref_inner(&poly_trait_ref.trait_ref, self_ty,
                                               poly_projections, false)
@@ -765,7 +766,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
                                   trait_segment: &hir::PathSegment)
                                   -> ty::TraitRef<'tcx>
     {
-        let (substs, assoc_bindings) =
+        let (substs, assoc_bindings, _) =
             self.create_substs_for_ast_trait_ref(span,
                                                  trait_def_id,
                                                  self_ty,
@@ -774,13 +775,13 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
         ty::TraitRef::new(trait_def_id, substs)
     }
 
-    fn create_substs_for_ast_trait_ref(&self,
-                                       span: Span,
-                                       trait_def_id: DefId,
-                                       self_ty: Ty<'tcx>,
-                                       trait_segment: &hir::PathSegment)
-                                       -> (&'tcx Substs<'tcx>, Vec<ConvertedBinding<'tcx>>)
-    {
+    fn create_substs_for_ast_trait_ref(
+        &self,
+        span: Span,
+        trait_def_id: DefId,
+        self_ty: Ty<'tcx>,
+        trait_segment: &hir::PathSegment,
+    ) -> (&'tcx Substs<'tcx>, Vec<ConvertedBinding<'tcx>>, Option<Vec<Span>>) {
         debug!("create_substs_for_ast_trait_ref(trait_segment={:?})",
                trait_segment);
 
@@ -970,9 +971,11 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
 
         let mut projection_bounds = Vec::new();
         let dummy_self = tcx.mk_ty(TRAIT_OBJECT_DUMMY_SELF);
-        let principal = self.instantiate_poly_trait_ref(&trait_bounds[0],
-                                                        dummy_self,
-                                                        &mut projection_bounds);
+        let (principal, potential_assoc_types) = self.instantiate_poly_trait_ref(
+            &trait_bounds[0],
+            dummy_self,
+            &mut projection_bounds,
+        );
         debug!("principal: {:?}", principal);
 
         for trait_bound in trait_bounds[1..].iter() {
@@ -1027,16 +1030,74 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx>+'o {
             associated_types.remove(&projection_bound.projection_def_id());
         }
 
-        for item_def_id in associated_types {
-            let assoc_item = tcx.associated_item(item_def_id);
-            let trait_def_id = assoc_item.container.id();
-            struct_span_err!(tcx.sess, span, E0191, "the value of the associated type `{}` \
-                                                     (from the trait `{}`) must be specified",
-                                                    assoc_item.ident,
-                                                    tcx.item_path_str(trait_def_id))
-                .span_label(span, format!("missing associated type `{}` value",
-                                          assoc_item.ident))
-                .emit();
+        if !associated_types.is_empty() {
+            let names = associated_types.iter().map(|item_def_id| {
+                let assoc_item = tcx.associated_item(*item_def_id);
+                let trait_def_id = assoc_item.container.id();
+                format!(
+                    "`{}` (from the trait `{}`)",
+                    assoc_item.ident,
+                    tcx.item_path_str(trait_def_id),
+                )
+            }).collect::<Vec<_>>().join(", ");
+            let mut err = struct_span_err!(
+                tcx.sess,
+                span,
+                E0191,
+                "the value of the associated type{} {} must be specified",
+                if associated_types.len() == 1 { "" } else { "s" },
+                names,
+            );
+            let mut suggest = false;
+            let mut potential_assoc_types_spans = vec![];
+            if let Some(potential_assoc_types) = potential_assoc_types {
+                if potential_assoc_types.len() == associated_types.len() {
+                    // Only suggest when the amount of missing associated types is equals to the
+                    // extra type arguments present, as that gives us a relatively high confidence
+                    // that the user forgot to give the associtated type's name. The canonical
+                    // example would be trying to use `Iterator<isize>` instead of
+                    // `Iterator<Item=isize>`.
+                    suggest = true;
+                    potential_assoc_types_spans = potential_assoc_types;
+                }
+            }
+            let mut suggestions = vec![];
+            for (i, item_def_id) in associated_types.iter().enumerate() {
+                let assoc_item = tcx.associated_item(*item_def_id);
+                err.span_label(
+                    span,
+                    format!("associated type `{}` must be specified", assoc_item.ident),
+                );
+                if item_def_id.is_local() {
+                    err.span_label(
+                        tcx.def_span(*item_def_id),
+                        format!("`{}` defined here", assoc_item.ident),
+                    );
+                }
+                if suggest {
+                    if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(
+                        potential_assoc_types_spans[i],
+                    ) {
+                        suggestions.push((
+                            potential_assoc_types_spans[i],
+                            format!("{} = {}", assoc_item.ident, snippet),
+                        ));
+                    }
+                }
+            }
+            if !suggestions.is_empty() {
+                let msg = if suggestions.len() == 1 {
+                    "if you meant to specify the associated type, write"
+                } else {
+                    "if you meant to specify the associated types, write"
+                };
+                err.multipart_suggestion_with_applicability(
+                    msg,
+                    suggestions,
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            err.emit();
         }
 
         // Erase the `dummy_self` (`TRAIT_OBJECT_DUMMY_SELF`) used above.
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index f3c570e8400..a1bb0b53f1f 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -1892,7 +1892,7 @@ fn explicit_predicates_of<'a, 'tcx>(
                         &hir::GenericBound::Trait(ref poly_trait_ref, _) => {
                             let mut projections = Vec::new();
 
-                            let trait_ref = AstConv::instantiate_poly_trait_ref(
+                            let (trait_ref, _) = AstConv::instantiate_poly_trait_ref(
                                 &icx,
                                 poly_trait_ref,
                                 ty,
@@ -2016,7 +2016,12 @@ pub fn compute_bounds<'gcx: 'tcx, 'tcx>(
     let mut projection_bounds = Vec::new();
 
     let mut trait_bounds: Vec<_> = trait_bounds.iter().map(|&bound| {
-        (astconv.instantiate_poly_trait_ref(bound, param_ty, &mut projection_bounds), bound.span)
+        let (poly_trait_ref, _) = astconv.instantiate_poly_trait_ref(
+            bound,
+            param_ty,
+            &mut projection_bounds,
+        );
+        (poly_trait_ref, bound.span)
     }).collect();
 
     let region_bounds = region_bounds
@@ -2057,7 +2062,7 @@ fn predicates_from_bound<'tcx>(
     match *bound {
         hir::GenericBound::Trait(ref tr, hir::TraitBoundModifier::None) => {
             let mut projections = Vec::new();
-            let pred = astconv.instantiate_poly_trait_ref(tr, param_ty, &mut projections);
+            let (pred, _) = astconv.instantiate_poly_trait_ref(tr, param_ty, &mut projections);
             iter::once((pred.to_predicate(), tr.span)).chain(
                 projections
                     .into_iter()
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index 1f5998d8ca3..0fba311d7f7 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -389,7 +389,7 @@ pub fn hir_trait_to_predicates<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, hir_trait:
     let env_def_id = tcx.hir.local_def_id(env_node_id);
     let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id);
     let mut projections = Vec::new();
-    let principal = astconv::AstConv::instantiate_poly_trait_ref_inner(
+    let (principal, _) = astconv::AstConv::instantiate_poly_trait_ref_inner(
         &item_cx, hir_trait, tcx.types.err, &mut projections, true
     );
 
diff --git a/src/test/compile-fail/issue-23595-1.rs b/src/test/compile-fail/issue-23595-1.rs
index a3422d859c6..1e615c4c0db 100644
--- a/src/test/compile-fail/issue-23595-1.rs
+++ b/src/test/compile-fail/issue-23595-1.rs
@@ -16,9 +16,7 @@ trait Hierarchy {
     type Value;
     type ChildKey;
     type Children = Index<Self::ChildKey, Output=Hierarchy>;
-    //~^ ERROR: the value of the associated type `ChildKey`
-    //~^^ ERROR: the value of the associated type `Children`
-    //~^^^ ERROR: the value of the associated type `Value`
+    //~^ ERROR: the value of the associated types `Value` (from the trait `Hierarchy`), `ChildKey`
 
     fn data(&self) -> Option<(Self::Value, Self::Children)>;
 }
diff --git a/src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.stderr b/src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.stderr
index 7a10b6d021f..b4285c0de29 100644
--- a/src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.stderr
+++ b/src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.stderr
@@ -25,8 +25,11 @@ LL | fn dent_object<COLOR>(c: BoxCar<Color=COLOR>) {
 error[E0191]: the value of the associated type `Color` (from the trait `Vehicle`) must be specified
   --> $DIR/associated-type-projection-from-multiple-supertraits.rs:33:26
    |
+LL |     type Color;
+   |     ----------- `Color` defined here
+...
 LL | fn dent_object<COLOR>(c: BoxCar<Color=COLOR>) {
-   |                          ^^^^^^^^^^^^^^^^^^^ missing associated type `Color` value
+   |                          ^^^^^^^^^^^^^^^^^^^ associated type `Color` must be specified
 
 error[E0221]: ambiguous associated type `Color` in bounds of `C`
   --> $DIR/associated-type-projection-from-multiple-supertraits.rs:38:29
diff --git a/src/test/ui/associated-types/associated-types-incomplete-object.rs b/src/test/ui/associated-types/associated-types-incomplete-object.rs
index 9f1df14605b..e575fd695b2 100644
--- a/src/test/ui/associated-types/associated-types-incomplete-object.rs
+++ b/src/test/ui/associated-types/associated-types-incomplete-object.rs
@@ -37,6 +37,5 @@ pub fn main() {
     //~^ ERROR the value of the associated type `A` (from the trait `Foo`) must be specified
 
     let d = &42isize as &Foo;
-    //~^ ERROR the value of the associated type `A` (from the trait `Foo`) must be specified
-    //~| ERROR the value of the associated type `B` (from the trait `Foo`) must be specified
+    //~^ ERROR the value of the associated types `A` (from the trait `Foo`), `B` (from the trait
 }
diff --git a/src/test/ui/associated-types/associated-types-incomplete-object.stderr b/src/test/ui/associated-types/associated-types-incomplete-object.stderr
index 95b1c631250..eb8e6f998a5 100644
--- a/src/test/ui/associated-types/associated-types-incomplete-object.stderr
+++ b/src/test/ui/associated-types/associated-types-incomplete-object.stderr
@@ -1,27 +1,35 @@
 error[E0191]: the value of the associated type `B` (from the trait `Foo`) must be specified
   --> $DIR/associated-types-incomplete-object.rs:33:26
    |
+LL |     type B;
+   |     ------- `B` defined here
+...
 LL |     let b = &42isize as &Foo<A=usize>;
-   |                          ^^^^^^^^^^^^ missing associated type `B` value
+   |                          ^^^^^^^^^^^^ associated type `B` must be specified
 
 error[E0191]: the value of the associated type `A` (from the trait `Foo`) must be specified
   --> $DIR/associated-types-incomplete-object.rs:36:26
    |
+LL |     type A;
+   |     ------- `A` defined here
+...
 LL |     let c = &42isize as &Foo<B=char>;
-   |                          ^^^^^^^^^^^ missing associated type `A` value
+   |                          ^^^^^^^^^^^ associated type `A` must be specified
 
-error[E0191]: the value of the associated type `A` (from the trait `Foo`) must be specified
-  --> $DIR/associated-types-incomplete-object.rs:39:26
-   |
-LL |     let d = &42isize as &Foo;
-   |                          ^^^ missing associated type `A` value
-
-error[E0191]: the value of the associated type `B` (from the trait `Foo`) must be specified
+error[E0191]: the value of the associated types `A` (from the trait `Foo`), `B` (from the trait `Foo`) must be specified
   --> $DIR/associated-types-incomplete-object.rs:39:26
    |
+LL |     type A;
+   |     ------- `A` defined here
+LL |     type B;
+   |     ------- `B` defined here
+...
 LL |     let d = &42isize as &Foo;
-   |                          ^^^ missing associated type `B` value
+   |                          ^^^
+   |                          |
+   |                          associated type `A` must be specified
+   |                          associated type `B` must be specified
 
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0191`.
diff --git a/src/test/ui/error-codes/E0107.rs b/src/test/ui/error-codes/E0107.rs
index 815c7fefd2a..87ac9e37853 100644
--- a/src/test/ui/error-codes/E0107.rs
+++ b/src/test/ui/error-codes/E0107.rs
@@ -26,7 +26,8 @@ struct Baz<'a, 'b, 'c> {
     //~| unexpected lifetime argument
     foo2: Foo<'a, 'b, 'c>,
     //~^ ERROR E0107
-    //~| 2 unexpected lifetime arguments
+    //~| unexpected lifetime argument
+    //~| unexpected lifetime argument
 }
 
 fn main() {}
diff --git a/src/test/ui/error-codes/E0107.stderr b/src/test/ui/error-codes/E0107.stderr
index 497fa91bd4f..a07c92cf26a 100644
--- a/src/test/ui/error-codes/E0107.stderr
+++ b/src/test/ui/error-codes/E0107.stderr
@@ -11,10 +11,12 @@ LL |     bar: Bar<'a>,
    |              ^^ unexpected lifetime argument
 
 error[E0107]: wrong number of lifetime arguments: expected 1, found 3
-  --> $DIR/E0107.rs:27:11
+  --> $DIR/E0107.rs:27:19
    |
 LL |     foo2: Foo<'a, 'b, 'c>,
-   |           ^^^^^^^^^^^^^^^ 2 unexpected lifetime arguments
+   |                   ^^  ^^ unexpected lifetime argument
+   |                   |
+   |                   unexpected lifetime argument
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/error-codes/E0191.stderr b/src/test/ui/error-codes/E0191.stderr
index 08b0a845814..f07529e7e9e 100644
--- a/src/test/ui/error-codes/E0191.stderr
+++ b/src/test/ui/error-codes/E0191.stderr
@@ -1,8 +1,11 @@
 error[E0191]: the value of the associated type `Bar` (from the trait `Trait`) must be specified
   --> $DIR/E0191.rs:15:12
    |
+LL |     type Bar;
+   |     --------- `Bar` defined here
+...
 LL | type Foo = Trait; //~ ERROR E0191
-   |            ^^^^^ missing associated type `Bar` value
+   |            ^^^^^ associated type `Bar` must be specified
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/error-codes/E0220.stderr b/src/test/ui/error-codes/E0220.stderr
index c8a587f2b53..d26e61fba8c 100644
--- a/src/test/ui/error-codes/E0220.stderr
+++ b/src/test/ui/error-codes/E0220.stderr
@@ -7,8 +7,11 @@ LL | type Foo = Trait<F=i32>; //~ ERROR E0220
 error[E0191]: the value of the associated type `Bar` (from the trait `Trait`) must be specified
   --> $DIR/E0220.rs:15:12
    |
+LL |     type Bar;
+   |     --------- `Bar` defined here
+...
 LL | type Foo = Trait<F=i32>; //~ ERROR E0220
-   |            ^^^^^^^^^^^^ missing associated type `Bar` value
+   |            ^^^^^^^^^^^^ associated type `Bar` must be specified
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/issues/issue-19482.stderr b/src/test/ui/issues/issue-19482.stderr
index 5e2d427ab72..d125438b851 100644
--- a/src/test/ui/issues/issue-19482.stderr
+++ b/src/test/ui/issues/issue-19482.stderr
@@ -1,8 +1,11 @@
 error[E0191]: the value of the associated type `A` (from the trait `Foo`) must be specified
   --> $DIR/issue-19482.rs:20:12
    |
+LL |     type A;
+   |     ------- `A` defined here
+...
 LL | fn bar(x: &Foo) {}
-   |            ^^^ missing associated type `A` value
+   |            ^^^ associated type `A` must be specified
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-21950.stderr b/src/test/ui/issues/issue-21950.stderr
index a2f74a29aab..33c89dd4994 100644
--- a/src/test/ui/issues/issue-21950.stderr
+++ b/src/test/ui/issues/issue-21950.stderr
@@ -10,7 +10,7 @@ error[E0191]: the value of the associated type `Output` (from the trait `std::op
   --> $DIR/issue-21950.rs:17:14
    |
 LL |             &Add;
-   |              ^^^ missing associated type `Output` value
+   |              ^^^ associated type `Output` must be specified
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/issues/issue-22434.stderr b/src/test/ui/issues/issue-22434.stderr
index 914da801ad4..3ba77346a1f 100644
--- a/src/test/ui/issues/issue-22434.stderr
+++ b/src/test/ui/issues/issue-22434.stderr
@@ -1,8 +1,11 @@
 error[E0191]: the value of the associated type `A` (from the trait `Foo`) must be specified
   --> $DIR/issue-22434.rs:15:19
    |
+LL |     type A;
+   |     ------- `A` defined here
+...
 LL | type I<'a> = &'a (Foo + 'a);
-   |                   ^^^^^^^^ missing associated type `A` value
+   |                   ^^^^^^^^ associated type `A` must be specified
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/issues/issue-22560.stderr b/src/test/ui/issues/issue-22560.stderr
index b5524036fae..715f84011f6 100644
--- a/src/test/ui/issues/issue-22560.stderr
+++ b/src/test/ui/issues/issue-22560.stderr
@@ -28,7 +28,7 @@ LL |   type Test = Add +
 LL | |             //~^ ERROR E0393
 LL | |             //~| ERROR E0191
 LL | |             Sub;
-   | |_______________^ missing associated type `Output` value
+   | |_______________^ associated type `Output` must be specified
 
 error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/issues/issue-23024.stderr b/src/test/ui/issues/issue-23024.stderr
index 129d0945303..adee12a36d3 100644
--- a/src/test/ui/issues/issue-23024.stderr
+++ b/src/test/ui/issues/issue-23024.stderr
@@ -16,7 +16,7 @@ error[E0191]: the value of the associated type `Output` (from the trait `std::op
   --> $DIR/issue-23024.rs:19:35
    |
 LL |     println!("{:?}",(vfnfer[0] as Fn)(3));
-   |                                   ^^ missing associated type `Output` value
+   |                                   ^^ associated type `Output` must be specified
 
 error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/issues/issue-28344.stderr b/src/test/ui/issues/issue-28344.stderr
index 7ef2e906422..734c761d0b7 100644
--- a/src/test/ui/issues/issue-28344.stderr
+++ b/src/test/ui/issues/issue-28344.stderr
@@ -2,7 +2,7 @@ error[E0191]: the value of the associated type `Output` (from the trait `std::op
   --> $DIR/issue-28344.rs:14:17
    |
 LL |     let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
-   |                 ^^^^^^^^^^^^^ missing associated type `Output` value
+   |                 ^^^^^^^^^^^^^ associated type `Output` must be specified
 
 error[E0599]: no function or associated item named `bitor` found for type `dyn std::ops::BitXor<_>` in the current scope
   --> $DIR/issue-28344.rs:14:17
@@ -16,7 +16,7 @@ error[E0191]: the value of the associated type `Output` (from the trait `std::op
   --> $DIR/issue-28344.rs:18:13
    |
 LL |     let g = BitXor::bitor;
-   |             ^^^^^^^^^^^^^ missing associated type `Output` value
+   |             ^^^^^^^^^^^^^ associated type `Output` must be specified
 
 error[E0599]: no function or associated item named `bitor` found for type `dyn std::ops::BitXor<_>` in the current scope
   --> $DIR/issue-28344.rs:18:13
diff --git a/src/test/ui/suggestions/use-type-argument-instead-of-assoc-type.rs b/src/test/ui/suggestions/use-type-argument-instead-of-assoc-type.rs
new file mode 100644
index 00000000000..58e7718ba5b
--- /dev/null
+++ b/src/test/ui/suggestions/use-type-argument-instead-of-assoc-type.rs
@@ -0,0 +1,8 @@
+pub trait T<X, Y> {
+    type A;
+    type B;
+    type C;
+}
+ pub struct Foo { i: Box<T<usize, usize, usize, usize, B=usize>> }
+
+ fn main() {}
diff --git a/src/test/ui/suggestions/use-type-argument-instead-of-assoc-type.stderr b/src/test/ui/suggestions/use-type-argument-instead-of-assoc-type.stderr
new file mode 100644
index 00000000000..b62b5d3b04c
--- /dev/null
+++ b/src/test/ui/suggestions/use-type-argument-instead-of-assoc-type.stderr
@@ -0,0 +1,31 @@
+error[E0107]: wrong number of type arguments: expected 2, found 4
+  --> $DIR/use-type-argument-instead-of-assoc-type.rs:6:42
+   |
+LL |  pub struct Foo { i: Box<T<usize, usize, usize, usize, B=usize>> }
+   |                                          ^^^^^  ^^^^^ unexpected type argument
+   |                                          |
+   |                                          unexpected type argument
+
+error[E0191]: the value of the associated types `A` (from the trait `T`), `C` (from the trait `T`) must be specified
+  --> $DIR/use-type-argument-instead-of-assoc-type.rs:6:26
+   |
+LL |     type A;
+   |     ------- `A` defined here
+LL |     type B;
+LL |     type C;
+   |     ------- `C` defined here
+LL | }
+LL |  pub struct Foo { i: Box<T<usize, usize, usize, usize, B=usize>> }
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                          |
+   |                          associated type `A` must be specified
+   |                          associated type `C` must be specified
+help: if you meant to specify the associated types, write
+   |
+LL |  pub struct Foo { i: Box<T<usize, usize, A = usize, C = usize, B=usize>> }
+   |                                          ^^^^^^^^^  ^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+Some errors occurred: E0107, E0191.
+For more information about an error, try `rustc --explain E0107`.
diff --git a/src/test/ui/traits/trait-alias-object.stderr b/src/test/ui/traits/trait-alias-object.stderr
index 6b7b322a53d..fdb9427cba7 100644
--- a/src/test/ui/traits/trait-alias-object.stderr
+++ b/src/test/ui/traits/trait-alias-object.stderr
@@ -10,7 +10,7 @@ error[E0191]: the value of the associated type `Item` (from the trait `std::iter
   --> $DIR/trait-alias-object.rs:18:13
    |
 LL |     let _: &dyn IteratorAlias = &vec![123].into_iter();
-   |             ^^^^^^^^^^^^^^^^^ missing associated type `Item` value
+   |             ^^^^^^^^^^^^^^^^^ associated type `Item` must be specified
 
 error: aborting due to 2 previous errors