about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2019-12-17 18:15:06 -0800
committerEsteban Küber <esteban@kuber.com.ar>2019-12-24 22:23:02 -0800
commit80adfd8de380d3055683e4a042ce845e663b7309 (patch)
tree5fefcb11b8abd707d88fb80ef3598b8ea9b76895
parent83c5b4ec40ceadfbcf3c558f06e04c48f01c1e52 (diff)
downloadrust-80adfd8de380d3055683e4a042ce845e663b7309.tar.gz
rust-80adfd8de380d3055683e4a042ce845e663b7309.zip
Account for multiple trait bounds with missing associated types
-rw-r--r--src/librustc_typeck/astconv.rs324
-rw-r--r--src/test/ui/associated-type/associated-type-projection-from-multiple-supertraits.stderr16
-rw-r--r--src/test/ui/associated-types/missing-associated-types.rs27
-rw-r--r--src/test/ui/associated-types/missing-associated-types.stderr129
-rw-r--r--src/test/ui/issues/issue-22560.stderr13
-rw-r--r--src/test/ui/suggestions/use-type-argument-instead-of-assoc-type.stderr4
6 files changed, 367 insertions, 146 deletions
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index 07b2f2c47a2..96295e7a326 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -1444,59 +1444,68 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         }
 
         // Use a `BTreeSet` to keep output in a more consistent order.
-        let mut associated_types = BTreeSet::default();
+        let mut associated_types: FxHashMap<Span, BTreeSet<DefId>> = FxHashMap::default();
 
-        let regular_traits_refs = bounds
+        let regular_traits_refs_spans = bounds
             .trait_bounds
             .into_iter()
