diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_typeck/src/astconv/errors.rs
index 1a10b07cc2706..38cc74a5e3734 100644
--- a/compiler/rustc_typeck/src/astconv/errors.rs
+++ b/compiler/rustc_typeck/src/astconv/errors.rs
@@ -10,7 +10,6 @@ use rustc_span::symbol::{sym, Ident};
 use rustc_span::{Span, DUMMY_SP};
 
 use std::collections::BTreeSet;
-use std::iter;
 
 impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
     /// On missing type parameters, emit an E0393 error and provide a structured suggestion using
@@ -323,6 +322,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         let mut suggestions = vec![];
         let mut types_count = 0;
         let mut where_constraints = vec![];
+        let mut already_has_generics_args_suggestion = false;
         for (span, assoc_items) in &associated_types {
             let mut names: FxHashMap<_, usize> = FxHashMap::default();
             for item in assoc_items {
@@ -343,16 +343,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 }
             }
             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 associated type's name. The canonical
-                // example would be trying to use `Iterator<isize>` instead of
-                // `Iterator<Item = isize>`.
-                for (potential, item) in iter::zip(&potential_assoc_types, assoc_items) {
-                    if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*potential) {
-                        suggestions.push((*potential, format!("{} = {}", item.name, snippet)));
-                    }
-                }
+                // When the amount of missing associated types equals the number of
+                // extra type arguments present.  A suggesting to replace the generic args with
+                // associated types is already emitted.
+                already_has_generics_args_suggestion = true;
             } else if let (Ok(snippet), false) =
                 (tcx.sess.source_map().span_to_snippet(*span), dupes)
             {
@@ -382,7 +376,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             // the same associated type name.
             err.help(where_msg);
         }
