From ef121f28d8684d32306becb7577b4e36d7c0ff08 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Mon, 10 Jun 2024 03:38:09 +0200
Subject: [PATCH 1/9] Suggesting an available assoc item is always
 maybe-incorrect

---
 compiler/rustc_hir_analysis/src/errors.rs            |  8 +++++---
 .../rustc_hir_analysis/src/hir_ty_lowering/errors.rs | 12 +-----------
 2 files changed, 6 insertions(+), 14 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index c83788928a978..d1f9ad9d58b7a 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -126,12 +126,14 @@ pub enum AssocItemNotFoundSugg<'a> {
         assoc_kind: &'static str,
         suggested_name: Symbol,
     },
-    #[suggestion(hir_analysis_assoc_item_not_found_other_sugg, code = "{suggested_name}")]
+    #[suggestion(
+        hir_analysis_assoc_item_not_found_other_sugg,
+        code = "{suggested_name}",
+        applicability = "maybe-incorrect"
+    )]
     Other {
         #[primary_span]
         span: Span,
-        #[applicability]
-        applicability: Applicability,
         ty_param_name: &'a str,
         assoc_kind: &'static str,
         suggested_name: Symbol,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index 8ff6ced8b3983..40675a1edba7a 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -269,20 +269,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         }
 
         // If we still couldn't find any associated item, and only one associated item exists,
-        // suggests using it.
+        // suggest using it.
         if let [candidate_name] = all_candidate_names.as_slice() {
-            // This should still compile, except on `#![feature(associated_type_defaults)]`
-            // where it could suggests `type A = Self::A`, thus recursing infinitely.
-            let applicability =
-                if assoc_kind == ty::AssocKind::Type && tcx.features().associated_type_defaults {
-                    Applicability::Unspecified
-                } else {
-                    Applicability::MaybeIncorrect
-                };
-
             err.sugg = Some(errors::AssocItemNotFoundSugg::Other {
                 span: assoc_name.span,
-                applicability,
                 ty_param_name,
                 assoc_kind: assoc_kind_str,
                 suggested_name: *candidate_name,

From 898448ca5872aed82920dbb8342096e25e50aa5a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Fri, 14 Jun 2024 12:28:23 +0200
Subject: [PATCH 2/9] HIR ty lowering: Refactor the way the projectee ("QSelf")
 gets passed to diagnostics

---
 compiler/rustc_hir_analysis/messages.ftl      |  6 +-
 compiler/rustc_hir_analysis/src/errors.rs     |  6 +-
 .../src/hir_ty_lowering/bounds.rs             |  9 +--
 .../src/hir_ty_lowering/errors.rs             | 59 +++++++++------
 .../src/hir_ty_lowering/mod.rs                | 75 ++++++++++---------
 5 files changed, 86 insertions(+), 69 deletions(-)

diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index cc404daa51f59..c209bf20c437c 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -1,4 +1,4 @@
-hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$ty_param_name}`
+hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$qself}`
     .label = ambiguous associated {$assoc_kind} `{$assoc_name}`
 
 hir_analysis_ambiguous_lifetime_bound =
@@ -12,14 +12,14 @@ hir_analysis_assoc_item_is_private = {$kind} `{$name}` is private
     .label = private {$kind}
     .defined_here_label = the {$kind} is defined here
 
-hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$ty_param_name}`
+hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$qself}`
 
 hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named ->
         [true] an
         *[false] a similarly named
     } associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}`
 hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found
-hir_analysis_assoc_item_not_found_other_sugg = `{$ty_param_name}` has the following associated {$assoc_kind}
+hir_analysis_assoc_item_not_found_other_sugg = `{$qself}` has the following associated {$assoc_kind}
 hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}`
 hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = and also change the associated {$assoc_kind} name
 hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index d1f9ad9d58b7a..8e8b3b3d32aa9 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -22,7 +22,7 @@ pub struct AmbiguousAssocItem<'a> {
     pub span: Span,
     pub assoc_kind: &'static str,
     pub assoc_name: Ident,
-    pub ty_param_name: &'a str,
+    pub qself: &'a str,
 }
 
 #[derive(Diagnostic)]
@@ -75,7 +75,7 @@ pub struct AssocItemNotFound<'a> {
     pub span: Span,
     pub assoc_name: Ident,
     pub assoc_kind: &'static str,
-    pub ty_param_name: &'a str,
+    pub qself: &'a str,
     #[subdiagnostic]
     pub label: Option<AssocItemNotFoundLabel<'a>>,
     #[subdiagnostic]
@@ -134,7 +134,7 @@ pub enum AssocItemNotFoundSugg<'a> {
     Other {
         #[primary_span]
         span: Span,
-        ty_param_name: &'a str,
+        qself: &'a str,
         assoc_kind: &'static str,
         suggested_name: Symbol,
     },
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index 6f9c481650b21..214eb6b2f06d5 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -6,7 +6,6 @@ use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::bug;
-use rustc_middle::ty::print::PrintTraitRefExt as _;
 use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt};
 use rustc_span::symbol::Ident;
 use rustc_span::{ErrorGuaranteed, Span, Symbol};
@@ -16,9 +15,8 @@ use smallvec::SmallVec;
 
 use crate::bounds::Bounds;
 use crate::errors;
-use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter};
-
-use super::RegionInferReason;
+use crate::hir_ty_lowering::HirTyLowerer;
+use crate::hir_ty_lowering::{AssocItemQSelf, OnlySelfBounds, PredicateFilter, RegionInferReason};
 
 impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     /// Add a `Sized` bound to the `bounds` if appropriate.
@@ -288,8 +286,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             // one that does define it.
             self.probe_single_bound_for_assoc_item(
                 || traits::supertraits(tcx, trait_ref),
-                trait_ref.skip_binder().print_only_trait_name(),
-                None,
+                AssocItemQSelf::Trait(trait_ref.def_id()),
                 assoc_kind,
                 constraint.ident,
                 path_span,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index 40675a1edba7a..ca741e4fbb6eb 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -3,7 +3,7 @@ use crate::errors::{
     ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits,
 };
 use crate::fluent_generated as fluent;
-use crate::hir_ty_lowering::HirTyLowerer;
+use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer};
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::unord::UnordMap;
@@ -11,9 +11,9 @@ use rustc_errors::MultiSpan;
 use rustc_errors::{
     codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed,
 };
+use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::{self as hir, Node};
+use rustc_hir::def_id::DefId;
 use rustc_middle::bug;
 use rustc_middle::query::Key;
 use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _};
@@ -116,8 +116,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     pub(super) fn complain_about_assoc_item_not_found<I>(
         &self,
         all_candidates: impl Fn() -> I,
-        ty_param_name: &str,
-        ty_param_def_id: Option<LocalDefId>,
+        qself: AssocItemQSelf,
         assoc_kind: ty::AssocKind,
         assoc_name: Ident,
         span: Span,
@@ -139,7 +138,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             );
         }
 
-        let assoc_kind_str = super::assoc_kind_str(assoc_kind);
+        let assoc_kind_str = assoc_kind_str(assoc_kind);
+        let qself_str = qself.to_string(tcx);
 
         // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a
         // valid span, so we point at the whole path segment instead.
@@ -149,7 +149,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             span: if is_dummy { span } else { assoc_name.span },
             assoc_name,
             assoc_kind: assoc_kind_str,
-            ty_param_name,
+            qself: &qself_str,
             label: None,
             sugg: None,
         };
@@ -219,19 +219,28 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     suggested_name,
                     identically_named: suggested_name == assoc_name.name,
                 });
-                let hir = tcx.hir();
-                if let Some(def_id) = ty_param_def_id
-                    && let parent = hir.get_parent_item(tcx.local_def_id_to_hir_id(def_id))
-                    && let Some(generics) = hir.get_generics(parent.def_id)
+                if let AssocItemQSelf::TyParam(ty_param_def_id) = qself
+                    // Not using `self.item_def_id()` here as that would yield the opaque type itself if we're
+                    // inside an opaque type while we're interested in the overarching type alias (TAIT).
+                    // FIXME: However, for trait aliases, this incorrectly returns the enclosing module...
+                    && let item_def_id =
+                        tcx.hir().get_parent_item(tcx.local_def_id_to_hir_id(ty_param_def_id))
+                    // FIXME: ...which obviously won't have any generics.
+                    && let Some(generics) = tcx.hir().get_generics(item_def_id.def_id)
                 {
-                    if generics.bounds_for_param(def_id).flat_map(|pred| pred.bounds.iter()).any(
-                        |b| match b {
+                    // FIXME: Suggest adding supertrait bounds if we have a `Self` type param.
+                    // FIXME(trait_alias): Suggest adding `Self: Trait` to
+                    // `trait Alias = where Self::Proj:;` with `trait Trait { type Proj; }`.
+                    if generics
+                        .bounds_for_param(ty_param_def_id)
+                        .flat_map(|pred| pred.bounds.iter())
+                        .any(|b| match b {
                             hir::GenericBound::Trait(t, ..) => {
                                 t.trait_ref.trait_def_id() == Some(best_trait)
                             }
                             _ => false,
-                        },
-                    ) {
+                        })
+                    {
                         // The type param already has a bound for `trait_name`, we just need to
                         // change the associated item.
                         err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait {
@@ -247,7 +256,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         tcx,
                         generics,
                         &mut err,
-                        &ty_param_name,
+                        &qself_str,
                         &trait_name,
                         None,
                         None,
@@ -273,7 +282,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         if let [candidate_name] = all_candidate_names.as_slice() {
             err.sugg = Some(errors::AssocItemNotFoundSugg::Other {
                 span: assoc_name.span,
-                ty_param_name,
+                qself: &qself_str,
                 assoc_kind: assoc_kind_str,
                 suggested_name: *candidate_name,
             });
@@ -339,10 +348,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
         self.dcx().emit_err(errors::AssocKindMismatch {
             span,
-            expected: super::assoc_kind_str(expected),
-            got: super::assoc_kind_str(got),
+            expected: assoc_kind_str(expected),
+            got: assoc_kind_str(got),
             expected_because_label,
-            assoc_kind: super::assoc_kind_str(assoc_item.kind),
+            assoc_kind: assoc_kind_str(assoc_item.kind),
             def_span: tcx.def_span(assoc_item.def_id),
             bound_on_assoc_const_label,
             wrap_in_braces_sugg,
@@ -736,7 +745,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
             let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id));
             in_expr_or_pat = match grandparent {
-                Node::Expr(_) | Node::Pat(_) => true,
+                hir::Node::Expr(_) | hir::Node::Pat(_) => true,
                 _ => false,
             };
             match bound.trait_ref.path.segments {
@@ -1602,3 +1611,11 @@ fn generics_args_err_extend<'a>(
         _ => {}
     }
 }
+
+pub(super) fn assoc_kind_str(kind: ty::AssocKind) -> &'static str {
+    match kind {
+        ty::AssocKind::Fn => "function",
+        ty::AssocKind::Const => "constant",
+        ty::AssocKind::Type => "type",
+    }
+}
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index d6eb1a66902fc..8d9055076a3bb 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -55,7 +55,6 @@ use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::wf::object_region_bounds;
 use rustc_trait_selection::traits::{self, ObligationCtxt};
 
-use std::fmt::Display;
 use std::slice;
 
 /// A path segment that is semantically allowed to have generic arguments.
@@ -193,6 +192,25 @@ pub trait HirTyLowerer<'tcx> {
     }
 }
 
+/// The "qualified self" of an associated item path.
+///
+/// For diagnostic purposes only.
+enum AssocItemQSelf {
+    Trait(DefId),
+    TyParam(LocalDefId),
+    SelfTyAlias,
+}
+
+impl AssocItemQSelf {
+    fn to_string(&self, tcx: TyCtxt<'_>) -> String {
+        match *self {
+            Self::Trait(def_id) => tcx.def_path_str(def_id),
+            Self::TyParam(def_id) => tcx.hir().ty_param_name(def_id).to_string(),
+            Self::SelfTyAlias => kw::SelfUpper.to_string(),
+        }
+    }
+}
+
 /// New-typed boolean indicating whether explicit late-bound lifetimes
 /// are present in a set of generic arguments.
 ///
@@ -811,19 +829,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_name).predicates;
         debug!("predicates={:#?}", predicates);
 
-        let param_name = tcx.hir().ty_param_name(ty_param_def_id);
         self.probe_single_bound_for_assoc_item(
             || {
-                traits::transitive_bounds_that_define_assoc_item(
-                    tcx,
-                    predicates
-                        .iter()
-                        .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))),
-                    assoc_name,
-                )
+                let trait_refs = predicates
+                    .iter()
+                    .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref)));
+                traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name)
             },
-            param_name,
-            Some(ty_param_def_id),
+            AssocItemQSelf::TyParam(ty_param_def_id),
             ty::AssocKind::Type,
             assoc_name,
             span,
@@ -835,12 +848,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     ///
     /// This fails if there is no such bound in the list of candidates or if there are multiple
     /// candidates in which case it reports ambiguity.
