Repository: rust-lang/rust (Rust)

Issue:
rustdoc doesn't render generic params & where-clauses of cross-crate GATs in certain sections
While rustdoc does indeed render generic parameters and where-clauses of cross-crate GATs in the `.item-decl` of traits or in the section *Required Associated Types* for example, it doesn't do so in section *Implementors* on trait pages or section *Trait Implementations* on an implementor's page. I haven't exhaustively checked yet where precisely they're dropped and where they aren't.

Reproducer:

```rs
// a.rs

pub struct S;

pub trait Tr {
    type F<T>
    where
        (T,): Clone;
}

impl Tr for S {
    type F<T> = T
    where
        (T,): Clone;
}
```

```rs
// b.rs

pub use a::*;
```

```sh
rustc a.rs --crate-type=lib --edition=2021
rustdoc b.rs --crate-type=lib --edition=2021 --extern=a=liba.rlib
```

![Screenshot 2023-06-21 at 20-44-55 Tr in unx - Rust](https://github.com/rust-lang/rust/assets/14913065/2d67c45b-b053-4b06-a0f5-1f20d38c7851)

@rustbot claim
@rustbot label C-bug T-rustdoc A-cross-crate-reexports

<!-- TRIAGEBOT_START -->

<!-- TRIAGEBOT_ASSIGN_START -->

<!-- TRIAGEBOT_ASSIGN_DATA_START$${"user":"fmease"}$$TRIAGEBOT_ASSIGN_DATA_END -->

<!-- TRIAGEBOT_ASSIGN_END -->
<!-- TRIAGEBOT_END -->


Discussion: strange that this wasn't fixed by #103190
that also came to my mind when I noticed this, looking into it



Fix (patch):
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index f86c32158e0b3..3dd3a6a8394f3 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1360,21 +1360,53 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
                 }
             }
 
+            let mut predicates = tcx.explicit_predicates_of(assoc_item.def_id).predicates;
             if let ty::TraitContainer = assoc_item.container {
                 let bounds = tcx
                     .explicit_item_bounds(assoc_item.def_id)
                     .subst_identity_iter_copied()
                     .map(|(c, s)| (c.as_predicate(), s));
-                let predicates = tcx.explicit_predicates_of(assoc_item.def_id).predicates;
-                let predicates =
-                    tcx.arena.alloc_from_iter(bounds.chain(predicates.iter().copied()));
-                let mut generics = clean_ty_generics(
-                    cx,
-                    tcx.generics_of(assoc_item.def_id),
-                    ty::GenericPredicates { parent: None, predicates },
-                );
-                // Filter out the bounds that are (likely?) directly attached to the associated type,
-                // as opposed to being located in the where clause.
+                predicates = tcx.arena.alloc_from_iter(bounds.chain(predicates.iter().copied()));
+            }
+            let mut generics = clean_ty_generics(
+                cx,
+                tcx.generics_of(assoc_item.def_id),
+                ty::GenericPredicates { parent: None, predicates },
+            );
+            // Move bounds that are (likely) directly attached to the parameters of the
+            // (generic) associated type from the where clause to the respective parameter.
+            // There is no guarantee that this is what the user actually wrote but we have
+            // no way of knowing.
+            let mut where_predicates = ThinVec::new();
+            for mut pred in generics.where_predicates {
+                if let WherePredicate::BoundPredicate { ty: Generic(arg), bounds, .. } = &mut pred
+                    && let Some(GenericParamDef {
+                        kind: GenericParamDefKind::Type { bounds: param_bounds, .. },
+                        ..
+                    }) = generics.params.iter_mut().find(|param| &param.name == arg)
+                {
+                    param_bounds.append(bounds);
+                } else if let WherePredicate::RegionPredicate { lifetime: Lifetime(arg), bounds } = &mut pred
+                    && let Some(GenericParamDef {
+                        kind: GenericParamDefKind::Lifetime { outlives: param_bounds },
+                        ..
+                    }) = generics.params.iter_mut().find(|param| &param.name == arg)
+                {
+                    param_bounds.extend(bounds.drain(..).map(|bound| match bound {
+                        GenericBound::Outlives(lifetime) => lifetime,
+                        _ => unreachable!(),
+                    }));
+                } else {
+                    where_predicates.push(pred);
+                }
+            }
+            generics.where_predicates = where_predicates;
+
+            if let ty::TraitContainer = assoc_item.container {
+                // Move bounds that are (likely) directly attached to the associated type
+                // from the where-clause to the associated type.
+                // There is no guarantee that this is what the user actually wrote but we have
+                // no way of knowing.
                 let mut bounds: Vec<GenericBound> = Vec::new();
                 generics.where_predicates.retain_mut(|pred| match *pred {
                     WherePredicate::BoundPredicate {
@@ -1431,33 +1463,6 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
                     }
                     None => bounds.push(GenericBound::maybe_sized(cx)),
                 }
-                // Move bounds that are (likely) directly attached to the parameters of the
-                // (generic) associated type from the where clause to the respective parameter.
-                // There is no guarantee that this is what the user actually wrote but we have
-                // no way of knowing.
-                let mut where_predicates = ThinVec::new();
-                for mut pred in generics.where_predicates {
-                    if let WherePredicate::BoundPredicate { ty: Generic(arg), bounds, .. } = &mut pred
-                    && let Some(GenericParamDef {
-                        kind: GenericParamDefKind::Type { bounds: param_bounds, .. },
-                        ..
-                    }) = generics.params.iter_mut().find(|param| &param.name == arg)
-                    {
-                        param_bounds.append(bounds);
-                    } else if let WherePredicate::RegionPredicate { lifetime: Lifetime(arg), bounds } = &mut pred
-                    && let Some(GenericParamDef {
-                        kind: GenericParamDefKind::Lifetime { outlives: param_bounds },
-                        ..
-                    }) = generics.params.iter_mut().find(|param| &param.name == arg) {
-                        param_bounds.extend(bounds.drain(..).map(|bound| match bound {
-                            GenericBound::Outlives(lifetime) => lifetime,
-                            _ => unreachable!(),
-                        }));
-                    } else {
-                        where_predicates.push(pred);
-                    }
-                }
-                generics.where_predicates = where_predicates;
 
                 if tcx.defaultness(assoc_item.def_id).has_value() {
                     AssocTypeItem(
@@ -1469,7 +1474,6 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
                                 None,
                             ),
                             generics,
-                            // FIXME: should we obtain the Type from HIR and pass it on here?
                             item_type: None,
                         }),
                         bounds,
@@ -1478,7 +1482,6 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
                     TyAssocTypeItem(generics, bounds)
                 }
             } else {
-                // FIXME: when could this happen? Associated items in inherent impls?
                 AssocTypeItem(
                     Box::new(Typedef {
                         type_: clean_middle_ty(
@@ -1487,12 +1490,11 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
                             Some(assoc_item.def_id),
                             None,
                         ),
-                        generics: Generics {
-                            params: ThinVec::new(),
-                            where_predicates: ThinVec::new(),
-                        },
+                        generics,
                         item_type: None,
                     }),
+                    // Associated types inside trait or inherent impls are not allowed to have
+                    // item bounds. Thus we don't attempt to move any bounds there.
                     Vec::new(),
                 )
             }