-        if suggestions.len() != 1 {
+        if suggestions.len() != 1 || already_has_generics_args_suggestion {
             // We don't need this label if there's an inline suggestion, show otherwise.
             for (span, assoc_items) in &associated_types {
                 let mut names: FxHashMap<_, usize> = FxHashMap::default();
diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
index 1b1a2037d9eaa..5cd7a7d578e4b 100644
--- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
+++ b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
@@ -6,9 +6,10 @@ use rustc_errors::{
 use rustc_hir as hir;
 use rustc_middle::hir::map::fn_sig;
 use rustc_middle::middle::resolve_lifetime::LifetimeScopeForPath;
-use rustc_middle::ty::{self as ty, TyCtxt};
+use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt};
 use rustc_session::Session;
 use rustc_span::def_id::DefId;
+use std::iter;
 
 use GenericArgsInfo::*;
 
@@ -334,6 +335,22 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
             .join(", ")
     }
 
+    fn get_unbound_associated_types(&self) -> Vec<String> {
+        if self.tcx.is_trait(self.def_id) {
+            let items: &AssocItems<'_> = self.tcx.associated_items(self.def_id);
+            items
+                .in_definition_order()
+                .filter(|item| item.kind == AssocKind::Type)
+                .filter(|item| {
+                    !self.gen_args.bindings.iter().any(|binding| binding.ident.name == item.name)
+                })
+                .map(|item| item.name.to_ident_string())
+                .collect()
+        } else {
+            Vec::default()
+        }
+    }
+
     fn create_error_message(&self) -> String {
         let def_path = self.tcx.def_path_str(self.def_id);
         let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id);
@@ -618,6 +635,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     fn suggest_removing_args_or_generics(&self, err: &mut Diagnostic) {
         let num_provided_lt_args = self.num_provided_lifetime_args();
         let num_provided_type_const_args = self.num_provided_type_or_const_args();
+        let unbound_types = self.get_unbound_associated_types();
         let num_provided_args = num_provided_lt_args + num_provided_type_const_args;
         assert!(num_provided_args > 0);
 
@@ -629,6 +647,8 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
         let redundant_type_or_const_args = num_redundant_type_or_const_args > 0;
 
         let remove_entire_generics = num_redundant_args >= self.gen_args.args.len();
+        let provided_args_matches_unbound_traits =
+            unbound_types.len() == num_redundant_type_or_const_args;
 
         let remove_lifetime_args = |err: &mut Diagnostic| {
             let mut lt_arg_spans = Vec::new();
@@ -713,7 +733,28 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
             );
         };
 
-        if remove_entire_generics {
+        // If there is a single unbound associated type and a single excess generic param
+        // suggest replacing the generic param with the associated type bound
+        if provided_args_matches_unbound_traits && !unbound_types.is_empty() {
+            let mut suggestions = vec![];
+            let unused_generics = &self.gen_args.args[self.num_expected_type_or_const_args()..];
+            for (potential, name) in iter::zip(unused_generics, &unbound_types) {
+                if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(potential.span()) {
+                    suggestions.push((potential.span(), format!("{} = {}", name, snippet)));
+                }
+            }
+
+            if !suggestions.is_empty() {
+                err.multipart_suggestion(
+                    &format!(
+                        "replace the generic bound{s} with the associated type{s}",
+                        s = pluralize!(unbound_types.len())
+                    ),
+                    suggestions,
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        } else if remove_entire_generics {
             let span = self
                 .path_segment
                 .args
diff --git a/src/test/ui/const-generics/issues/issue-87493.stderr b/src/test/ui/const-generics/issues/issue-87493.stderr
index 8f92eeaffd19d..f998c1187d810 100644
--- a/src/test/ui/const-generics/issues/issue-87493.stderr
+++ b/src/test/ui/const-generics/issues/issue-87493.stderr
@@ -13,7 +13,7 @@ error[E0107]: this trait takes 0 generic arguments but 1 generic argument was su
   --> $DIR/issue-87493.rs:8:8
    |
 LL |     T: MyTrait<Assoc == S::Assoc>,
-   |        ^^^^^^^------------------- help: remove these generics
+   |        ^^^^^^^ ----------------- help: replace the generic bound with the associated type: `Assoc = Assoc == S::Assoc`
    |        |
    |        expected 0 generic arguments
    |
diff --git a/src/test/ui/error-codes/E0107.rs b/src/test/ui/error-codes/E0107.rs
index 840700c9cc6d6..d369fc2a5658b 100644
--- a/src/test/ui/error-codes/E0107.rs
+++ b/src/test/ui/error-codes/E0107.rs
@@ -47,4 +47,14 @@ struct Baz<'a, 'b, 'c> {
     //~| HELP remove this lifetime argument
 }
 
+pub trait T {
+    type A;
+    type B;
+}
+
+fn trait_bound_generic<I: T<u8, u16>>(_i: I) {
+    //~^ ERROR this trait takes 0 generic arguments
+    //~| HELP replace the generic bounds with the associated types
+}
+
 fn main() {}
diff --git a/src/test/ui/error-codes/E0107.stderr b/src/test/ui/error-codes/E0107.stderr
index c90f85df96741..5ca03b45d82b3 100644
--- a/src/test/ui/error-codes/E0107.stderr
+++ b/src/test/ui/error-codes/E0107.stderr
@@ -128,6 +128,22 @@ note: struct defined here, with 0 lifetime parameters
 LL | struct Quux<T>(T);
    |        ^^^^
 
-error: aborting due to 9 previous errors
+error[E0107]: this trait takes 0 generic arguments but 2 generic arguments were supplied
+  --> $DIR/E0107.rs:55:27
+   |
+LL | fn trait_bound_generic<I: T<u8, u16>>(_i: I) {
+   |                           ^ expected 0 generic arguments
+   |
+note: trait defined here, with 0 generic parameters
+  --> $DIR/E0107.rs:50:11
+   |
+LL | pub trait T {
+   |           ^
+help: replace the generic bounds with the associated types
+   |
+LL | fn trait_bound_generic<I: T<A = u8, B = u16>>(_i: I) {
+   |                             ~~~~~~  ~~~~~~~
+
+error: aborting due to 10 previous errors
 
 For more information about this error, try `rustc --explain E0107`.
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 5a158e5876a06..5409e32c436fb 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
@@ -2,15 +2,17 @@ error[E0107]: this trait takes 2 generic arguments but 4 generic arguments were
   --> $DIR/use-type-argument-instead-of-assoc-type.rs:7:16
    |
 LL |     i: Box<dyn T<usize, usize, usize, usize, B=usize>>,
-   |                ^               ------------ help: remove these generic arguments
-   |                |
-   |                expected 2 generic arguments
+   |                ^ expected 2 generic arguments
    |
 note: trait defined here, with 2 generic parameters: `X`, `Y`
   --> $DIR/use-type-argument-instead-of-assoc-type.rs:1:11
    |
 LL | pub trait T<X, Y> {
    |           ^ -  -
+help: replace the generic bounds with the associated types
+   |
+LL |     i: Box<dyn T<usize, usize, A = usize, C = usize, B=usize>>,
+   |                                ~~~~~~~~~  ~~~~~~~~~
 
 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:16
@@ -23,11 +25,6 @@ LL |     type C;
 ...
 LL |     i: Box<dyn T<usize, usize, usize, usize, B=usize>>,
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated types `A`, `C` must be specified
-   |
-help: specify the associated types
-   |
-LL |     i: Box<dyn T<usize, usize, A = usize, C = usize, B=usize>>,
-   |                                ~~~~~~~~~  ~~~~~~~~~
 
 error: aborting due to 2 previous errors