-    #[instrument(level = "debug", skip(self, all_candidates, ty_param_name, constraint), ret)]
+    #[instrument(level = "debug", skip(self, all_candidates, qself, constraint), ret)]
     fn probe_single_bound_for_assoc_item<I>(
         &self,
         all_candidates: impl Fn() -> I,
-        ty_param_name: impl Display,
-        ty_param_def_id: Option<LocalDefId>,
+        qself: AssocItemQSelf,
         assoc_kind: ty::AssocKind,
         assoc_name: Ident,
         span: Span,
@@ -858,8 +870,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let Some(bound) = matching_candidates.next() else {
             let reported = self.complain_about_assoc_item_not_found(
                 all_candidates,
-                &ty_param_name.to_string(),
-                ty_param_def_id,
+                qself,
                 assoc_kind,
                 assoc_name,
                 span,
@@ -872,13 +883,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         if let Some(bound2) = matching_candidates.next() {
             debug!(?bound2);
 
-            let assoc_kind_str = assoc_kind_str(assoc_kind);
-            let ty_param_name = &ty_param_name.to_string();
+            let assoc_kind_str = errors::assoc_kind_str(assoc_kind);
+            let qself_str = qself.to_string(tcx);
             let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem {
                 span,
                 assoc_kind: assoc_kind_str,
                 assoc_name,
-                ty_param_name,
+                qself: &qself_str,
             });
             // Provide a more specific error code index entry for equality bindings.
             err.code(
@@ -929,7 +940,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         err.span_suggestion_verbose(
                             span.with_hi(assoc_name.span.lo()),
                             "use fully-qualified syntax to disambiguate",
-                            format!("<{ty_param_name} as {}>::", bound.print_only_trait_path()),
+                            format!("<{qself_str} as {}>::", bound.print_only_trait_path()),
                             Applicability::MaybeIncorrect,
                         );
                     }
@@ -943,7 +954,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             if !where_bounds.is_empty() {
                 err.help(format!(
                     "consider introducing a new type parameter `T` and adding `where` constraints:\
-                     \n    where\n        T: {ty_param_name},\n{}",
+                     \n    where\n        T: {qself_str},\n{}",
                     where_bounds.join(",\n"),
                 ));
             }
@@ -997,11 +1008,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let tcx = self.tcx();
 
         let assoc_ident = assoc_segment.ident;
-        let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
-            path.res
-        } else {
-            Res::Err
-        };
 
         // Check if we have an enum variant or an inherent associated type.
         let mut variant_resolution = None;
@@ -1038,6 +1044,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             }
         }
 
+        let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind {
+            path.res
+        } else {
+            Res::Err
+        };
+
         // Find the type of the associated item, and the trait where the associated
         // item is declared.
         let bound = match (&qself_ty.kind(), qself_res) {
@@ -1056,8 +1068,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                             ty::Binder::dummy(trait_ref.instantiate_identity()),
                         )
                     },
-                    kw::SelfUpper,
-                    None,
+                    AssocItemQSelf::SelfTyAlias,
                     ty::AssocKind::Type,
                     assoc_ident,
                     span,
@@ -2522,11 +2533,3 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         Some(r)
     }
 }
-
-fn assoc_kind_str(kind: ty::AssocKind) -> &'static str {
-    match kind {
-        ty::AssocKind::Fn => "function",
-        ty::AssocKind::Const => "constant",
-        ty::AssocKind::Type => "type",
-    }
-}

From 3c8b1085127b64de64cb6d880b59b55c1d6fc8e9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Fri, 14 Jun 2024 12:38:21 +0200
Subject: [PATCH 3/9] Inside eager ty aliases on unresolved assoc tys suggest
 fully qualifying instead of restricting their self ty

---
 compiler/rustc_hir_analysis/messages.ftl      |  7 ++-
 compiler/rustc_hir_analysis/src/errors.rs     | 16 ++++++
 .../src/hir_ty_lowering/errors.rs             | 55 ++++++++++++-------
 .../src/hir_ty_lowering/mod.rs                |  8 ++-
 tests/ui/resolve/issue-55673.stderr           |  2 +-
 ...solved-assoc-ty-suggest-trait.eager.stderr | 25 +++++++++
 ...esolved-assoc-ty-suggest-trait.lazy.stderr | 29 ++++++++++
 .../unresolved-assoc-ty-suggest-trait.rs      | 20 +++++++
 8 files changed, 138 insertions(+), 24 deletions(-)
 create mode 100644 tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr
 create mode 100644 tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr
 create mode 100644 tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs

diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index c209bf20c437c..367f6e17e7fe6 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -20,8 +20,13 @@ hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identi
     } associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}`
 hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found
 hir_analysis_assoc_item_not_found_other_sugg = `{$qself}` has the following associated {$assoc_kind}
+hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg =
+    consider fully qualifying{$identically_named ->
+        [true] {""}
+        *[false] {" "}and renaming
+    } the associated {$assoc_kind}
 hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}`
-hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = and also change the associated {$assoc_kind} name
+hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = ...and changing the associated {$assoc_kind} name
 hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name
 
 hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got}
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 8e8b3b3d32aa9..90861ba1ff840 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -126,6 +126,22 @@ pub enum AssocItemNotFoundSugg<'a> {
         assoc_kind: &'static str,
         suggested_name: Symbol,
     },
+    #[multipart_suggestion(
+        hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg,
+        style = "verbose",
+        applicability = "maybe-incorrect"
+    )]
+    SimilarInOtherTraitQPath {
+        #[suggestion_part(code = "<")]
+        lo: Span,
+        #[suggestion_part(code = " as {trait_}>")]
+        mi: Span,
+        #[suggestion_part(code = "{suggested_name}")]
+        hi: Option<Span>,
+        trait_: &'a str,
+        suggested_name: Symbol,
+        identically_named: bool,
+    },
     #[suggestion(
         hir_analysis_assoc_item_not_found_other_sugg,
         code = "{suggested_name}",
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index ca741e4fbb6eb..fd2bbabe779de 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -219,7 +219,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     suggested_name,
                     identically_named: suggested_name == assoc_name.name,
                 });
-                if let AssocItemQSelf::TyParam(ty_param_def_id) = qself
+                if let AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span) = qself
                     // Not using `self.item_def_id()` here as that would yield the opaque type itself if we're
                     // inside an opaque type while we're interested in the overarching type alias (TAIT).
                     // FIXME: However, for trait aliases, this incorrectly returns the enclosing module...
@@ -251,27 +251,44 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         return self.dcx().emit_err(err);
                     }
 
-                    let mut err = self.dcx().create_err(err);
-                    if suggest_constraining_type_param(
-                        tcx,
-                        generics,
-                        &mut err,
-                        &qself_str,
-                        &trait_name,
-                        None,
-                        None,
-                    ) && suggested_name != assoc_name.name
+                    let identically_named = suggested_name == assoc_name.name;
+
+                    if let DefKind::TyAlias = tcx.def_kind(item_def_id)
+                        && !tcx.type_alias_is_lazy(item_def_id)
                     {
-                        // We suggested constraining a type parameter, but the associated item on it
-                        // was also not an exact match, so we also suggest changing it.
-                        err.span_suggestion_verbose(
-                            assoc_name.span,
-                            fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg,
+                        err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTraitQPath {
+                            lo: ty_param_span.shrink_to_lo(),
+                            mi: ty_param_span.shrink_to_hi(),
+                            hi: (!identically_named).then_some(assoc_name.span),
+                            // FIXME(fmease): Use a full trait ref here (with placeholders).
+                            trait_: &trait_name,
+                            identically_named,
                             suggested_name,
-                            Applicability::MaybeIncorrect,
-                        );
+                        });
+                    } else {
+                        let mut err = self.dcx().create_err(err);
+                        if suggest_constraining_type_param(
+                            tcx,
+                            generics,
+                            &mut err,
+                            &qself_str,
+                            // FIXME(fmease): Use a full trait ref here (with placeholders).
+                            &trait_name,
+                            None,
+                            None,
+                        ) && !identically_named
+                        {
+                            // We suggested constraining a type parameter, but the associated item on it
+                            // was also not an exact match, so we also suggest changing it.
+                            err.span_suggestion_verbose(
+                                assoc_name.span,
+                                fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg,
+                                suggested_name,
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+                        return err.emit();
                     }
-                    return err.emit();
                 }
                 return self.dcx().emit_err(err);
             }
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 8d9055076a3bb..ce298641e6060 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -197,7 +197,7 @@ pub trait HirTyLowerer<'tcx> {
 /// For diagnostic purposes only.
 enum AssocItemQSelf {
     Trait(DefId),
-    TyParam(LocalDefId),
+    TyParam(LocalDefId, Span),
     SelfTyAlias,
 }
 
@@ -205,7 +205,7 @@ impl AssocItemQSelf {
     fn to_string(&self, tcx: TyCtxt<'_>) -> String {
         match *self {
             Self::Trait(def_id) => tcx.def_path_str(def_id),
-            Self::TyParam(def_id) => tcx.hir().ty_param_name(def_id).to_string(),
+            Self::TyParam(def_id, _) => tcx.hir().ty_param_name(def_id).to_string(),
             Self::SelfTyAlias => kw::SelfUpper.to_string(),
         }
     }
@@ -820,6 +820,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     fn probe_single_ty_param_bound_for_assoc_ty(
         &self,
         ty_param_def_id: LocalDefId,
+        ty_param_span: Span,
         assoc_name: Ident,
         span: Span,
     ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> {
@@ -836,7 +837,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref)));
                 traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name)
             },
-            AssocItemQSelf::TyParam(ty_param_def_id),
+            AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span),
             ty::AssocKind::Type,
             assoc_name,
             span,
@@ -1080,6 +1081,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did),
             ) => self.probe_single_ty_param_bound_for_assoc_ty(
                 param_did.expect_local(),
+                qself.span,
                 assoc_ident,
                 span,
             )?,
diff --git a/tests/ui/resolve/issue-55673.stderr b/tests/ui/resolve/issue-55673.stderr
index ffc3252230ab8..4069b35a99848 100644
--- a/tests/ui/resolve/issue-55673.stderr
+++ b/tests/ui/resolve/issue-55673.stderr
@@ -19,7 +19,7 @@ help: consider further restricting type parameter `T`
    |
 LL |     T::Baa: std::fmt::Debug, T: Foo
    |                            ~~~~~~~~
-help: and also change the associated type name
+help: ...and changing the associated type name
    |
 LL |     T::Bar: std::fmt::Debug,
    |        ~~~
diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr
new file mode 100644
index 0000000000000..8e6490c548d1c
--- /dev/null
+++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr
@@ -0,0 +1,25 @@
+error[E0220]: associated type `Assoc` not found for `T`
+  --> $DIR/unresolved-assoc-ty-suggest-trait.rs:11:22
+   |
+LL | type AssocOf<T> = T::Assoc;
+   |                      ^^^^^ there is an associated type `Assoc` in the trait `Trait`
+   |
+help: consider fully qualifying the associated type
+   |
+LL | type AssocOf<T> = <T as Trait>::Assoc;
+   |                   +  +++++++++
+
+error[E0220]: associated type `Assok` not found for `T`
+  --> $DIR/unresolved-assoc-ty-suggest-trait.rs:15:22
+   |
+LL | type AssokOf<T> = T::Assok;
+   |                      ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait`
+   |
+help: consider fully qualifying and renaming the associated type
+   |
+LL | type AssokOf<T> = <T as Trait>::Assoc;
+   |                   +  +++++++++  ~~~~~
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0220`.
diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr
new file mode 100644
index 0000000000000..051f181cb6fe4
--- /dev/null
+++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr
@@ -0,0 +1,29 @@
+error[E0220]: associated type `Assoc` not found for `T`
+  --> $DIR/unresolved-assoc-ty-suggest-trait.rs:11:22
+   |
+LL | type AssocOf<T> = T::Assoc;
+   |                      ^^^^^ there is an associated type `Assoc` in the trait `Trait`
+   |
+help: consider restricting type parameter `T`
+   |
+LL | type AssocOf<T: Trait> = T::Assoc;
+   |               +++++++
+
+error[E0220]: associated type `Assok` not found for `T`
+  --> $DIR/unresolved-assoc-ty-suggest-trait.rs:15:22
+   |
+LL | type AssokOf<T> = T::Assok;
+   |                      ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait`
+   |
+help: consider restricting type parameter `T`
+   |
+LL | type AssokOf<T: Trait> = T::Assok;
+   |               +++++++
+help: ...and changing the associated type name
+   |
+LL | type AssokOf<T> = T::Assoc;
+   |                      ~~~~~
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0220`.
diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs
new file mode 100644
index 0000000000000..75e4eb496b414
--- /dev/null
+++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs
@@ -0,0 +1,20 @@
+// Ensure that we don't suggest *type alias bounds* for **eager** type aliases.
+// issue: rust-lang/rust#125789
+
+//@ revisions: eager lazy
+#![cfg_attr(lazy, feature(lazy_type_alias), allow(incomplete_features))]
+
+// FIXME(fmease): Suggest a full trait ref (with placeholders) instead of just a trait name.
+
+trait Trait<T> { type Assoc; }
+
+type AssocOf<T> = T::Assoc; //~ ERROR associated type `Assoc` not found for `T`
+//[eager]~^ HELP consider fully qualifying the associated type
+//[lazy]~| HELP consider restricting type parameter `T`
+
+type AssokOf<T> = T::Assok; //~ ERROR associated type `Assok` not found for `T`
+//[eager]~^ HELP consider fully qualifying and renaming the associated type
+//[lazy]~| HELP consider restricting type parameter `T`
+//[lazy]~| HELP and changing the associated type name
+
+fn main() {}

