Change:
Currently on nightly the declaration for looks like: ! While the actual declaration with attributes stripped is: Previously up till 1.48.0 this rendered as: ! Which is not great, but at least it has the information there. I tried to identify the PR which changed this in 1.49.0, but skimming the list from a few search queries none of them stood out to me. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
Rendering the declaration without the attributes and outside core/std works just fine: !
Ooooh, it works on that it has this issue. Similarly on 1.48.0 showed the associated type bound nicely, only showed it as a clause.
I may have found the reason why we are skipping the associated type binding. So when an external item (in our case an associated type) gets documented it goes through which then goes through this visitor: Further down we get to: which produces (for the associated item): Then produces: <details<summaryOutput</summary</detailsThe last boils down to which gets discarded further down here (in a operation): Afaics, the reason the unexported trait gets properly "documented" is because a different code path is run which doesn't involve clean on a .
label A-cross-crate-reexports A-associated-items Update: The output is still incorrect but it now looks like this: claim

Discussion:
} if let ty::TraitContainer = assoc_item.container { <ins> // FIXME(fmease): `tcx.explicit_item_bounds` does not contain the bounds of GATs, //                e.g. the bounds `Copy`, `Display` & (implicitly) `Sized` in //                `type Assoc<T: Copy> where T: Display`. This also means that we //                later incorrectly render `where T: ?Sized`. // //                The result of `tcx.explicit_predicates_of` *does* contain them but //                it does not contain the other bounds / predicates we need. //                Either merge those two interned lists somehow or refactor //                `clean_ty_generics` to call `explicit_item_bounds` by itself. </ins> let bounds = tcx.explicit_item_bounds(assoc_item.def_id); let predicates = ty::GenericPredicates { parent: None, predicates: bounds }; let mut generics =

use rustc_data_structures::fx::FxIndexMap; use rustc_hir::def_id::DefId; use rustc_middle::ty; <del> use rustc_span::Symbol; </del> use crate::clean; use crate::clean::GenericArgs as PP;

// // We use `FxIndexMap` so that the insertion order is preserved to prevent messing up to // the order of the generated bounds. <del> let mut params: FxIndexMap<Symbol, (Vec<_>, Vec<_>)> = FxIndexMap::default(); </del> <ins> let mut tybounds = FxIndexMap::default(); </ins> let mut lifetimes = Vec::new(); let mut equalities = Vec::new(); <del> let mut tybounds = Vec::new(); </del> for clause in clauses { match clause { <del> WP::BoundPredicate { ty, bounds, bound_params } => match ty { clean::Generic(s) => { let (b, p) = params.entry(s).or_default(); b.extend(bounds); p.extend(bound_params); } t => tybounds.push((t, (bounds, bound_params))), }, </del> <ins> WP::BoundPredicate { ty, bounds, bound_params } => { let (b, p): &mut (Vec<_>, Vec<_>) = tybounds.entry(ty).or_default(); b.extend(bounds); p.extend(bound_params); } </ins> WP::RegionPredicate { lifetime, bounds } => { lifetimes.push((lifetime, bounds)); }

} // Look for equality predicates on associated types that can be merged into <del> // general bound predicates </del> <ins> // general bound predicates. </ins> equalities.retain(|&(ref lhs, ref rhs)| { <del> let Some((self_, trait_did, name)) = lhs.projection() else { return true; }; let clean::Generic(generic) = self_ else { return true }; let Some((bounds, _)) = params.get_mut(generic) else { return true }; </del> <ins> let Some((ty, trait_did, name)) = lhs.projection() else { return true; }; // FIXME(fmease): We don't handle HRTBs correctly here. //                Pass `_bound_params` (higher-rank lifetimes) to a modified version of //                `merge_bounds`. That vector is currently always empty though since we //                don't keep track of late-bound lifetimes when cleaning projection //                predicates to cleaned equality predicates while we should first query //                them with `collect_referenced_late_bound_regions` and then store them //                (or something similar). For prior art, see `clean::auto_trait`. let Some((bounds, _bound_params)) = tybounds.get_mut(ty) else { return true }; </ins> merge_bounds(cx, bounds, trait_did, name, rhs) });