-            .filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()))
-            .map(|(trait_ref, _)| trait_ref);
-        for trait_ref in traits::elaborate_trait_refs(tcx, regular_traits_refs) {
-            debug!("conv_object_ty_poly_trait_ref: observing object predicate `{:?}`", trait_ref);
-            match trait_ref {
-                ty::Predicate::Trait(pred) => {
-                    associated_types.extend(
-                        tcx.associated_items(pred.def_id())
-                            .filter(|item| item.kind == ty::AssocKind::Type)
-                            .map(|item| item.def_id),
-                    );
-                }
-                ty::Predicate::Projection(pred) => {
-                    // A `Self` within the original bound will be substituted with a
-                    // `trait_object_dummy_self`, so check for that.
-                    let references_self = pred.skip_binder().ty.walk().any(|t| t == dummy_self);
-
-                    // If the projection output contains `Self`, force the user to
-                    // elaborate it explicitly to avoid a lot of complexity.
-                    //
-                    // The "classicaly useful" case is the following:
-                    // ```
-                    //     trait MyTrait: FnMut() -> <Self as MyTrait>::MyOutput {
-                    //         type MyOutput;
-                    //     }
-                    // ```
-                    //
-                    // Here, the user could theoretically write `dyn MyTrait<Output = X>`,
-                    // but actually supporting that would "expand" to an infinitely-long type
-                    // `fix $ τ → dyn MyTrait<MyOutput = X, Output = <τ as MyTrait>::MyOutput`.
-                    //
-                    // Instead, we force the user to write `dyn MyTrait<MyOutput = X, Output = X>`,
-                    // which is uglier but works. See the discussion in #56288 for alternatives.
-                    if !references_self {
-                        // Include projections defined on supertraits.
-                        bounds.projection_bounds.push((pred, DUMMY_SP))
+            .filter(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
+
+        for (base_trait_ref, span) in regular_traits_refs_spans {
+            debug!("conv_object_ty_poly_trait_ref regular_trait_ref `{:?}`", base_trait_ref);
+            let mut new_bounds = vec![];
+            for trait_ref in traits::elaborate_trait_ref(tcx, base_trait_ref) {
+                debug!(
+                    "conv_object_ty_poly_trait_ref: observing object predicate `{:?}`",
+                    trait_ref
+                );
+                match trait_ref {
+                    ty::Predicate::Trait(pred) => {
+                        associated_types.entry(span).or_default().extend(
+                            tcx.associated_items(pred.def_id())
+                                .filter(|item| item.kind == ty::AssocKind::Type)
+                                .map(|item| item.def_id),
+                        );
                     }
+                    ty::Predicate::Projection(pred) => {
+                        // A `Self` within the original bound will be substituted with a
+                        // `trait_object_dummy_self`, so check for that.
+                        let references_self = pred.skip_binder().ty.walk().any(|t| t == dummy_self);
+
+                        // If the projection output contains `Self`, force the user to
+                        // elaborate it explicitly to avoid a lot of complexity.
+                        //
+                        // The "classicaly useful" case is the following:
+                        // ```
+                        //     trait MyTrait: FnMut() -> <Self as MyTrait>::MyOutput {
+                        //         type MyOutput;
+                        //     }
+                        // ```
+                        //
+                        // Here, the user could theoretically write `dyn MyTrait<Output = X>`,
+                        // but actually supporting that would "expand" to an infinitely-long type
+                        // `fix $ τ → dyn MyTrait<MyOutput = X, Output = <τ as MyTrait>::MyOutput`.
+                        //
+                        // Instead, we force the user to write `dyn MyTrait<MyOutput = X, Output = X>`,
+                        // which is uglier but works. See the discussion in #56288 for alternatives.
+                        if !references_self {
+                            // Include projections defined on supertraits.
+                            new_bounds.push((pred, span));
+                        }
+                    }
+                    _ => (),
                 }
-                _ => (),
             }
+            bounds.projection_bounds.extend(new_bounds);
         }
 
         for (projection_bound, _) in &bounds.projection_bounds {
-            associated_types.remove(&projection_bound.projection_def_id());
+            for (_, def_ids) in &mut associated_types {
+                def_ids.remove(&projection_bound.projection_def_id());
+            }
         }
 
         self.complain_about_missing_associated_types(
-            span,
             associated_types,
             potential_assoc_types,
             trait_bounds,
@@ -1591,134 +1600,183 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
     fn complain_about_missing_associated_types(
         &self,
-        span: Span,
-        associated_types: BTreeSet<DefId>,
+        mut associated_types: FxHashMap<Span, BTreeSet<DefId>>,
         potential_assoc_types: Vec<Span>,
         trait_bounds: &[hir::PolyTraitRef],
     ) {
-        if associated_types.is_empty() {
+        if !associated_types.values().any(|v| v.len() > 0) {
             return;
         }
-        // Account for things like `dyn Foo + 'a` by pointing at the `TraitRef.path`
-        // `Span` instead of the `PolyTraitRef` `Span`. That way the suggestion will
-        // be valid, otherwise we would suggest `dyn Foo + 'a<A = Type>`. See tests
-        // `issue-22434.rs` and `issue-22560.rs` for examples.
-        let sugg_span = match (&potential_assoc_types[..], &trait_bounds) {
+        let tcx = self.tcx();
+        let mut names = vec![];
+
+        // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
+        // `issue-22560.rs`.
+        let mut trait_bound_spans: Vec<Span> = vec![];
+        for (span, item_def_ids) in &associated_types {
+            if !item_def_ids.is_empty() {
+                trait_bound_spans.push(*span);
+            }
+            for item_def_id in item_def_ids {
+                let assoc_item = tcx.associated_item(*item_def_id);
+                let trait_def_id = assoc_item.container.id();
+                names.push(format!(
+                    "`{}` (from trait `{}`)",
+                    assoc_item.ident,
+                    tcx.def_path_str(trait_def_id),
+                ));
+            }
+        }
+
+        match (&potential_assoc_types[..], &trait_bounds) {
             ([], [bound]) => match &bound.trait_ref.path.segments[..] {
                 // FIXME: `trait_ref.path.span` can point to a full path with multiple
                 // segments, even though `trait_ref.path.segments` is of length `1`. Work
                 // around that bug here, even though it should be fixed elsewhere.
                 // This would otherwise cause an invalid suggestion. For an example, look at
-                // `src/test/ui/issues/issue-28344.rs`.
-                [segment] if segment.args.is_none() => segment.ident.span,
-                _ => bound.trait_ref.path.span,
+                // `src/test/ui/issues/issue-28344.rs` where instead of the following:
+                //
+                //   error[E0191]: the value of the associated type `Output`
+                //                 (from trait `std::ops::BitXor`) must be specified
+                //   --> $DIR/issue-28344.rs:4:17
+                //    |
+                // LL |     let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
+                //    |                 ^^^^^^ help: specify the associated type:
+                //    |                              `BitXor<Output = Type>`
+                //
+                // we would output:
+                //
+                //   error[E0191]: the value of the associated type `Output`
+                //                 (from trait `std::ops::BitXor`) must be specified
+                //   --> $DIR/issue-28344.rs:4:17
+                //    |
+                // LL |     let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
+                //    |                 ^^^^^^^^^^^^^ help: specify the associated type:
+                //    |                                     `BitXor::bitor<Output = Type>`
+                [segment] if segment.args.is_none() => {
+                    trait_bound_spans = vec![segment.ident.span];
+                    associated_types = associated_types
+                        .into_iter()
+                        .map(|(_, defs)| (segment.ident.span, defs))
+                        .collect();
+                }
+                _ => {}
             },
-            _ => span,
-        };
-        let tcx = self.tcx();
-        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 trait `{}`)", assoc_item.ident, tcx.def_path_str(trait_def_id))
-            })
-            .collect::<Vec<_>>()
-            .join(", ");
+            _ => {}
+        }
         let mut err = struct_span_err!(
             tcx.sess,
-            sugg_span,
+            trait_bound_spans,
             E0191,
             "the value of the associated type{} {} must be specified",
-            pluralize!(associated_types.len()),
-            names,
+            pluralize!(names.len()),
+            names.join(", "),
         );
-        let mut suggestions = Vec::new();
-        let mut applicability = Applicability::MaybeIncorrect;
-        for (i, item_def_id) in associated_types.iter().enumerate() {
-            let assoc_item = tcx.associated_item(*item_def_id);
-            if let Some(sp) = tcx.hir().span_if_local(*item_def_id) {
-                err.span_label(sp, format!("`{}` defined here", assoc_item.ident));
-            }
-            if potential_assoc_types.len() == associated_types.len() {
+        let mut suggestions = vec![];
+        let mut types_count = 0;
+        let mut where_constraints = vec![];
+        for (span, def_ids) in &associated_types {
+            let assoc_items: Vec<_> =
+                def_ids.iter().map(|def_id| tcx.associated_item(*def_id)).collect();
+            let mut names: FxHashMap<_, usize> = FxHashMap::default();
+            for item in &assoc_items {
+                types_count += 1;
+                *names.entry(item.ident.name).or_insert(0) += 1;
+            }
+            let mut dupes = false;
+            for item in &assoc_items {
+                let prefix = if names[&item.ident.name] > 1 {
+                    let trait_def_id = item.container.id();
+                    dupes = true;
+                    format!("{}::", tcx.def_path_str(trait_def_id))
+                } else {
+                    String::new()
+                };
+                if let Some(sp) = tcx.hir().span_if_local(item.def_id) {
+                    err.span_label(sp, format!("`{}{}` defined here", prefix, item.ident));
+                }
+            }
+            if potential_assoc_types.len() == assoc_items.len() {
                 // Only suggest when the amount of missing associated types equals the number of
                 // 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>`.
-                if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(potential_assoc_types[i])
-                {
-                    suggestions.push((
-                        potential_assoc_types[i],
-                        format!("{} = {}", assoc_item.ident, snippet),
-                    ));
+                for (potential, item) in potential_assoc_types.iter().zip(assoc_items.iter()) {
+                    if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*potential) {
+                        suggestions.push((*potential, format!("{} = {}", item.ident, snippet)));
+                    }
                 }
-            }
-        }
-        let mut suggestions_len = suggestions.len();
-        if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(sugg_span) {
-            let assoc_types: Vec<String> = associated_types
-                .iter()
-                .map(|item_def_id| {
-                    let assoc_item = tcx.associated_item(*item_def_id);
-                    format!("{} = Type", assoc_item.ident)
-                })
-                .collect();
-            let dedup = assoc_types.clone().drain(..).collect::<FxHashSet<_>>();
-
-            if dedup.len() != assoc_types.len() && trait_bounds.len() == 1 {
-                // If there are duplicates associated type names and a single trait bound do not
-                // use structured suggestion, it means that there are multiple super-traits with
-                // the same associated type name.
-                err.help(
-                    "consider introducing a new type parameter, adding `where` constraints \
-                          using the fully-qualified path to the associated type",
-                );
-            } else if dedup.len() == assoc_types.len() &&
-                potential_assoc_types.is_empty() &&
-                trait_bounds.len() == 1 &&
-                // Do not attempt to suggest when we don't know which path segment needs the
-                // type parameter set.
-                trait_bounds[0].trait_ref.path.segments.len() == 1
+            } else if let (Ok(snippet), false) =
+                (tcx.sess.source_map().span_to_snippet(*span), dupes)
             {
-                applicability = Applicability::HasPlaceholders;
-                let sugg = assoc_types.join(", ");
-                if snippet.ends_with('>') {
+                let types: Vec<_> =
+                    assoc_items.iter().map(|item| format!("{} = Type", item.ident)).collect();
+                let code = if snippet.ends_with(">") {
                     // The user wrote `Trait<'a>` or similar and we don't have a type we can
                     // suggest, but at least we can clue them to the correct syntax
                     // `Trait<'a, Item = Type>` while accounting for the `<'a>` in the
                     // suggestion.
-                    suggestions.push((
-                        sugg_span,
-                        format!("{}, {}>", &snippet[..snippet.len() - 1], sugg,),
-                    ));
+                    format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", "))
                 } else {
                     // The user wrote `Iterator`, so we don't have a type we can suggest, but at
                     // least we can clue them to the correct syntax `Iterator<Item = Type>`.
-                    suggestions.push((sugg_span, format!("{}<{}>", snippet, sugg)));
-                }
-                suggestions_len = assoc_types.len();
+                    format!("{}<{}>", snippet, types.join(", "))
+                };
+                suggestions.push((*span, code));
+            } else if dupes {
+                where_constraints.push(*span);
             }
         }
+        let where_msg = "consider introducing a new type parameter, adding `where` constraints \
+                         using the fully-qualified path to the associated types";
+        if !where_constraints.is_empty() && suggestions.is_empty() {
+            // If there are duplicates associated type names and a single trait bound do not
+            // use structured suggestion, it means that there are multiple super-traits with
+            // the same associated type name.
+            err.help(where_msg);
+        }
         if suggestions.len() != 1 {
             // We don't need this label if there's an inline suggestion, show otherwise.
-            let names = associated_types
-                .iter()
-                .map(|t| format!("`{}`", tcx.associated_item(*t).ident))
-                .collect::<Vec<_>>()
-                .join(", ");
-            err.span_label(
-                sugg_span,
-                format!(
-                    "associated type{} {} must be specified",
-                    pluralize!(associated_types.len()),
-                    names,
-                ),
-            );
+            for (span, def_ids) in &associated_types {
+                let assoc_items: Vec<_> =
+                    def_ids.iter().map(|def_id| tcx.associated_item(*def_id)).collect();
+                let mut names: FxHashMap<_, usize> = FxHashMap::default();
+                for item in &assoc_items {
+                    types_count += 1;
+                    *names.entry(item.ident.name).or_insert(0) += 1;
+                }
+                let mut label = vec![];
+                for item in &assoc_items {
+                    let postfix = if names[&item.ident.name] > 1 {
+                        let trait_def_id = item.container.id();
+                        format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id))
+                    } else {
+                        String::new()
+                    };
+                    label.push(format!("`{}`{}", item.ident, postfix));
+                }
+                if !label.is_empty() {
+                    err.span_label(
+                        *span,
+                        format!(
+                            "associated type{} {} must be specified",
+                            pluralize!(label.len()),
+                            label.join(", "),
+                        ),
+                    );
+                }
+            }
         }
         if !suggestions.is_empty() {
-            let msg = format!("specify the associated type{}", pluralize!(suggestions_len));
-            err.multipart_suggestion(&msg, suggestions, applicability);
+            err.multipart_suggestion(
+                &format!("specify the associated type{}", pluralize!(types_count)),
+                suggestions,
+                Applicability::HasPlaceholders,
+            );
+            if !where_constraints.is_empty() {
+                err.span_help(where_constraints, where_msg);
+            }
         }
         err.emit();
     }
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 aa46e5d077f..8eb296d4998 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
@@ -49,15 +49,15 @@ error[E0191]: the value of the associated types `Color` (from trait `Vehicle`),
   --> $DIR/associated-type-projection-from-multiple-supertraits.rs:23:30
    |
 LL |     type Color;
-   |     ----------- `Color` defined here
+   |     ----------- `Vehicle::Color` defined here
 ...
 LL |     type Color;
-   |     ----------- `Color` defined here
+   |     ----------- `Box::Color` defined here
 ...
 LL | fn dent_object<COLOR>(c: dyn BoxCar<Color=COLOR>) {
-   |                              ^^^^^^^^^^^^^^^^^^^ associated types `Color`, `Color` must be specified
+   |                              ^^^^^^^^^^^^^^^^^^^ associated types `Color` (from trait `Vehicle`), `Color` (from trait `Box`) must be specified
    |
-   = help: consider introducing a new type parameter, adding `where` constraints using the fully-qualified path to the associated type
+   = help: consider introducing a new type parameter, adding `where` constraints using the fully-qualified path to the associated types
 
 error[E0221]: ambiguous associated type `Color` in bounds of `C`
   --> $DIR/associated-type-projection-from-multiple-supertraits.rs:28:29
@@ -84,15 +84,15 @@ error[E0191]: the value of the associated types `Color` (from trait `Vehicle`),
   --> $DIR/associated-type-projection-from-multiple-supertraits.rs:32:32
    |
 LL |     type Color;
-   |     ----------- `Color` defined here
+   |     ----------- `Vehicle::Color` defined here
 ...
 LL |     type Color;
-   |     ----------- `Color` defined here
+   |     ----------- `Box::Color` defined here
 ...
 LL | fn dent_object_2<COLOR>(c: dyn BoxCar) where <dyn BoxCar as Vehicle>::Color = COLOR {
-   |                                ^^^^^^ associated types `Color`, `Color` must be specified
+   |                                ^^^^^^ associated types `Color` (from trait `Vehicle`), `Color` (from trait `Box`) must be specified
    |
-   = help: consider introducing a new type parameter, adding `where` constraints using the fully-qualified path to the associated type
+   = help: consider introducing a new type parameter, adding `where` constraints using the fully-qualified path to the associated types
 
 error: aborting due to 6 previous errors
 
diff --git a/src/test/ui/associated-types/missing-associated-types.rs b/src/test/ui/associated-types/missing-associated-types.rs
new file mode 100644
index 00000000000..3c8410e39bd
--- /dev/null
+++ b/src/test/ui/associated-types/missing-associated-types.rs
@@ -0,0 +1,27 @@
+use std::ops::{Add, Sub, Mul, Div};
+trait X<Rhs>: Mul<Rhs> + Div<Rhs> {}
+trait Y<Rhs>: Div<Rhs, Output = Rhs> {
+    type A;
+}
+trait Z<Rhs>: Div<Rhs> {
+    type A;
+    type B;
+}
+trait Fine<Rhs>: Div<Rhs, Output = Rhs> {}
+
+type Foo<Rhs> = dyn Add<Rhs> + Sub<Rhs> + X<Rhs> + Y<Rhs>;
+//~^ ERROR only auto traits can be used as additional traits in a trait object
+//~| ERROR the value of the associated types
+type Bar<Rhs> = dyn Add<Rhs> + Sub<Rhs> + X<Rhs> + Z<Rhs>;
+//~^ ERROR only auto traits can be used as additional traits in a trait object
+//~| ERROR the value of the associated types
+type Baz<Rhs> = dyn Add<Rhs> + Sub<Rhs> + Y<Rhs>;
+//~^ ERROR only auto traits can be used as additional traits in a trait object
+//~| ERROR the value of the associated types
+type Bat<Rhs> = dyn Add<Rhs> + Sub<Rhs> + Fine<Rhs>;
+//~^ ERROR only auto traits can be used as additional traits in a trait object
+//~| ERROR the value of the associated types
+type Bal<Rhs> = dyn X<Rhs>;
+//~^ ERROR the value of the associated types
+
+fn main() {}
diff --git a/src/test/ui/associated-types/missing-associated-types.stderr b/src/test/ui/associated-types/missing-associated-types.stderr
new file mode 100644
index 00000000000..7be26638817
--- /dev/null
+++ b/src/test/ui/associated-types/missing-associated-types.stderr
@@ -0,0 +1,129 @@
+error[E0225]: only auto traits can be used as additional traits in a trait object
+  --> $DIR/missing-associated-types.rs:12:32
+   |
+LL | type Foo<Rhs> = dyn Add<Rhs> + Sub<Rhs> + X<Rhs> + Y<Rhs>;
+   |                     --------   ^^^^^^^^
+   |                     |          |
+   |                     |          additional non-auto trait
+   |                     |          trait alias used in trait object type (additional use)
+   |                     first non-auto trait
+   |                     trait alias used in trait object type (first use)
+
+error[E0191]: the value of the associated types `A` (from trait `Y`), `Output` (from trait `std::ops::Mul`), `Output` (from trait `std::ops::Add`), `Output` (from trait `std::ops::Sub`) must be specified
+  --> $DIR/missing-associated-types.rs:12:52
+   |
+LL |     type A;
+   |     ------- `A` defined here
+...
+LL | type Foo<Rhs> = dyn Add<Rhs> + Sub<Rhs> + X<Rhs> + Y<Rhs>;
+   |                     ^^^^^^^^   ^^^^^^^^   ^^^^^^   ^^^^^^ associated type `A` must be specified
+   |                     |          |          |
+   |                     |          |          associated type `Output` must be specified
+   |                     |          associated type `Output` must be specified
+   |                     associated type `Output` must be specified
+   |
+help: specify the associated types
+   |
+LL | type Foo<Rhs> = dyn Add<Rhs, Output = Type> + Sub<Rhs, Output = Type> + X<Rhs, Output = Type> + Y<Rhs, A = Type>;
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^
+
+error[E0225]: only auto traits can be used as additional traits in a trait object
+  --> $DIR/missing-associated-types.rs:15:32
+   |
+LL | type Bar<Rhs> = dyn Add<Rhs> + Sub<Rhs> + X<Rhs> + Z<Rhs>;
+   |                     --------   ^^^^^^^^
+   |                     |          |
+   |                     |          additional non-auto trait
+   |                     |          trait alias used in trait object type (additional use)
+   |                     first non-auto trait
+   |                     trait alias used in trait object type (first use)
+
+error[E0191]: the value of the associated types `Output` (from trait `std::ops::Add`), `Output` (from trait `std::ops::Sub`), `A` (from trait `Z`), `B` (from trait `Z`), `Output` (from trait `std::ops::Div`), `Output` (from trait `std::ops::Mul`), `Output` (from trait `std::ops::Div`) must be specified
+  --> $DIR/missing-associated-types.rs:15:21
+   |
+LL |     type A;
+   |     ------- `A` defined here
+LL |     type B;
+   |     ------- `B` defined here
+...
+LL | type Bar<Rhs> = dyn Add<Rhs> + Sub<Rhs> + X<Rhs> + Z<Rhs>;
+   |                     ^^^^^^^^   ^^^^^^^^   ^^^^^^   ^^^^^^ associated types `A`, `B`, `Output` must be specified
+   |                     |          |          |
+   |                     |          |          associated types `Output` (from trait `std::ops::Mul`), `Output` (from trait `std::ops::Div`) must be specified
+   |                     |          associated type `Output` must be specified
+   |                     associated type `Output` must be specified
+   |
+help: consider introducing a new type parameter, adding `where` constraints using the fully-qualified path to the associated types
+  --> $DIR/missing-associated-types.rs:15:43
+   |
+LL | type Bar<Rhs> = dyn Add<Rhs> + Sub<Rhs> + X<Rhs> + Z<Rhs>;
+   |                                           ^^^^^^
+help: specify the associated types
+   |
+LL | type Bar<Rhs> = dyn Add<Rhs, Output = Type> + Sub<Rhs, Output = Type> + X<Rhs> + Z<Rhs, A = Type, B = Type, Output = Type>;
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^^            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0225]: only auto traits can be used as additional traits in a trait object
+  --> $DIR/missing-associated-types.rs:18:32
+   |
+LL | type Baz<Rhs> = dyn Add<Rhs> + Sub<Rhs> + Y<Rhs>;
+   |                     --------   ^^^^^^^^
+   |                     |          |
+   |                     |          additional non-auto trait
+   |                     |          trait alias used in trait object type (additional use)
+   |                     first non-auto trait
+   |                     trait alias used in trait object type (first use)
+
+error[E0191]: the value of the associated types `Output` (from trait `std::ops::Sub`), `A` (from trait `Y`), `Output` (from trait `std::ops::Add`) must be specified
+  --> $DIR/missing-associated-types.rs:18:32
+   |
+LL |     type A;
+   |     ------- `A` defined here
+...
+LL | type Baz<Rhs> = dyn Add<Rhs> + Sub<Rhs> + Y<Rhs>;
+   |                     ^^^^^^^^   ^^^^^^^^   ^^^^^^ associated type `A` must be specified
+   |                     |          |
+   |                     |          associated type `Output` must be specified
+   |                     associated type `Output` must be specified
+   |
+help: specify the associated types
+   |
+LL | type Baz<Rhs> = dyn Add<Rhs, Output = Type> + Sub<Rhs, Output = Type> + Y<Rhs, A = Type>;
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^
+
+error[E0225]: only auto traits can be used as additional traits in a trait object
+  --> $DIR/missing-associated-types.rs:21:32
+   |
+LL | type Bat<Rhs> = dyn Add<Rhs> + Sub<Rhs> + Fine<Rhs>;
+   |                     --------   ^^^^^^^^
+   |                     |          |
+   |                     |          additional non-auto trait
+   |                     |          trait alias used in trait object type (additional use)
+   |                     first non-auto trait
+   |                     trait alias used in trait object type (first use)
+
+error[E0191]: the value of the associated types `Output` (from trait `std::ops::Sub`), `Output` (from trait `std::ops::Add`) must be specified
+  --> $DIR/missing-associated-types.rs:21:32
+   |
+LL | type Bat<Rhs> = dyn Add<Rhs> + Sub<Rhs> + Fine<Rhs>;
+   |                     ^^^^^^^^   ^^^^^^^^ associated type `Output` must be specified
+   |                     |
+   |                     associated type `Output` must be specified
+   |
+help: specify the associated types
+   |
+LL | type Bat<Rhs> = dyn Add<Rhs, Output = Type> + Sub<Rhs, Output = Type> + Fine<Rhs>;
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0191]: the value of the associated types `Output` (from trait `std::ops::Mul`), `Output` (from trait `std::ops::Div`) must be specified
+  --> $DIR/missing-associated-types.rs:24:21
+   |
+LL | type Bal<Rhs> = dyn X<Rhs>;
+   |                     ^^^^^^ associated types `Output` (from trait `std::ops::Mul`), `Output` (from trait `std::ops::Div`) must be specified
+   |
+   = help: consider introducing a new type parameter, adding `where` constraints using the fully-qualified path to the associated types
+
+error: aborting due to 9 previous errors
+
+Some errors have detailed explanations: E0191, E0225.
+For more information about an error, try `rustc --explain E0191`.
diff --git a/src/test/ui/issues/issue-22560.stderr b/src/test/ui/issues/issue-22560.stderr
index 4466a40f0da..bf80b24fa56 100644
--- a/src/test/ui/issues/issue-22560.stderr
+++ b/src/test/ui/issues/issue-22560.stderr
@@ -35,8 +35,8 @@ LL | type Test = dyn Add + Sub;
    |                 first non-auto trait
    |                 trait alias used in trait object type (first use)
 
-error[E0191]: the value of the associated types `Output` (from trait `Add`), `Output` (from trait `Sub`) must be specified
-  --> $DIR/issue-22560.rs:9:13
+error[E0191]: the value of the associated types `Output` (from trait `Sub`), `Output` (from trait `Add`) must be specified
+  --> $DIR/issue-22560.rs:9:23
    |
 LL |     type Output;
    |     ------------ `Output` defined here
@@ -45,7 +45,14 @@ LL |     type Output;
    |     ------------ `Output` defined here
 ...
 LL | type Test = dyn Add + Sub;
-   |             ^^^^^^^^^^^^^ associated types `Output`, `Output` must be specified
+   |                 ^^^   ^^^ associated type `Output` must be specified
+   |                 |
+   |                 associated type `Output` must be specified
+   |
+help: specify the associated types
+   |
+LL | type Test = dyn Add<Output = Type> + Sub<Output = Type>;
+   |                 ^^^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 4 previous errors
 
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
index 321196d14b7..58a73187fb1 100644
--- 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
@@ -7,7 +7,7 @@ LL |     i: Box<dyn T<usize, usize, usize, usize, B=usize>>,
    |                                unexpected type argument
 
 error[E0191]: the value of the associated types `A` (from trait `T`), `C` (from trait `T`) must be specified
-  --> $DIR/use-type-argument-instead-of-assoc-type.rs:7:12
+  --> $DIR/use-type-argument-instead-of-assoc-type.rs:7:16
    |
 LL |     type A;
    |     ------- `A` defined here
@@ -16,7 +16,7 @@ LL |     type C;
    |     ------- `C` defined here
 ...
 LL |     i: Box<dyn T<usize, usize, usize, usize, B=usize>>,
-   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated types `A`, `C` must be specified
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated types `A`, `C` must be specified
    |
 help: specify the associated types
    |