From 02a2f0272736c65b51996b0c6e7cbd991ac21d5c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Sat, 15 Jun 2024 11:08:12 +0200
Subject: [PATCH 4/9] Suggest full trait ref (with placeholders) on unresolved
 assoc tys

---
 compiler/rustc_hir_analysis/src/errors.rs     |  9 ++++---
 .../src/hir_ty_lowering/errors.rs             | 25 +++++++++++--------
 ...solved-assoc-ty-suggest-trait.eager.stderr | 17 ++++++++++---
 ...esolved-assoc-ty-suggest-trait.lazy.stderr | 17 ++++++++++---
 .../unresolved-assoc-ty-suggest-trait.rs      | 12 ++++++---
 5 files changed, 57 insertions(+), 23 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 90861ba1ff840..c364a56163106 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -128,19 +128,20 @@ pub enum AssocItemNotFoundSugg<'a> {
     },
     #[multipart_suggestion(
         hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg,
-        style = "verbose",
-        applicability = "maybe-incorrect"
+        style = "verbose"
     )]
     SimilarInOtherTraitQPath {
         #[suggestion_part(code = "<")]
         lo: Span,
-        #[suggestion_part(code = " as {trait_}>")]
+        #[suggestion_part(code = " as {trait_ref}>")]
         mi: Span,
         #[suggestion_part(code = "{suggested_name}")]
         hi: Option<Span>,
-        trait_: &'a str,
+        trait_ref: String,
         suggested_name: Symbol,
         identically_named: bool,
+        #[applicability]
+        applicability: Applicability,
     },
     #[suggestion(
         hir_analysis_assoc_item_not_found_other_sugg,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index fd2bbabe779de..56f508a2d43e1 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -251,6 +251,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         return self.dcx().emit_err(err);
                     }
 
+                    let trait_args = &ty::GenericArgs::identity_for_item(tcx, best_trait)[1..];
+                    let mut trait_ref = trait_name.clone();
+                    let applicability = if let [arg, args @ ..] = trait_args {
+                        use std::fmt::Write;
+                        write!(trait_ref, "</* {arg}").unwrap();
+                        args.iter().try_for_each(|arg| write!(trait_ref, ", {arg}")).unwrap();
+                        trait_ref += " */>";
+                        Applicability::HasPlaceholders
+                    } else {
+                        Applicability::MaybeIncorrect
+                    };
+
                     let identically_named = suggested_name == assoc_name.name;
 
                     if let DefKind::TyAlias = tcx.def_kind(item_def_id)
@@ -260,22 +272,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                             lo: ty_param_span.shrink_to_lo(),
                             mi: ty_param_span.shrink_to_hi(),
                             hi: (!identically_named).then_some(assoc_name.span),
-                            // FIXME(fmease): Use a full trait ref here (with placeholders).
-                            trait_: &trait_name,
+                            trait_ref,
                             identically_named,
                             suggested_name,