clauses.extend( lifetimes.into_iter().map(|(lt, bounds)| WP::RegionPredicate { lifetime: lt, bounds }), ); <del> clauses.extend(params.into_iter().map(|(k, (bounds, params))| WP::BoundPredicate { ty: clean::Generic(k), bounds, bound_params: params, })); </del> clauses.extend(tybounds.into_iter().map(|(ty, (bounds, bound_params))| WP::BoundPredicate { ty, bounds,

<ins> <h4 class="code-header">type <a href="#associatedtype.Out0" class="associatedtype">Out0</a>: <a class="trait" href="../assoc_item_trait_bounds_with_bindings/trait.Support.html" title="trait assoc_item_trait_bounds_with_bindings::Support">Support</a>&lt;Item = <a class="primitive" href="{{channel}}/std/primitive.unit.html">()</a>&gt;</h4> </ins>  No newline at end of file

<ins> <h4 class="code-header">type <a href="#associatedtype.Out9" class="associatedtype">Out9</a>: <a class="trait" href="{{channel}}/core/ops/function/trait.FnMut.html" title="trait core::ops::function::FnMut">FnMut</a>(<a class="primitive" href="{{channel}}/std/primitive.i32.html">i32</a>) -&gt; <a class="primitive" href="{{channel}}/std/primitive.bool.html">bool</a> + <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a></h4> </ins>  No newline at end of file

<ins> // Regression test for issues #77763, #84579 and #102142. #![crate_name = "main"] // aux-build:assoc_item_trait_bounds_with_bindings.rs // build-aux-docs // ignore-cross-compile extern crate assoc_item_trait_bounds_with_bindings as aux; // FIXME(fmease): Don't render an incorrect `T: ?Sized` where-clause for parameters //                of GATs like `Main::Out{2,4}`. Add a snapshot test once it's fixed. // FIXME(fmease): Print the `for<>` parameter list in the bounds of //                `Main::Out{6,11,12}`. // @has main/trait.Main.html // @has - '//*[@id="associatedtype.Out0"]' 'type Out0: Support<Item = ()>' // @has - '//*[@id="associatedtype.Out1"]' 'type Out1: Support<Item = Self::Item>' // @has - '//*[@id="associatedtype.Out2"]' 'type Out2<T>: Support<Item = T>' // @has - '//*[@id="associatedtype.Out3"]' 'type Out3: Support<Produce<()> = bool>' // @has - '//*[@id="associatedtype.Out4"]' 'type Out4<T>: Support<Produce<T> = T>' // @has - '//*[@id="associatedtype.Out5"]' "type Out5: Support<Output<'static> = &'static ()>" // @has - '//*[@id="associatedtype.Out6"]' "type Out6: Support<Output<'a> = &'a ()>" // @has - '//*[@id="associatedtype.Out7"]' "type Out7: Support<Item = String, Produce<i32> = u32> + Unrelated" // @has - '//*[@id="associatedtype.Out8"]' "type Out8: Unrelated + Protocol<i16, Q1 = u128, Q0 = ()>" // @has - '//*[@id="associatedtype.Out9"]' "type Out9: FnMut(i32) -> bool + Clone" // @has - '//*[@id="associatedtype.Out10"]' "type Out10<'q>: Support<Output<'q> = ()>" // @has - '//*[@id="associatedtype.Out11"]' "type Out11: Helper<A<'s> = &'s (), B<'r> = ()>" // @has - '//*[@id="associatedtype.Out12"]' "type Out12: Helper<B<'w> = Cow<'w, str>, A<'w> = bool>" // // Snapshots: Check that we do not render any where-clauses for those associated types since all of // the trait bounds contained within were moved to the bounds of the respective item. // // @snapshot out0 - '//*[@id="associatedtype.Out0"]/*[@class="code-header"]' // @snapshot out9 - '//*[@id="associatedtype.Out9"]/*[@class="code-header"]' // // @has - '//*[@id="tymethod.make"]'  // "fn make<F>(F, impl FnMut(&str) -> bool) // where  //     F: FnOnce(u32) -> String,  //     Self::Out2<()>: Protocol<u8, Q0 = Self::Item, Q1 = ()>" pub use aux::Main; </ins>

<ins> pub trait Main { type Item; type Out0: Support<Item = ()>; type Out1: Support<Item = Self::Item>; type Out2<T>: Support<Item = T>; type Out3: Support<Produce<()> = bool>; type Out4<T>: Support<Produce<T> = T>; type Out5: Support<Output<'static> = &'static ()>; type Out6: for<'a> Support<Output<'a> = &'a ()>; type Out7: Support<Item = String, Produce<i32> = u32> + Unrelated; type Out8: Unrelated + Protocol<i16, Q1 = u128, Q0 = ()>; type Out9: FnMut(i32) -> bool + Clone; type Out10<'q>: Support<Output<'q> = ()>; type Out11: for<'r, 's> Helper<A<'s> = &'s (), B<'r> = ()>; type Out12: for<'w> Helper<B<'w> = std::borrow::Cow<'w, str>, A<'w> = bool>; fn make<F>(_: F, _: impl FnMut(&str) -> bool) where F: FnOnce(u32) -> String, Self::Out2<()>: Protocol<u8, Q0 = Self::Item, Q1 = ()>; } pub trait Support { type Item; type Output<'a>; type Produce<T>; } pub trait Protocol<K> { type Q0; type Q1; } pub trait Unrelated {} pub trait Helper { type A<'q>; type B<'q>; } </ins>