+                            applicability,
                         });
                     } else {
                         let mut err = self.dcx().create_err(err);
                         if suggest_constraining_type_param(
-                            tcx,
-                            generics,
-                            &mut err,
-                            &qself_str,
-                            // FIXME(fmease): Use a full trait ref here (with placeholders).
-                            &trait_name,
-                            None,
-                            None,
+                            tcx, generics, &mut err, &qself_str, &trait_ref, None, None,
                         ) && !identically_named
                         {
                             // We suggested constraining a type parameter, but the associated item on it
diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr
index 8e6490c548d1c..e891ff10fdaa9 100644
--- a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr
+++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr
@@ -1,5 +1,5 @@
 error[E0220]: associated type `Assoc` not found for `T`
-  --> $DIR/unresolved-assoc-ty-suggest-trait.rs:11:22
+  --> $DIR/unresolved-assoc-ty-suggest-trait.rs:9:22
    |
 LL | type AssocOf<T> = T::Assoc;
    |                      ^^^^^ there is an associated type `Assoc` in the trait `Trait`
@@ -10,7 +10,7 @@ LL | type AssocOf<T> = <T as Trait>::Assoc;
    |                   +  +++++++++
 
 error[E0220]: associated type `Assok` not found for `T`
-  --> $DIR/unresolved-assoc-ty-suggest-trait.rs:15:22
+  --> $DIR/unresolved-assoc-ty-suggest-trait.rs:13:22
    |
 LL | type AssokOf<T> = T::Assok;
    |                      ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait`
@@ -20,6 +20,17 @@ help: consider fully qualifying and renaming the associated type
 LL | type AssokOf<T> = <T as Trait>::Assoc;
    |                   +  +++++++++  ~~~~~
 
-error: aborting due to 2 previous errors
+error[E0220]: associated type `Proj` not found for `T`
+  --> $DIR/unresolved-assoc-ty-suggest-trait.rs:22:21
+   |
+LL | type ProjOf<T> = T::Proj;
+   |                     ^^^^ there is an associated type `Proj` in the trait `Parametrized`
+   |
+help: consider fully qualifying the associated type
+   |
+LL | type ProjOf<T> = <T as Parametrized</* 'a, T, N */>>::Proj;
+   |                  +  ++++++++++++++++++++++++++++++++
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0220`.
diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr
index 051f181cb6fe4..96179a7b48466 100644
--- a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr
+++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr
@@ -1,5 +1,5 @@
 error[E0220]: associated type `Assoc` not found for `T`
-  --> $DIR/unresolved-assoc-ty-suggest-trait.rs:11:22
+  --> $DIR/unresolved-assoc-ty-suggest-trait.rs:9:22
    |
 LL | type AssocOf<T> = T::Assoc;
    |                      ^^^^^ there is an associated type `Assoc` in the trait `Trait`
@@ -10,7 +10,7 @@ LL | type AssocOf<T: Trait> = T::Assoc;
    |               +++++++
 
 error[E0220]: associated type `Assok` not found for `T`
-  --> $DIR/unresolved-assoc-ty-suggest-trait.rs:15:22
+  --> $DIR/unresolved-assoc-ty-suggest-trait.rs:13:22
    |
 LL | type AssokOf<T> = T::Assok;
    |                      ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait`
@@ -24,6 +24,17 @@ help: ...and changing the associated type name
 LL | type AssokOf<T> = T::Assoc;
    |                      ~~~~~
 
-error: aborting due to 2 previous errors
+error[E0220]: associated type `Proj` not found for `T`
+  --> $DIR/unresolved-assoc-ty-suggest-trait.rs:22:21
+   |
+LL | type ProjOf<T> = T::Proj;
+   |                     ^^^^ there is an associated type `Proj` in the trait `Parametrized`
+   |
+help: consider restricting type parameter `T`
+   |
+LL | type ProjOf<T: Parametrized</* 'a, T, N */>> = T::Proj;
+   |              ++++++++++++++++++++++++++++++
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0220`.
diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs
index 75e4eb496b414..2c8d448f3083e 100644
--- a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs
+++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs
@@ -4,9 +4,7 @@
 //@ revisions: eager lazy
 #![cfg_attr(lazy, feature(lazy_type_alias), allow(incomplete_features))]
 
-// FIXME(fmease): Suggest a full trait ref (with placeholders) instead of just a trait name.
-
-trait Trait<T> { type Assoc; }
+trait Trait { type Assoc; }
 
 type AssocOf<T> = T::Assoc; //~ ERROR associated type `Assoc` not found for `T`
 //[eager]~^ HELP consider fully qualifying the associated type
@@ -17,4 +15,12 @@ type AssokOf<T> = T::Assok; //~ ERROR associated type `Assok` not found for `T`
 //[lazy]~| HELP consider restricting type parameter `T`
 //[lazy]~| HELP and changing the associated type name
 
+trait Parametrized<'a, T, const N: usize> {
+    type Proj;
+}
+
+type ProjOf<T> = T::Proj; //~ ERROR associated type `Proj` not found for `T`
+//[eager]~^ HELP consider fully qualifying the associated type
+//[lazy]~| HELP consider restricting type parameter `T`
+
 fn main() {}

From 63a54d93be33bb1c0357c48532df778f6f2a416b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Fri, 14 Jun 2024 15:30:11 +0200
Subject: [PATCH 5/9] Don't suppress lint type_alias_bounds for ty aliases
 containing inherent assoc tys

---
 compiler/rustc_lint/src/builtin.rs            | 28 +++++++-----------
 compiler/rustc_type_ir/src/visit.rs           |  4 ---
 .../type-alias-bounds-are-enforced.rs         | 20 -------------
 .../type-alias-bounds.rs                      | 29 +++++++++++++++++++
 .../type-alias-bounds.stderr                  | 15 ++++++++++
 5 files changed, 55 insertions(+), 41 deletions(-)
 delete mode 100644 tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs
 create mode 100644 tests/ui/associated-inherent-types/type-alias-bounds.rs
 create mode 100644 tests/ui/associated-inherent-types/type-alias-bounds.stderr

diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 9ebada0fff377..40b336cd5ea0c 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -1425,30 +1425,27 @@ impl TypeAliasBounds {
 
 impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
     fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
-        let hir::ItemKind::TyAlias(hir_ty, type_alias_generics) = &item.kind else { return };
+        let hir::ItemKind::TyAlias(hir_ty, generics) = &item.kind else { return };
 
-        // Bounds of lazy type aliases and TAITs are respected.
-        if cx.tcx.type_alias_is_lazy(item.owner_id) {
+        // There must not be a where clause.
+        if generics.predicates.is_empty() {
             return;
         }
 
-        let ty = cx.tcx.type_of(item.owner_id).skip_binder();
-        if ty.has_inherent_projections() {
-            // Bounds of type aliases that contain opaque types or inherent projections are
-            // respected. E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`, `type X =
-            // Type::Inherent;`.
+        // Bounds of lazy type aliases and TAITs are respected.
+        if cx.tcx.type_alias_is_lazy(item.owner_id) {
             return;
         }
 
-        // There must not be a where clause
-        if type_alias_generics.predicates.is_empty() {
-            return;
-        }
+        // NOTE(inherent_associated_types): While we currently do take some bounds in type
+        // aliases into consideration during IAT *selection*, we don't perform full use+def
+        // site wfchecking for such type aliases. Therefore TAB should still trigger.
+        // See also `tests/ui/associated-inherent-types/type-alias-bounds.rs`.
 
         let mut where_spans = Vec::new();
         let mut inline_spans = Vec::new();
         let mut inline_sugg = Vec::new();
-        for p in type_alias_generics.predicates {
+        for p in generics.predicates {
             let span = p.span();
             if p.in_where_clause() {
                 where_spans.push(span);
@@ -1469,10 +1466,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
             cx.emit_span_lint(
                 TYPE_ALIAS_BOUNDS,
                 where_spans,
-                BuiltinTypeAliasWhereClause {
-                    suggestion: type_alias_generics.where_clause_span,
-                    sub,
-                },
+                BuiltinTypeAliasWhereClause { suggestion: generics.where_clause_span, sub },
             );
         }
 
diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs
index 6ec38b78fc22a..d5e114b2175c8 100644
--- a/compiler/rustc_type_ir/src/visit.rs
+++ b/compiler/rustc_type_ir/src/visit.rs
@@ -241,10 +241,6 @@ pub trait TypeVisitableExt<I: Interner>: TypeVisitable<I> {
         self.has_type_flags(TypeFlags::HAS_ALIAS)
     }
 
-    fn has_inherent_projections(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_TY_INHERENT)
-    }
-
     fn has_opaque_types(&self) -> bool {
         self.has_type_flags(TypeFlags::HAS_TY_OPAQUE)
     }
diff --git a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs b/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs
deleted file mode 100644
index 5ac7e1e58b8b2..0000000000000
--- a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs
+++ /dev/null
@@ -1,20 +0,0 @@
-//@ compile-flags: --crate-type=lib
-//@ check-pass
-
-#![feature(inherent_associated_types)]
-#![allow(incomplete_features)]
-
-// Bounds on the self type play a major role in the resolution of inherent associated types (*).
-// As a result of that, if a type alias contains any then its bounds have to be respected and the
-// lint `type_alias_bounds` should not fire.
-
-#![deny(type_alias_bounds)]
-
-pub type Alias<T: Bound> = (Source<T>::Assoc,);
-
-pub struct Source<T>(T);
-pub trait Bound {}
-
-impl<T: Bound> Source<T> {
-    pub type Assoc = ();
-}
diff --git a/tests/ui/associated-inherent-types/type-alias-bounds.rs b/tests/ui/associated-inherent-types/type-alias-bounds.rs
new file mode 100644
index 0000000000000..c27bcbed43ae0
--- /dev/null
+++ b/tests/ui/associated-inherent-types/type-alias-bounds.rs
@@ -0,0 +1,29 @@
+//@ compile-flags: --crate-type=lib
+//@ check-pass
+
+#![feature(inherent_associated_types)]
+#![allow(incomplete_features)]
+
+// FIXME(inherent_associated_types):
+// While we currently do take some clauses of the ParamEnv into consideration
+// when performing IAT selection, we do not perform full well-formedness checking
+// for (eager) type alias definition and usage sites.
+//
+// Therefore it's *correct* for lint `type_alias_bounds` to fire here despite the
+// fact that removing `Bound` from `T` in `Alias` would lead to an error!
+//
+// Obviously, the present situation isn't ideal and we should fix it in one way
+// or another. Either we somehow delay IAT selection until after HIR ty lowering
+// to avoid the need to specify any bounds inside (eager) type aliases or we
+// force the overarching type alias to be *lazy* (similar to TAITs) which would
+// automatically lead to full wfchecking and lint TAB getting suppressed.
+
+pub type Alias<T: Bound> = (Source<T>::Assoc,);
+//~^ WARN bounds on generic parameters are not enforced in type aliases
+
+pub struct Source<T>(T);
+pub trait Bound {}
+
+impl<T: Bound> Source<T> {
+    pub type Assoc = ();
+}
diff --git a/tests/ui/associated-inherent-types/type-alias-bounds.stderr b/tests/ui/associated-inherent-types/type-alias-bounds.stderr
new file mode 100644
index 0000000000000..c5d0e9e5041df
--- /dev/null
+++ b/tests/ui/associated-inherent-types/type-alias-bounds.stderr
@@ -0,0 +1,15 @@
+warning: bounds on generic parameters are not enforced in type aliases
+  --> $DIR/type-alias-bounds.rs:21:19
+   |
+LL | pub type Alias<T: Bound> = (Source<T>::Assoc,);
+   |                   ^^^^^
+   |
+   = note: `#[warn(type_alias_bounds)]` on by default
+help: the bound will not be checked when the type alias is used, and should be removed
+   |
+LL - pub type Alias<T: Bound> = (Source<T>::Assoc,);
+LL + pub type Alias<T> = (Source<T>::Assoc,);
+   |
+
+warning: 1 warning emitted
+

From a8b3dfd25336dc12f17da23a0ed7004a4ebee234 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Fri, 14 Jun 2024 16:18:32 +0200
Subject: [PATCH 6/9] Suppress lint type_alias_bounds for ty aliases containing
 const projections under GCE

---
 compiler/rustc_lint/src/builtin.rs            | 10 +++
 .../type-alias-bounds.neg.stderr              | 63 ++++++++++++++++
 .../generic_const_exprs/type-alias-bounds.rs  | 71 +++++++++++++++++++
 3 files changed, 144 insertions(+)
 create mode 100644 tests/ui/const-generics/generic_const_exprs/type-alias-bounds.neg.stderr
 create mode 100644 tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs

diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 40b336cd5ea0c..9055500760778 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -1437,6 +1437,16 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
             return;
         }
 
+
+        // FIXME(generic_const_exprs): Revisit this before stabilization.
+        // See also `tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs`.
+        let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
+        if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION)
+            && cx.tcx.features().generic_const_exprs
+        {
+            return;
+        }
+
         // NOTE(inherent_associated_types): While we currently do take some bounds in type
         // aliases into consideration during IAT *selection*, we don't perform full use+def
         // site wfchecking for such type aliases. Therefore TAB should still trigger.
diff --git a/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.neg.stderr b/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.neg.stderr
new file mode 100644
index 0000000000000..fa12dd14753f9
--- /dev/null
+++ b/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.neg.stderr
@@ -0,0 +1,63 @@
+error[E0277]: the trait bound `String: Copy` is not satisfied
+  --> $DIR/type-alias-bounds.rs:23:12
+   |
+LL |     let _: AliasConstUnused<String>;
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
+   |
+note: required by a bound in `ct_unused_0::AliasConstUnused`
+  --> $DIR/type-alias-bounds.rs:20:30
+   |
+LL |     type AliasConstUnused<T: Copy> = (T, I32<{ DATA }>);
+   |                              ^^^^ required by this bound in `AliasConstUnused`
+
+error[E0277]: the trait bound `String: Copy` is not satisfied
+  --> $DIR/type-alias-bounds.rs:31:12
+   |
+LL |     let _: AliasConstUnused;
+   |            ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
+   |
+note: required by a bound in `ct_unused_1::AliasConstUnused`
+  --> $DIR/type-alias-bounds.rs:29:41
+   |
+LL |     type AliasConstUnused where String: Copy = I32<{ 0; 0 }>;
+   |                                         ^^^^ required by this bound in `AliasConstUnused`
+
+error[E0277]: the trait bound `String: Copy` is not satisfied
+  --> $DIR/type-alias-bounds.rs:39:12
+   |
+LL |     let _: AliasFnUnused<String>;
+   |            ^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
+   |
+note: required by a bound in `AliasFnUnused`
+  --> $DIR/type-alias-bounds.rs:36:27
+   |
+LL |     type AliasFnUnused<T: Copy> = (T, I32<{ code() }>);
+   |                           ^^^^ required by this bound in `AliasFnUnused`
+
+error[E0277]: the trait bound `String: Copy` is not satisfied
+  --> $DIR/type-alias-bounds.rs:57:12
+   |
+LL |     let _: AliasAssocConstUsed<String>;
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
+   |
+note: required by a bound in `AliasAssocConstUsed`
+  --> $DIR/type-alias-bounds.rs:55:41
+   |
+LL |     type AliasAssocConstUsed<T: Trait + Copy> = I32<{ T::DATA }>;
+   |                                         ^^^^ required by this bound in `AliasAssocConstUsed`
+
+error[E0277]: the trait bound `String: Copy` is not satisfied
+  --> $DIR/type-alias-bounds.rs:65:12
+   |
+LL |     let _: AliasFnUsed<String>;
+   |            ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
+   |
+note: required by a bound in `AliasFnUsed`
+  --> $DIR/type-alias-bounds.rs:62:33
+   |
+LL |     type AliasFnUsed<T: Trait + Copy> = I32<{ code::<T>() }>;
+   |                                 ^^^^ required by this bound in `AliasFnUsed`
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs b/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs
new file mode 100644
index 0000000000000..f16e646129c65
--- /dev/null
+++ b/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs
@@ -0,0 +1,71 @@
+//@ revisions: pos neg
+//@[pos] check-pass
+
+#![feature(generic_const_exprs)]
+#![feature(trivial_bounds)] // only used in test case `ct_unused_1`
+#![allow(incomplete_features)]
+
+// FIXME(generic_const_exprs): Revisit this before stabilization.
+// Check that we don't emit the lint `type_alias_bounds` for (eager) type aliases
+// whose RHS contains a const projection (aka uneval'ed const).
+// Since anon consts inherit the parent generics and predicates and we effectively
+// check them before and after instantiaton for well-formedness, the type alias
+// bounds are in every sense "enforced".
+// Note that the test cases whose name ends in "unused" just demonstrate that this
+// holds even if the const projections don't "visibly" capture any generics and/or
+// predicates.
+#![deny(type_alias_bounds)]
+
+fn ct_unused_0() {
+    type AliasConstUnused<T: Copy> = (T, I32<{ DATA }>);
+    const DATA: i32 = 0;
+    #[cfg(neg)]
+    let _: AliasConstUnused<String>;
+    //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied
+}
+
+fn ct_unused_1() {
+    #[allow(trivial_bounds)]
+    type AliasConstUnused where String: Copy = I32<{ 0; 0 }>;
+    #[cfg(neg)]
+    let _: AliasConstUnused;
+    //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied
+}
+
+fn fn_unused() {
+    type AliasFnUnused<T: Copy> = (T, I32<{ code() }>);
+    const fn code() -> i32 { 0 }
+    #[cfg(neg)]
+    let _: AliasFnUnused<String>;
+    //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied
+}
+
+trait Trait {
+    type Proj;
+    const DATA: i32;
+}
+
+impl Trait for String {
+    type Proj = i32;
+    const DATA: i32 = 0;
+}
+
+// Regression test for issue #94398.
+fn assoc_ct_used() {
+    type AliasAssocConstUsed<T: Trait + Copy> = I32<{ T::DATA }>;
+    #[cfg(neg)]
+    let _: AliasAssocConstUsed<String>;
+    //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied
+}
+
+fn fn_used() {
+    type AliasFnUsed<T: Trait + Copy> = I32<{ code::<T>() }>;
+    const fn code<T: Trait>() -> i32 { T::DATA }
+    #[cfg(neg)]
+    let _: AliasFnUsed<String>;
+    //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied
+}
+
+struct I32<const N: i32>;
+
+fn main() {}

From fdf8f024ad71c6e9c46867fb31b74df0fcaaf3f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Sat, 15 Jun 2024 21:34:44 +0200
Subject: [PATCH 7/9] Improve the impl and diag output of lint
 type_alias_bounds

---
 compiler/rustc_lint/messages.ftl              |  19 +-
 compiler/rustc_lint/src/builtin.rs            |  92 ++++----
 compiler/rustc_lint/src/lints.rs              | 148 +++++++------
 .../type-alias-bounds.rs                      |   2 +-
 .../type-alias-bounds.stderr                  |  15 +-
 .../associated-type-bounds/type-alias.stderr  | 204 +++++++++---------
 tests/ui/privacy/private-in-public-warn.rs    |   4 +-
 .../ui/privacy/private-in-public-warn.stderr  |  34 +--
 .../trivial-bounds-inconsistent.stderr        |  17 +-
 ...67690-type-alias-bound-diagnostic-crash.rs |   2 +-
 ...0-type-alias-bound-diagnostic-crash.stderr |  15 +-
 tests/ui/type/type-alias-bounds.rs            |  21 +-
 tests/ui/type/type-alias-bounds.stderr        | 163 +++++++-------
 13 files changed, 401 insertions(+), 335 deletions(-)

diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 7d7b97e2eb1a3..ce99c8686baaf 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -139,13 +139,18 @@ lint_builtin_special_module_name_used_main = found module declaration for main.r
 
 lint_builtin_trivial_bounds = {$predicate_kind_name} bound {$predicate} does not depend on any type or lifetime parameters
 
-lint_builtin_type_alias_bounds_help = use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to associated types in type aliases
-
-lint_builtin_type_alias_generic_bounds = bounds on generic parameters are not enforced in type aliases
-    .suggestion = the bound will not be checked when the type alias is used, and should be removed
-
-lint_builtin_type_alias_where_clause = where clauses are not enforced in type aliases
-    .suggestion = the clause will not be checked when the type alias is used, and should be removed
+lint_builtin_type_alias_bounds_enable_feat_help = add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
+lint_builtin_type_alias_bounds_label = will not be checked at usage sites of the type alias
+lint_builtin_type_alias_bounds_limitation_note = this is a known limitation of the type checker that may be lifted in a future edition.
+    see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+lint_builtin_type_alias_bounds_param_bounds = bounds on generic parameters in type aliases are not enforced
+    .suggestion = remove {$count ->
+        [one] this bound
+        *[other] these bounds
+    }
+lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg = fully qualify this associated type
+lint_builtin_type_alias_bounds_where_clause = where clauses on type aliases are not enforced
+    .suggestion = remove this where clause
 
 lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed
 lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 9055500760778..a8c8c71927aaf 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -31,12 +31,12 @@ use crate::{
         BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents,
         BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
         BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,
-        BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds,
-        BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause,
-        BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit,
-        BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe,
-        BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
-        BuiltinWhileTrue, InvalidAsmLabel, SuggestChangingAssocTypes,
+        BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds,
+        BuiltinTypeAliasParamBoundsSuggestion, BuiltinUngatedAsyncFnTrackCaller,
+        BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub,
+        BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment,
+        BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel,
+        TypeAliasBoundsQualifyAssocTysSugg,
     },
     EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext,
 };
@@ -1406,23 +1406,6 @@ declare_lint_pass!(
     TypeAliasBounds => [TYPE_ALIAS_BOUNDS]
 );
 
-impl TypeAliasBounds {
-    pub(crate) fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool {
-        match *qpath {
-            hir::QPath::TypeRelative(ty, _) => {
-                // If this is a type variable, we found a `T::Assoc`.
-                match ty.kind {
-                    hir::TyKind::Path(hir::QPath::Resolved(None, path)) => {
-                        matches!(path.res, Res::Def(DefKind::TyParam, _))
-                    }
-                    _ => false,
-                }
-            }
-            hir::QPath::Resolved(..) | hir::QPath::LangItem(..) => false,
-        }
-    }
-}
-
 impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
     fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
         let hir::ItemKind::TyAlias(hir_ty, generics) = &item.kind else { return };
@@ -1437,7 +1420,6 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
             return;
         }
 
-
         // FIXME(generic_const_exprs): Revisit this before stabilization.
         // See also `tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs`.
         let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
@@ -1455,6 +1437,8 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
         let mut where_spans = Vec::new();
         let mut inline_spans = Vec::new();
         let mut inline_sugg = Vec::new();
+        let mut affects_object_lifetime_defaults = false;
+
         for p in generics.predicates {
             let span = p.span();
             if p.in_where_clause() {
@@ -1465,31 +1449,61 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
                 }
                 inline_sugg.push((span, String::new()));
             }
+
+            // FIXME(fmease): Move this into a "diagnostic decorator" for increased laziness
+            // Bounds of the form `T: 'a` where `T` is a type param of
+            // the type alias affect object lifetime defaults.
+            if !affects_object_lifetime_defaults
+                && let hir::WherePredicate::BoundPredicate(pred) = p
+                && pred.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Outlives(_)))
+                && pred.bound_generic_params.is_empty()
+                && let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = pred.bounded_ty.kind
+                && let Res::Def(DefKind::TyParam, _) = path.res
+            {
+                affects_object_lifetime_defaults = true;
+            }
         }
 
-        let mut suggested_changing_assoc_types = false;
-        if !where_spans.is_empty() {
-            let sub = (!suggested_changing_assoc_types).then(|| {
-                suggested_changing_assoc_types = true;
-                SuggestChangingAssocTypes { ty: hir_ty }
-            });
+        // FIXME(fmease): Add a disclaimer (in the form of a multi-span note) that the removal of
+        //                type-param-outlives-bounds affects OLDs and explicit object lifetime
+        //                bounds might be required [...].
+        // FIXME(fmease): The applicability should also depend on the outcome of the HIR walker
+        //                inside of `TypeAliasBoundsQualifyAssocTysSugg`: Whether it found a
+        //                shorthand projection or not.
+        let applicability = if affects_object_lifetime_defaults {
+            Applicability::MaybeIncorrect
+        } else {
+            Applicability::MachineApplicable
+        };
+
+        let mut qualify_assoc_tys_sugg = Some(TypeAliasBoundsQualifyAssocTysSugg { ty: hir_ty });
+        let enable_feat_help = cx.tcx.sess.is_nightly_build().then_some(());
+
+        if let [.., label_sp] = *where_spans {
             cx.emit_span_lint(
                 TYPE_ALIAS_BOUNDS,
                 where_spans,
-                BuiltinTypeAliasWhereClause { suggestion: generics.where_clause_span, sub },
+                BuiltinTypeAliasBounds::WhereClause {
+                    label: label_sp,
+                    enable_feat_help,
+                    suggestion: (generics.where_clause_span, applicability),
+                    qualify_assoc_tys_sugg: qualify_assoc_tys_sugg.take(),
+                },
             );
         }
-
-        if !inline_spans.is_empty() {
-            let suggestion = BuiltinTypeAliasGenericBoundsSuggestion { suggestions: inline_sugg };
-            let sub = (!suggested_changing_assoc_types).then(|| {
-                suggested_changing_assoc_types = true;
-                SuggestChangingAssocTypes { ty: hir_ty }
-            });
+        if let [.., label_sp] = *inline_spans {
             cx.emit_span_lint(
                 TYPE_ALIAS_BOUNDS,
                 inline_spans,
-                BuiltinTypeAliasGenericBounds { suggestion, sub },
+                BuiltinTypeAliasBounds::ParamBounds {
+                    label: label_sp,
+                    enable_feat_help,
+                    suggestion: BuiltinTypeAliasParamBoundsSuggestion {
+                        suggestions: inline_sugg,
+                        applicability,
+                    },
+                    qualify_assoc_tys_sugg,
+                },
             );
         }
     }
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 6c5f366727f95..6cf8b9330fc40 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -9,7 +9,7 @@ use rustc_errors::{
     ElidedLifetimeInPathSubdiag, EmissionGuarantee, LintDiagnostic, MultiSpan, SubdiagMessageOp,
     Subdiagnostic, SuggestionStyle,
 };
-use rustc_hir::{def::Namespace, def_id::DefId};
+use rustc_hir::{self as hir, def::Namespace, def_id::DefId};
 use rustc_macros::{LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::{
     inhabitedness::InhabitedPredicate, Clause, PolyExistentialTraitRef, Ty, TyCtxt,
@@ -22,9 +22,7 @@ use rustc_span::{
     Span, Symbol,
 };
 
-use crate::{
-    builtin::InitError, builtin::TypeAliasBounds, errors::OverruledAttributeSub, LateContext,
-};
+use crate::{builtin::InitError, errors::OverruledAttributeSub, LateContext};
 
 // array_into_iter.rs
 #[derive(LintDiagnostic)]
@@ -263,84 +261,104 @@ pub struct BuiltinUnreachablePub<'a> {
     pub help: Option<()>,
 }
 
-pub struct SuggestChangingAssocTypes<'a, 'b> {
-    pub ty: &'a rustc_hir::Ty<'b>,
+#[derive(LintDiagnostic)]
+#[diag(lint_macro_expr_fragment_specifier_2024_migration)]
+pub struct MacroExprFragment2024 {
+    #[suggestion(code = "expr_2021", applicability = "machine-applicable")]
+    pub suggestion: Span,
+}
+
+#[derive(LintDiagnostic)]
+pub enum BuiltinTypeAliasBounds<'a, 'hir> {
+    #[diag(lint_builtin_type_alias_bounds_where_clause)]
+    #[note(lint_builtin_type_alias_bounds_limitation_note)]
+    WhereClause {
+        #[label(lint_builtin_type_alias_bounds_label)]
+        label: Span,
+        #[help(lint_builtin_type_alias_bounds_enable_feat_help)]
+        enable_feat_help: Option<()>,
+        #[suggestion(code = "")]
+        suggestion: (Span, Applicability),
+        #[subdiagnostic]
+        qualify_assoc_tys_sugg: Option<TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir>>,
+    },
+    #[diag(lint_builtin_type_alias_bounds_param_bounds)]
+    #[note(lint_builtin_type_alias_bounds_limitation_note)]
+    ParamBounds {
+        #[label(lint_builtin_type_alias_bounds_label)]
+        label: Span,
+        #[help(lint_builtin_type_alias_bounds_enable_feat_help)]
+        enable_feat_help: Option<()>,
+        #[subdiagnostic]
+        suggestion: BuiltinTypeAliasParamBoundsSuggestion,
+        #[subdiagnostic]
+        qualify_assoc_tys_sugg: Option<TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir>>,
+    },
+}
+
+pub struct BuiltinTypeAliasParamBoundsSuggestion {
+    pub suggestions: Vec<(Span, String)>,
+    pub applicability: Applicability,
 }
 
-impl<'a, 'b> Subdiagnostic for SuggestChangingAssocTypes<'a, 'b> {
+impl Subdiagnostic for BuiltinTypeAliasParamBoundsSuggestion {
     fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
         self,
         diag: &mut Diag<'_, G>,
         _f: &F,
     ) {
-        // Access to associates types should use `<T as Bound>::Assoc`, which does not need a
-        // bound. Let's see if this type does that.
-
-        // We use a HIR visitor to walk the type.
-        use rustc_hir::intravisit::{self, Visitor};
-        struct WalkAssocTypes<'a, 'b, G: EmissionGuarantee> {
-            err: &'a mut Diag<'b, G>,
-        }
-        impl<'a, 'b, G: EmissionGuarantee> Visitor<'_> for WalkAssocTypes<'a, 'b, G> {
-            fn visit_qpath(
-                &mut self,
-                qpath: &rustc_hir::QPath<'_>,
-                id: rustc_hir::HirId,
-                span: Span,
-            ) {
-                if TypeAliasBounds::is_type_variable_assoc(qpath) {
-                    self.err.span_help(span, fluent::lint_builtin_type_alias_bounds_help);
-                }
-                intravisit::walk_qpath(self, qpath, id)
-            }
-        }
-
-        // Let's go for a walk!
-        let mut visitor = WalkAssocTypes { err: diag };
-        visitor.visit_ty(self.ty);
+        diag.arg("count", self.suggestions.len());
+        diag.multipart_suggestion(fluent::lint_suggestion, self.suggestions, self.applicability);
     }
 }
 
-#[derive(LintDiagnostic)]
-#[diag(lint_builtin_type_alias_where_clause)]
-pub struct BuiltinTypeAliasWhereClause<'a, 'b> {
-    #[suggestion(code = "", applicability = "machine-applicable")]
-    pub suggestion: Span,
-    #[subdiagnostic]
-    pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
-}
-
-#[derive(LintDiagnostic)]
-#[diag(lint_builtin_type_alias_generic_bounds)]
-pub struct BuiltinTypeAliasGenericBounds<'a, 'b> {
-    #[subdiagnostic]
-    pub suggestion: BuiltinTypeAliasGenericBoundsSuggestion,
-    #[subdiagnostic]
-    pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
-}
-
-#[derive(LintDiagnostic)]
-#[diag(lint_macro_expr_fragment_specifier_2024_migration)]
-pub struct MacroExprFragment2024 {
-    #[suggestion(code = "expr_2021", applicability = "machine-applicable")]
-    pub suggestion: Span,
+pub struct TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir> {
+    pub ty: &'a hir::Ty<'hir>,
 }
 
-pub struct BuiltinTypeAliasGenericBoundsSuggestion {
-    pub suggestions: Vec<(Span, String)>,
-}
-
-impl Subdiagnostic for BuiltinTypeAliasGenericBoundsSuggestion {
+impl<'a, 'hir> Subdiagnostic for TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir> {
     fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
         self,
         diag: &mut Diag<'_, G>,
         _f: &F,
     ) {
-        diag.multipart_suggestion(
-            fluent::lint_suggestion,
-            self.suggestions,
-            Applicability::MachineApplicable,
-        );
+        // We perform the walk in here instead of in `<TypeAliasBounds as LateLintPass>` to
+        // avoid doing throwaway work in case the lint ends up getting suppressed.
+
+        use hir::intravisit::Visitor;
+        struct ProbeShorthandAssocTys<'a, 'b, G: EmissionGuarantee> {
+            diag: &'a mut Diag<'b, G>,
+        }
+        impl<'a, 'b, G: EmissionGuarantee> Visitor<'_> for ProbeShorthandAssocTys<'a, 'b, G> {
+            fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, _: Span) {
+                // Look for "type-parameter shorthand-associated-types". I.e., paths of the
+                // form `T::Assoc` with `T` type param. These are reliant on trait bounds.
+                // Suggest fully qualifying them via `<T as /* Trait */>::Assoc`.
+                //
+                // Instead of attempting to figure out the necessary trait ref, just use a
+                // placeholder. Since we don't record type-dependent resolutions for non-body
+                // items like type aliases, we can't simply deduce the corresp. trait from
+                // the HIR path alone without rerunning parts of HIR ty lowering here
+                // (namely `probe_single_ty_param_bound_for_assoc_ty`) which is infeasible.
+                //
+                // (We could employ some simple heuristics but that's likely not worth it).
+                if let hir::QPath::TypeRelative(qself, _) = qpath
+                    && let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = qself.kind
+                    && let hir::def::Res::Def(hir::def::DefKind::TyParam, _) = path.res
+                {
+                    self.diag.multipart_suggestion(
+                        fluent::lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg,
+                        vec![
+                            (qself.span.shrink_to_lo(), "<".into()),
+                            (qself.span.shrink_to_hi(), " as /* Trait */>".into()),
+                        ],
+                        Applicability::HasPlaceholders,
+                    );
+                }
+                hir::intravisit::walk_qpath(self, qpath, id)
+            }
+        }
+        ProbeShorthandAssocTys { diag }.visit_ty(self.ty);
     }
 }
 
diff --git a/tests/ui/associated-inherent-types/type-alias-bounds.rs b/tests/ui/associated-inherent-types/type-alias-bounds.rs
index c27bcbed43ae0..61641a83994ab 100644
--- a/tests/ui/associated-inherent-types/type-alias-bounds.rs
+++ b/tests/ui/associated-inherent-types/type-alias-bounds.rs
@@ -19,7 +19,7 @@
 // automatically lead to full wfchecking and lint TAB getting suppressed.
 
 pub type Alias<T: Bound> = (Source<T>::Assoc,);
-//~^ WARN bounds on generic parameters are not enforced in type aliases
+//~^ WARN bounds on generic parameters in type aliases are not enforced
 
 pub struct Source<T>(T);
 pub trait Bound {}
diff --git a/tests/ui/associated-inherent-types/type-alias-bounds.stderr b/tests/ui/associated-inherent-types/type-alias-bounds.stderr
index c5d0e9e5041df..c56dd498f7708 100644
--- a/tests/ui/associated-inherent-types/type-alias-bounds.stderr
+++ b/tests/ui/associated-inherent-types/type-alias-bounds.stderr
@@ -1,15 +1,16 @@
-warning: bounds on generic parameters are not enforced in type aliases
+warning: bounds on generic parameters in type aliases are not enforced
   --> $DIR/type-alias-bounds.rs:21:19
    |
 LL | pub type Alias<T: Bound> = (Source<T>::Assoc,);
-   |                   ^^^^^
+   |                 --^^^^^
+   |                 | |
+   |                 | will not be checked at usage sites of the type alias
+   |                 help: remove this bound
    |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
    = note: `#[warn(type_alias_bounds)]` on by default
-help: the bound will not be checked when the type alias is used, and should be removed
-   |
-LL - pub type Alias<T: Bound> = (Source<T>::Assoc,);
-LL + pub type Alias<T> = (Source<T>::Assoc,);
-   |
 
 warning: 1 warning emitted
 
diff --git a/tests/ui/associated-type-bounds/type-alias.stderr b/tests/ui/associated-type-bounds/type-alias.stderr
index 072c471467c77..d59952b4a146a 100644
--- a/tests/ui/associated-type-bounds/type-alias.stderr
+++ b/tests/ui/associated-type-bounds/type-alias.stderr
@@ -1,147 +1,159 @@
-warning: where clauses are not enforced in type aliases
+warning: where clauses on type aliases are not enforced
   --> $DIR/type-alias.rs:3:25
    |
 LL | type _TaWhere1<T> where T: Iterator<Item: Copy> = T;
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^
-   |
+   |                   ------^^^^^^^^^^^^^^^^^^^^^^^
+   |                   |     |
+   |                   |     will not be checked at usage sites of the type alias
+   |                   help: remove this where clause
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
    = note: `#[warn(type_alias_bounds)]` on by default
-help: the clause will not be checked when the type alias is used, and should be removed
-   |
-LL - type _TaWhere1<T> where T: Iterator<Item: Copy> = T;
-LL + type _TaWhere1<T>  = T;
-   |
 
-warning: where clauses are not enforced in type aliases
+warning: where clauses on type aliases are not enforced
   --> $DIR/type-alias.rs:4:25
    |
 LL | type _TaWhere2<T> where T: Iterator<Item: 'static> = T;
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: the clause will not be checked when the type alias is used, and should be removed
-   |
-LL - type _TaWhere2<T> where T: Iterator<Item: 'static> = T;
-LL + type _TaWhere2<T>  = T;
-   |
+   |                   ------^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                   |     |
+   |                   |     will not be checked at usage sites of the type alias
+   |                   help: remove this where clause
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
-warning: where clauses are not enforced in type aliases
+warning: where clauses on type aliases are not enforced
   --> $DIR/type-alias.rs:5:25
    |
 LL | type _TaWhere3<T> where T: Iterator<Item: 'static> = T;
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: the clause will not be checked when the type alias is used, and should be removed
-   |
-LL - type _TaWhere3<T> where T: Iterator<Item: 'static> = T;
-LL + type _TaWhere3<T>  = T;
-   |
+   |                   ------^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                   |     |
+   |                   |     will not be checked at usage sites of the type alias
+   |                   help: remove this where clause
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
-warning: where clauses are not enforced in type aliases
+warning: where clauses on type aliases are not enforced
   --> $DIR/type-alias.rs:6:25
    |
 LL | type _TaWhere4<T> where T: Iterator<Item: 'static + Copy + Send> = T;
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: the clause will not be checked when the type alias is used, and should be removed
-   |
-LL - type _TaWhere4<T> where T: Iterator<Item: 'static + Copy + Send> = T;
-LL + type _TaWhere4<T>  = T;
-   |
+   |                   ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                   |     |
+   |                   |     will not be checked at usage sites of the type alias
+   |                   help: remove this where clause
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
-warning: where clauses are not enforced in type aliases
+warning: where clauses on type aliases are not enforced
   --> $DIR/type-alias.rs:7:25
    |
 LL | type _TaWhere5<T> where T: Iterator<Item: for<'a> Into<&'a u8>> = T;
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: the clause will not be checked when the type alias is used, and should be removed
-   |
-LL - type _TaWhere5<T> where T: Iterator<Item: for<'a> Into<&'a u8>> = T;
-LL + type _TaWhere5<T>  = T;
-   |
+   |                   ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                   |     |
+   |                   |     will not be checked at usage sites of the type alias
+   |                   help: remove this where clause
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
-warning: where clauses are not enforced in type aliases
+warning: where clauses on type aliases are not enforced
   --> $DIR/type-alias.rs:8:25
    |
 LL | type _TaWhere6<T> where T: Iterator<Item: Iterator<Item: Copy>> = T;
-   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: the clause will not be checked when the type alias is used, and should be removed
-   |
-LL - type _TaWhere6<T> where T: Iterator<Item: Iterator<Item: Copy>> = T;
-LL + type _TaWhere6<T>  = T;
-   |
+   |                   ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                   |     |
+   |                   |     will not be checked at usage sites of the type alias
+   |                   help: remove this where clause
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
-warning: bounds on generic parameters are not enforced in type aliases
+warning: bounds on generic parameters in type aliases are not enforced
   --> $DIR/type-alias.rs:10:20
    |
 LL | type _TaInline1<T: Iterator<Item: Copy>> = T;
-   |                    ^^^^^^^^^^^^^^^^^^^^
-   |
-help: the bound will not be checked when the type alias is used, and should be removed
-   |
-LL - type _TaInline1<T: Iterator<Item: Copy>> = T;
-LL + type _TaInline1<T> = T;
-   |
+   |                  --^^^^^^^^^^^^^^^^^^^^
+   |                  | |
+   |                  | will not be checked at usage sites of the type alias
+   |                  help: remove this bound
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
-warning: bounds on generic parameters are not enforced in type aliases
+warning: bounds on generic parameters in type aliases are not enforced
   --> $DIR/type-alias.rs:11:20
    |
 LL | type _TaInline2<T: Iterator<Item: 'static>> = T;
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: the bound will not be checked when the type alias is used, and should be removed
-   |
-LL - type _TaInline2<T: Iterator<Item: 'static>> = T;
-LL + type _TaInline2<T> = T;
-   |
+   |                  --^^^^^^^^^^^^^^^^^^^^^^^
+   |                  | |
+   |                  | will not be checked at usage sites of the type alias
+   |                  help: remove this bound
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
-warning: bounds on generic parameters are not enforced in type aliases
+warning: bounds on generic parameters in type aliases are not enforced
   --> $DIR/type-alias.rs:12:20
    |
 LL | type _TaInline3<T: Iterator<Item: 'static>> = T;
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: the bound will not be checked when the type alias is used, and should be removed
-   |
-LL - type _TaInline3<T: Iterator<Item: 'static>> = T;
-LL + type _TaInline3<T> = T;
-   |
+   |                  --^^^^^^^^^^^^^^^^^^^^^^^
+   |                  | |
+   |                  | will not be checked at usage sites of the type alias
+   |                  help: remove this bound
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
-warning: bounds on generic parameters are not enforced in type aliases
+warning: bounds on generic parameters in type aliases are not enforced
   --> $DIR/type-alias.rs:13:20
    |
 LL | type _TaInline4<T: Iterator<Item: 'static + Copy + Send>> = T;
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: the bound will not be checked when the type alias is used, and should be removed
-   |
-LL - type _TaInline4<T: Iterator<Item: 'static + Copy + Send>> = T;
-LL + type _TaInline4<T> = T;
-   |
+   |                  --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                  | |
+   |                  | will not be checked at usage sites of the type alias
+   |                  help: remove this bound
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
-warning: bounds on generic parameters are not enforced in type aliases
+warning: bounds on generic parameters in type aliases are not enforced
   --> $DIR/type-alias.rs:14:20
    |
 LL | type _TaInline5<T: Iterator<Item: for<'a> Into<&'a u8>>> = T;
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: the bound will not be checked when the type alias is used, and should be removed
-   |
-LL - type _TaInline5<T: Iterator<Item: for<'a> Into<&'a u8>>> = T;
-LL + type _TaInline5<T> = T;
-   |
+   |                  --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                  | |
+   |                  | will not be checked at usage sites of the type alias
+   |                  help: remove this bound
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
-warning: bounds on generic parameters are not enforced in type aliases
+warning: bounds on generic parameters in type aliases are not enforced
   --> $DIR/type-alias.rs:15:20
    |
 LL | type _TaInline6<T: Iterator<Item: Iterator<Item: Copy>>> = T;
-   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: the bound will not be checked when the type alias is used, and should be removed
-   |
-LL - type _TaInline6<T: Iterator<Item: Iterator<Item: Copy>>> = T;
-LL + type _TaInline6<T> = T;
-   |
+   |                  --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                  | |
+   |                  | will not be checked at usage sites of the type alias
+   |                  help: remove this bound
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
 warning: 12 warnings emitted
 
diff --git a/tests/ui/privacy/private-in-public-warn.rs b/tests/ui/privacy/private-in-public-warn.rs
index 99d318e36be3d..746b98fbd078e 100644
--- a/tests/ui/privacy/private-in-public-warn.rs
+++ b/tests/ui/privacy/private-in-public-warn.rs
@@ -39,7 +39,7 @@ mod traits {
     pub trait PubTr {}
 
     pub type Alias<T: PrivTr> = T; //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Alias`
-    //~^ WARNING bounds on generic parameters are not enforced in type aliases
+    //~^ WARNING bounds on generic parameters in type aliases are not enforced
     pub trait Tr1: PrivTr {} //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr1`
     pub trait Tr2<T: PrivTr> {} //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr2`
     pub trait Tr3 {
@@ -58,7 +58,7 @@ mod traits_where {
 
     pub type Alias<T> where T: PrivTr = T;
         //~^ ERROR trait `traits_where::PrivTr` is more private than the item `traits_where::Alias`
-        //~| WARNING where clauses are not enforced in type aliases
+        //~| WARNING where clauses on type aliases are not enforced
     pub trait Tr2<T> where T: PrivTr {}
         //~^ ERROR trait `traits_where::PrivTr` is more private than the item `traits_where::Tr2`
     pub trait Tr3 {
diff --git a/tests/ui/privacy/private-in-public-warn.stderr b/tests/ui/privacy/private-in-public-warn.stderr
index ac7e5547de914..3f7b8c281e706 100644
--- a/tests/ui/privacy/private-in-public-warn.stderr
+++ b/tests/ui/privacy/private-in-public-warn.stderr
@@ -395,30 +395,32 @@ note: but type `Priv2` is only usable at visibility `pub(self)`
 LL |     struct Priv2;
    |     ^^^^^^^^^^^^
 
-warning: bounds on generic parameters are not enforced in type aliases
+warning: bounds on generic parameters in type aliases are not enforced
   --> $DIR/private-in-public-warn.rs:41:23
    |
 LL |     pub type Alias<T: PrivTr> = T;
-   |                       ^^^^^^
-   |
+   |                     --^^^^^^
+   |                     | |
+   |                     | will not be checked at usage sites of the type alias
+   |                     help: remove this bound
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
    = note: `#[warn(type_alias_bounds)]` on by default
-help: the bound will not be checked when the type alias is used, and should be removed
-   |
-LL -     pub type Alias<T: PrivTr> = T;
-LL +     pub type Alias<T> = T;
-   |
 
-warning: where clauses are not enforced in type aliases
+warning: where clauses on type aliases are not enforced
   --> $DIR/private-in-public-warn.rs:59:29
    |
 LL |     pub type Alias<T> where T: PrivTr = T;
-   |                             ^^^^^^^^^
-   |
-help: the clause will not be checked when the type alias is used, and should be removed
-   |
-LL -     pub type Alias<T> where T: PrivTr = T;
-LL +     pub type Alias<T>  = T;
-   |
+   |                       ------^^^^^^^^^
+   |                       |     |
+   |                       |     will not be checked at usage sites of the type alias
+   |                       help: remove this where clause
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
 error: aborting due to 34 previous errors; 2 warnings emitted
 
diff --git a/tests/ui/trivial-bounds/trivial-bounds-inconsistent.stderr b/tests/ui/trivial-bounds/trivial-bounds-inconsistent.stderr
index d66e468873ba4..0eae68bfcf0f8 100644
--- a/tests/ui/trivial-bounds/trivial-bounds-inconsistent.stderr
+++ b/tests/ui/trivial-bounds/trivial-bounds-inconsistent.stderr
@@ -24,18 +24,19 @@ warning: trait bound i32: Foo does not depend on any type or lifetime parameters
 LL | union U where i32: Foo { f: i32 }
    |                    ^^^
 
-warning: where clauses are not enforced in type aliases
+warning: where clauses on type aliases are not enforced
   --> $DIR/trivial-bounds-inconsistent.rs:22:14
    |
 LL | type Y where i32: Foo = ();
-   |              ^^^^^^^^
-   |
+   |        ------^^^^^^^^
+   |        |     |
+   |        |     will not be checked at usage sites of the type alias
+   |        help: remove this where clause
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
    = note: `#[warn(type_alias_bounds)]` on by default
-help: the clause will not be checked when the type alias is used, and should be removed
-   |
-LL - type Y where i32: Foo = ();
-LL + type Y  = ();
-   |
 
 warning: trait bound i32: Foo does not depend on any type or lifetime parameters
   --> $DIR/trivial-bounds-inconsistent.rs:22:19
diff --git a/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs b/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs
index 5ee3c027f4049..52e0887175d44 100644
--- a/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs
+++ b/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs
@@ -3,6 +3,6 @@
 
 //@ check-pass
 pub type T<P: Send + Send + Send> = P;
-//~^ WARN bounds on generic parameters are not enforced in type aliases
+//~^ WARN bounds on generic parameters in type aliases are not enforced
 
 fn main() {}
diff --git a/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr b/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr
index 125ffbbb41757..9fd0fe4913b47 100644
--- a/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr
+++ b/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr
@@ -1,15 +1,16 @@
-warning: bounds on generic parameters are not enforced in type aliases
+warning: bounds on generic parameters in type aliases are not enforced
   --> $DIR/issue-67690-type-alias-bound-diagnostic-crash.rs:5:15
    |
 LL | pub type T<P: Send + Send + Send> = P;
-   |               ^^^^   ^^^^   ^^^^
+   |             --^^^^---^^^^---^^^^
+   |             |               |
+   |             |               will not be checked at usage sites of the type alias
+   |             help: remove this bound
    |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
    = note: `#[warn(type_alias_bounds)]` on by default
-help: the bound will not be checked when the type alias is used, and should be removed
-   |
-LL - pub type T<P: Send + Send + Send> = P;
-LL + pub type T<P> = P;
-   |
 
 warning: 1 warning emitted
 
diff --git a/tests/ui/type/type-alias-bounds.rs b/tests/ui/type/type-alias-bounds.rs
index 6d63c0c7e1bda..37c073fe1f95d 100644
--- a/tests/ui/type/type-alias-bounds.rs
+++ b/tests/ui/type/type-alias-bounds.rs
@@ -6,15 +6,15 @@
 use std::rc::Rc;
 
 type SVec<T: Send + Send> = Vec<T>;
-//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds]
+//~^ WARN bounds on generic parameters in type aliases are not enforced [type_alias_bounds]
 type S2Vec<T> where T: Send = Vec<T>;
-//~^ WARN where clauses are not enforced in type aliases [type_alias_bounds]
+//~^ WARN where clauses on type aliases are not enforced [type_alias_bounds]
 type VVec<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>);
-//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds]
+//~^ WARN bounds on generic parameters in type aliases are not enforced [type_alias_bounds]
 type WVec<'b, T: 'b + 'b> = (&'b u32, Vec<T>);
-//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds]
+//~^ WARN bounds on generic parameters in type aliases are not enforced [type_alias_bounds]
 type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec<T>);
-//~^ WARN where clauses are not enforced in type aliases [type_alias_bounds]
+//~^ WARN where clauses on type aliases are not enforced [type_alias_bounds]
 
 static STATIC: u32 = 0;
 
@@ -42,10 +42,11 @@ fn foo<'a>(y: &'a i32) {
 struct Sendable<T: Send>(T);
 type MySendable<T> = Sendable<T>; // no error here!
 
-// However, bounds *are* taken into account when accessing associated types
+// Bounds on type params do enable shorthand type alias paths.
+// However, that doesn't actually mean that they are properly enforced.
 trait Bound { type Assoc; }
-type T1<U: Bound> = U::Assoc; //~ WARN not enforced in type aliases
-type T2<U> where U: Bound = U::Assoc;  //~ WARN not enforced in type aliases
+type T1<U: Bound> = U::Assoc; //~ WARN are not enforced
+type T2<U> where U: Bound = U::Assoc;  //~ WARN are not enforced
 
 // This errors:
 // `type T3<U> = U::Assoc;`
@@ -53,7 +54,7 @@ type T2<U> where U: Bound = U::Assoc;  //~ WARN not enforced in type aliases
 type T4<U> = <U as Bound>::Assoc;
 
 // Make sure the help about associated types is not shown incorrectly
-type T5<U: Bound> = <U as Bound>::Assoc;  //~ WARN not enforced in type aliases
-type T6<U: Bound> = ::std::vec::Vec<U>;  //~ WARN not enforced in type aliases
+type T5<U: Bound> = <U as Bound>::Assoc;  //~ WARN are not enforced
+type T6<U: Bound> = ::std::vec::Vec<U>;  //~ WARN are not enforced
 
 fn main() {}
diff --git a/tests/ui/type/type-alias-bounds.stderr b/tests/ui/type/type-alias-bounds.stderr
index 92e573393c90e..15c0090106655 100644
--- a/tests/ui/type/type-alias-bounds.stderr
+++ b/tests/ui/type/type-alias-bounds.stderr
@@ -1,121 +1,132 @@
-warning: bounds on generic parameters are not enforced in type aliases
+warning: bounds on generic parameters in type aliases are not enforced
   --> $DIR/type-alias-bounds.rs:8:14
    |
 LL | type SVec<T: Send + Send> = Vec<T>;
-   |              ^^^^   ^^^^
-   |
+   |            --^^^^---^^^^
+   |            |        |
+   |            |        will not be checked at usage sites of the type alias
+   |            help: remove this bound
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
    = note: `#[warn(type_alias_bounds)]` on by default
-help: the bound will not be checked when the type alias is used, and should be removed
-   |
-LL - type SVec<T: Send + Send> = Vec<T>;
-LL + type SVec<T> = Vec<T>;
-   |
 
-warning: where clauses are not enforced in type aliases
+warning: where clauses on type aliases are not enforced
   --> $DIR/type-alias-bounds.rs:10:21
    |
 LL | type S2Vec<T> where T: Send = Vec<T>;
-   |                     ^^^^^^^
-   |
-help: the clause will not be checked when the type alias is used, and should be removed
-   |
-LL - type S2Vec<T> where T: Send = Vec<T>;
-LL + type S2Vec<T>  = Vec<T>;
-   |
+   |               ------^^^^^^^
+   |               |     |
+   |               |     will not be checked at usage sites of the type alias
+   |               help: remove this where clause
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
-warning: bounds on generic parameters are not enforced in type aliases
+warning: bounds on generic parameters in type aliases are not enforced
   --> $DIR/type-alias-bounds.rs:12:19
    |
 LL | type VVec<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>);
-   |                   ^^   ^^
-   |
-help: the bound will not be checked when the type alias is used, and should be removed
-   |
-LL - type VVec<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>);
-LL + type VVec<'b, 'a> = (&'b u32, Vec<&'a i32>);
-   |
+   |                 --^^---^^
+   |                 |      |
+   |                 |      will not be checked at usage sites of the type alias
+   |                 help: remove this bound
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
-warning: bounds on generic parameters are not enforced in type aliases
+warning: bounds on generic parameters in type aliases are not enforced
   --> $DIR/type-alias-bounds.rs:14:18
    |
 LL | type WVec<'b, T: 'b + 'b> = (&'b u32, Vec<T>);
-   |                  ^^   ^^
-   |
-help: the bound will not be checked when the type alias is used, and should be removed
-   |
-LL - type WVec<'b, T: 'b + 'b> = (&'b u32, Vec<T>);
-LL + type WVec<'b, T> = (&'b u32, Vec<T>);
-   |
+   |                --^^---^^
+   |                |      |
+   |                |      will not be checked at usage sites of the type alias
+   |                help: remove this bound
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
-warning: where clauses are not enforced in type aliases
+warning: where clauses on type aliases are not enforced
   --> $DIR/type-alias-bounds.rs:16:25
    |
 LL | type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec<T>);
-   |                         ^^^^^  ^^^^^
-   |
-help: the clause will not be checked when the type alias is used, and should be removed
-   |
-LL - type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec<T>);
-LL + type W2Vec<'b, T>  = (&'b u32, Vec<T>);
-   |
+   |                   ------^^^^^--^^^^^
+   |                   |            |
+   |                   |            will not be checked at usage sites of the type alias
+   |                   help: remove this where clause
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
-warning: bounds on generic parameters are not enforced in type aliases
-  --> $DIR/type-alias-bounds.rs:47:12
+warning: bounds on generic parameters in type aliases are not enforced
+  --> $DIR/type-alias-bounds.rs:48:12
    |
 LL | type T1<U: Bound> = U::Assoc;
-   |            ^^^^^
+   |            ^^^^^ will not be checked at usage sites of the type alias
    |
-help: use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to associated types in type aliases
-  --> $DIR/type-alias-bounds.rs:47:21
-   |
-LL | type T1<U: Bound> = U::Assoc;
-   |                     ^^^^^^^^
-help: the bound will not be checked when the type alias is used, and should be removed
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
+help: remove this bound
    |
 LL - type T1<U: Bound> = U::Assoc;
 LL + type T1<U> = U::Assoc;
    |
+help: fully qualify this associated type
+   |
+LL | type T1<U: Bound> = <U as /* Trait */>::Assoc;
+   |                     +  +++++++++++++++
 
-warning: where clauses are not enforced in type aliases
-  --> $DIR/type-alias-bounds.rs:48:18
+warning: where clauses on type aliases are not enforced
+  --> $DIR/type-alias-bounds.rs:49:18
    |
 LL | type T2<U> where U: Bound = U::Assoc;
-   |                  ^^^^^^^^
+   |                  ^^^^^^^^ will not be checked at usage sites of the type alias
    |
-help: use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to associated types in type aliases
-  --> $DIR/type-alias-bounds.rs:48:29
-   |
-LL | type T2<U> where U: Bound = U::Assoc;
-   |                             ^^^^^^^^
-help: the clause will not be checked when the type alias is used, and should be removed
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
+help: remove this where clause
    |
 LL - type T2<U> where U: Bound = U::Assoc;
 LL + type T2<U>  = U::Assoc;
    |
+help: fully qualify this associated type
+   |
+LL | type T2<U> where U: Bound = <U as /* Trait */>::Assoc;
+   |                             +  +++++++++++++++
 
-warning: bounds on generic parameters are not enforced in type aliases
-  --> $DIR/type-alias-bounds.rs:56:12
+warning: bounds on generic parameters in type aliases are not enforced
+  --> $DIR/type-alias-bounds.rs:57:12
    |
 LL | type T5<U: Bound> = <U as Bound>::Assoc;
-   |            ^^^^^
-   |
-help: the bound will not be checked when the type alias is used, and should be removed
-   |
-LL - type T5<U: Bound> = <U as Bound>::Assoc;
-LL + type T5<U> = <U as Bound>::Assoc;
-   |
+   |          --^^^^^
+   |          | |
+   |          | will not be checked at usage sites of the type alias
+   |          help: remove this bound
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
-warning: bounds on generic parameters are not enforced in type aliases
-  --> $DIR/type-alias-bounds.rs:57:12
+warning: bounds on generic parameters in type aliases are not enforced
+  --> $DIR/type-alias-bounds.rs:58:12
    |
 LL | type T6<U: Bound> = ::std::vec::Vec<U>;
-   |            ^^^^^
-   |
-help: the bound will not be checked when the type alias is used, and should be removed
-   |
-LL - type T6<U: Bound> = ::std::vec::Vec<U>;
-LL + type T6<U> = ::std::vec::Vec<U>;
-   |
+   |          --^^^^^
+   |          | |
+   |          | will not be checked at usage sites of the type alias
+   |          help: remove this bound
+   |
+   = note: this is a known limitation of the type checker that may be lifted in a future edition.
+           see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information
+   = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics
 
 warning: 9 warnings emitted
 

From d67b61637e96e6e72b5a676b2f02d1a01b3a94a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Sun, 16 Jun 2024 12:22:12 +0200
Subject: [PATCH 8/9] Make lint type_alias_bounds's removal sugg
 maybe-incorrect if the RHS contains shorthand assoc tys

---
 compiler/rustc_lint/src/builtin.rs |  91 +++++++++---------
 compiler/rustc_lint/src/lints.rs   | 147 ++++++++++++-----------------
 2 files changed, 109 insertions(+), 129 deletions(-)

diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index a8c8c71927aaf..4e1e19e522e9d 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -32,11 +32,10 @@ use crate::{
         BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
         BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns,
         BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds,
-        BuiltinTypeAliasParamBoundsSuggestion, BuiltinUngatedAsyncFnTrackCaller,
-        BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub,
-        BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment,
-        BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel,
-        TypeAliasBoundsQualifyAssocTysSugg,
+        BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit,
+        BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe,
+        BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub,
+        BuiltinWhileTrue, InvalidAsmLabel,
     },
     EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext,
 };
@@ -1406,9 +1405,23 @@ declare_lint_pass!(
     TypeAliasBounds => [TYPE_ALIAS_BOUNDS]
 );
 
+impl TypeAliasBounds {
+    pub(crate) fn affects_object_lifetime_defaults(pred: &hir::WherePredicate<'_>) -> bool {
+        // Bounds of the form `T: 'a` with `T` type param affect object lifetime defaults.
+        if let hir::WherePredicate::BoundPredicate(pred) = pred
+            && pred.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Outlives(_)))
+            && pred.bound_generic_params.is_empty() // indeed, even if absent from the RHS
+            && pred.bounded_ty.as_generic_param().is_some()
+        {
+            return true;
+        }
+        false
+    }
+}
+
 impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
     fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
-        let hir::ItemKind::TyAlias(hir_ty, generics) = &item.kind else { return };
+        let hir::ItemKind::TyAlias(hir_ty, generics) = item.kind else { return };
 
         // There must not be a where clause.
         if generics.predicates.is_empty() {
@@ -1437,7 +1450,6 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
         let mut where_spans = Vec::new();
         let mut inline_spans = Vec::new();
         let mut inline_sugg = Vec::new();
-        let mut affects_object_lifetime_defaults = false;
 
         for p in generics.predicates {
             let span = p.span();
@@ -1449,45 +1461,22 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
                 }
                 inline_sugg.push((span, String::new()));
             }
-
-            // FIXME(fmease): Move this into a "diagnostic decorator" for increased laziness
-            // Bounds of the form `T: 'a` where `T` is a type param of
-            // the type alias affect object lifetime defaults.
-            if !affects_object_lifetime_defaults
-                && let hir::WherePredicate::BoundPredicate(pred) = p
-                && pred.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Outlives(_)))
-                && pred.bound_generic_params.is_empty()
-                && let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = pred.bounded_ty.kind
-                && let Res::Def(DefKind::TyParam, _) = path.res
-            {
-                affects_object_lifetime_defaults = true;
-            }
         }
 
-        // FIXME(fmease): Add a disclaimer (in the form of a multi-span note) that the removal of
-        //                type-param-outlives-bounds affects OLDs and explicit object lifetime
-        //                bounds might be required [...].
-        // FIXME(fmease): The applicability should also depend on the outcome of the HIR walker
-        //                inside of `TypeAliasBoundsQualifyAssocTysSugg`: Whether it found a
-        //                shorthand projection or not.
-        let applicability = if affects_object_lifetime_defaults {
-            Applicability::MaybeIncorrect
-        } else {
-            Applicability::MachineApplicable
-        };
-
-        let mut qualify_assoc_tys_sugg = Some(TypeAliasBoundsQualifyAssocTysSugg { ty: hir_ty });
-        let enable_feat_help = cx.tcx.sess.is_nightly_build().then_some(());
+        let mut ty = Some(hir_ty);
+        let enable_feat_help = cx.tcx.sess.is_nightly_build();
 
         if let [.., label_sp] = *where_spans {
             cx.emit_span_lint(
                 TYPE_ALIAS_BOUNDS,
                 where_spans,
-                BuiltinTypeAliasBounds::WhereClause {
+                BuiltinTypeAliasBounds {
+                    in_where_clause: true,
                     label: label_sp,
                     enable_feat_help,
-                    suggestion: (generics.where_clause_span, applicability),
-                    qualify_assoc_tys_sugg: qualify_assoc_tys_sugg.take(),
+                    suggestions: vec![(generics.where_clause_span, String::new())],
+                    preds: generics.predicates,
+                    ty: ty.take(),
                 },
             );
         }
@@ -1495,20 +1484,36 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
             cx.emit_span_lint(
                 TYPE_ALIAS_BOUNDS,
                 inline_spans,
-                BuiltinTypeAliasBounds::ParamBounds {
+                BuiltinTypeAliasBounds {
+                    in_where_clause: false,
                     label: label_sp,
                     enable_feat_help,
-                    suggestion: BuiltinTypeAliasParamBoundsSuggestion {
-                        suggestions: inline_sugg,
-                        applicability,
-                    },
-                    qualify_assoc_tys_sugg,
+                    suggestions: inline_sugg,
+                    preds: generics.predicates,
+                    ty,
                 },
             );
         }
     }
 }
 
+pub(crate) struct ShorthandAssocTyCollector {
+    pub(crate) qselves: Vec<Span>,
+}
+
+impl hir::intravisit::Visitor<'_> for ShorthandAssocTyCollector {
+    fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, _: Span) {
+        // Look for "type-parameter shorthand-associated-types". I.e., paths of the
+        // form `T::Assoc` with `T` type param. These are reliant on trait bounds.
+        if let hir::QPath::TypeRelative(qself, _) = qpath
+            && qself.as_generic_param().is_some()
+        {
+            self.qselves.push(qself.span);
+        }
+        hir::intravisit::walk_qpath(self, qpath, id)
+    }
+}
+
 declare_lint! {
     /// The `trivial_bounds` lint detects trait bounds that don't depend on
     /// any type parameters.
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 6cf8b9330fc40..b833d56c9bc95 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -2,8 +2,10 @@
 #![allow(rustc::untranslatable_diagnostic)]
 use std::num::NonZero;
 
-use crate::errors::RequestedLevel;
+use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds};
+use crate::errors::{OverruledAttributeSub, RequestedLevel};
 use crate::fluent_generated as fluent;
+use crate::LateContext;
 use rustc_errors::{
     codes::*, Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString,
     ElidedLifetimeInPathSubdiag, EmissionGuarantee, LintDiagnostic, MultiSpan, SubdiagMessageOp,
@@ -22,8 +24,6 @@ use rustc_span::{
     Span, Symbol,
 };
 
-use crate::{builtin::InitError, errors::OverruledAttributeSub, LateContext};
-
 // array_into_iter.rs
 #[derive(LintDiagnostic)]
 #[diag(lint_shadowed_into_iter)]
@@ -268,97 +268,72 @@ pub struct MacroExprFragment2024 {
     pub suggestion: Span,
 }
 
-#[derive(LintDiagnostic)]
-pub enum BuiltinTypeAliasBounds<'a, 'hir> {
-    #[diag(lint_builtin_type_alias_bounds_where_clause)]
-    #[note(lint_builtin_type_alias_bounds_limitation_note)]
-    WhereClause {
-        #[label(lint_builtin_type_alias_bounds_label)]
-        label: Span,
-        #[help(lint_builtin_type_alias_bounds_enable_feat_help)]
-        enable_feat_help: Option<()>,
-        #[suggestion(code = "")]
-        suggestion: (Span, Applicability),
-        #[subdiagnostic]
-        qualify_assoc_tys_sugg: Option<TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir>>,
-    },
-    #[diag(lint_builtin_type_alias_bounds_param_bounds)]
-    #[note(lint_builtin_type_alias_bounds_limitation_note)]
-    ParamBounds {
-        #[label(lint_builtin_type_alias_bounds_label)]
-        label: Span,
-        #[help(lint_builtin_type_alias_bounds_enable_feat_help)]
-        enable_feat_help: Option<()>,
-        #[subdiagnostic]
-        suggestion: BuiltinTypeAliasParamBoundsSuggestion,
-        #[subdiagnostic]
-        qualify_assoc_tys_sugg: Option<TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir>>,
-    },
-}
-
-pub struct BuiltinTypeAliasParamBoundsSuggestion {
+pub struct BuiltinTypeAliasBounds<'a, 'hir> {
+    pub in_where_clause: bool,
+    pub label: Span,
+    pub enable_feat_help: bool,
     pub suggestions: Vec<(Span, String)>,
-    pub applicability: Applicability,
+    pub preds: &'hir [hir::WherePredicate<'hir>],
+    pub ty: Option<&'a hir::Ty<'hir>>,
 }
 
-impl Subdiagnostic for BuiltinTypeAliasParamBoundsSuggestion {
-    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
-        self,
-        diag: &mut Diag<'_, G>,
-        _f: &F,
-    ) {
-        diag.arg("count", self.suggestions.len());
-        diag.multipart_suggestion(fluent::lint_suggestion, self.suggestions, self.applicability);
-    }
-}
-
-pub struct TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir> {
-    pub ty: &'a hir::Ty<'hir>,
-}
+impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_, '_> {
+    fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
+        diag.primary_message(if self.in_where_clause {
+            fluent::lint_builtin_type_alias_bounds_where_clause
+        } else {
+            fluent::lint_builtin_type_alias_bounds_param_bounds
+        });
+        diag.span_label(self.label, fluent::lint_builtin_type_alias_bounds_label);
+        diag.note(fluent::lint_builtin_type_alias_bounds_limitation_note);
+        if self.enable_feat_help {
+            diag.help(fluent::lint_builtin_type_alias_bounds_enable_feat_help);
+        }
 
-impl<'a, 'hir> Subdiagnostic for TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir> {
-    fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
-        self,
-        diag: &mut Diag<'_, G>,
-        _f: &F,
-    ) {
         // We perform the walk in here instead of in `<TypeAliasBounds as LateLintPass>` to
         // avoid doing throwaway work in case the lint ends up getting suppressed.
-
-        use hir::intravisit::Visitor;
-        struct ProbeShorthandAssocTys<'a, 'b, G: EmissionGuarantee> {
-            diag: &'a mut Diag<'b, G>,
+        let mut collector = ShorthandAssocTyCollector { qselves: Vec::new() };
+        if let Some(ty) = self.ty {
+            hir::intravisit::Visitor::visit_ty(&mut collector, ty);
         }
-        impl<'a, 'b, G: EmissionGuarantee> Visitor<'_> for ProbeShorthandAssocTys<'a, 'b, G> {
-            fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, _: Span) {
-                // Look for "type-parameter shorthand-associated-types". I.e., paths of the
-                // form `T::Assoc` with `T` type param. These are reliant on trait bounds.
-                // Suggest fully qualifying them via `<T as /* Trait */>::Assoc`.
-                //
-                // Instead of attempting to figure out the necessary trait ref, just use a
-                // placeholder. Since we don't record type-dependent resolutions for non-body
-                // items like type aliases, we can't simply deduce the corresp. trait from
-                // the HIR path alone without rerunning parts of HIR ty lowering here
-                // (namely `probe_single_ty_param_bound_for_assoc_ty`) which is infeasible.
-                //
-                // (We could employ some simple heuristics but that's likely not worth it).
-                if let hir::QPath::TypeRelative(qself, _) = qpath
-                    && let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = qself.kind
-                    && let hir::def::Res::Def(hir::def::DefKind::TyParam, _) = path.res
-                {
-                    self.diag.multipart_suggestion(
-                        fluent::lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg,
-                        vec![
-                            (qself.span.shrink_to_lo(), "<".into()),
-                            (qself.span.shrink_to_hi(), " as /* Trait */>".into()),
-                        ],
-                        Applicability::HasPlaceholders,
-                    );
-                }
-                hir::intravisit::walk_qpath(self, qpath, id)
-            }
+
+        let affect_object_lifetime_defaults = self
+            .preds
+            .iter()
+            .filter(|pred| pred.in_where_clause() == self.in_where_clause)
+            .any(|pred| TypeAliasBounds::affects_object_lifetime_defaults(pred));
+
+        // If there are any shorthand assoc tys, then the bounds can't be removed automatically.
+        // The user first needs to fully qualify the assoc tys.
+        let applicability = if !collector.qselves.is_empty() || affect_object_lifetime_defaults {
+            Applicability::MaybeIncorrect
+        } else {
+            Applicability::MachineApplicable
+        };
+
+        diag.arg("count", self.suggestions.len());
+        diag.multipart_suggestion(fluent::lint_suggestion, self.suggestions, applicability);
+
+        // Suggest fully qualifying paths of the form `T::Assoc` with `T` type param via
+        // `<T as /* Trait */>::Assoc` to remove their reliance on any type param bounds.
+        //
+        // Instead of attempting to figure out the necessary trait ref, just use a
+        // placeholder. Since we don't record type-dependent resolutions for non-body
+        // items like type aliases, we can't simply deduce the corresp. trait from
+        // the HIR path alone without rerunning parts of HIR ty lowering here
+        // (namely `probe_single_ty_param_bound_for_assoc_ty`) which is infeasible.
+        //
+        // (We could employ some simple heuristics but that's likely not worth it).
+        for qself in collector.qselves {
+            diag.multipart_suggestion(
+                fluent::lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg,
+                vec![
+                    (qself.shrink_to_lo(), "<".into()),
+                    (qself.shrink_to_hi(), " as /* Trait */>".into()),
+                ],
+                Applicability::HasPlaceholders,
+            );
         }
-        ProbeShorthandAssocTys { diag }.visit_ty(self.ty);
     }
 }
 

From 5859dff7429436498aed7fc7efed88a73b3eaf4d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Sun, 16 Jun 2024 15:53:01 +0200
Subject: [PATCH 9/9] Update the description of lint type_alias_bounds

---
 compiler/rustc_lint/src/builtin.rs | 30 +++++++++++++++++++++---------
 1 file changed, 21 insertions(+), 9 deletions(-)

diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 4e1e19e522e9d..ab0b47d48e5b5 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -1390,20 +1390,32 @@ declare_lint! {
     ///
     /// ### Explanation
     ///
-    /// The trait bounds in a type alias are currently ignored, and should not
-    /// be included to avoid confusion. This was previously allowed
-    /// unintentionally; this may become a hard error in the future.
+    /// Trait and lifetime bounds on generic parameters and in where clauses of
+    /// type aliases are not checked at usage sites of the type alias. Moreover,
+    /// they are not thoroughly checked for correctness at their definition site
+    /// either similar to the aliased type.
+    ///
+    /// This is a known limitation of the type checker that may be lifted in a
+    /// future edition. Permitting such bounds in light of this was unintentional.
+    ///
+    /// While these bounds may have secondary effects such as enabling the use of
+    /// "shorthand" associated type paths[^1] and affecting the default trait
+    /// object lifetime[^2] of trait object types passed to the type alias, this
+    /// should not have been allowed until the aforementioned restrictions of the
+    /// type checker have been lifted.
+    ///
+    /// Using such bounds is highly discouraged as they are actively misleading.
+    ///
+    /// [^1]: I.e., paths of the form `T::Assoc` where `T` is a type parameter
+    /// bounded by trait `Trait` which defines an associated type called `Assoc`
+    /// as opposed to a fully qualified path of the form `<T as Trait>::Assoc`.
+    /// [^2]: <https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes>
     TYPE_ALIAS_BOUNDS,
     Warn,
     "bounds in type aliases are not enforced"
 }
 
-declare_lint_pass!(
-    /// Lint for trait and lifetime bounds in type aliases being mostly ignored.
-    /// They are relevant when using associated types, but otherwise neither checked
-    /// at definition site nor enforced at use site.
-    TypeAliasBounds => [TYPE_ALIAS_BOUNDS]
-);
+declare_lint_pass!(TypeAliasBounds => [TYPE_ALIAS_BOUNDS]);
 
 impl TypeAliasBounds {
     pub(crate) fn affects_object_lifetime_defaults(pred: &hir::WherePredicate<'_>) -> bool {