rustdoc: Improve handling inlined associated types
* All bounds are now discovered through the trait to be inlined. * The `?Sized` bound now renders correctly for inlined associated types. * All `QPath`s (`<A as B>::C`) instances are rendered as `A::C` where `C` is a hyperlink to the trait `B`. This should improve at least how the docs look at least. * Supertrait bounds are now separated and display as the source lists them. Closes #20727 Closes #21145
This commit is contained in:
parent
11f26f9995
commit
75ef0832ae
9 changed files with 382 additions and 69 deletions
|
@ -498,6 +498,35 @@ pub enum TyParamBound {
|
|||
TraitBound(PolyTrait, ast::TraitBoundModifier)
|
||||
}
|
||||
|
||||
impl TyParamBound {
|
||||
fn maybe_sized(cx: &DocContext) -> TyParamBound {
|
||||
use syntax::ast::TraitBoundModifier as TBM;
|
||||
let mut sized_bound = ty::BuiltinBound::BoundSized.clean(cx);
|
||||
if let TyParamBound::TraitBound(_, ref mut tbm) = sized_bound {
|
||||
*tbm = TBM::Maybe
|
||||
};
|
||||
sized_bound
|
||||
}
|
||||
|
||||
fn is_sized_bound(&self, cx: &DocContext) -> bool {
|
||||
use syntax::ast::TraitBoundModifier as TBM;
|
||||
if let Some(tcx) = cx.tcx_opt() {
|
||||
let sized_did = match tcx.lang_items.sized_trait() {
|
||||
Some(did) => did,
|
||||
None => return false
|
||||
};
|
||||
if let TyParamBound::TraitBound(PolyTrait {
|
||||
trait_: Type::ResolvedPath { did, .. }, ..
|
||||
}, TBM::None) = *self {
|
||||
if did == sized_did {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Clean<TyParamBound> for ast::TyParamBound {
|
||||
fn clean(&self, cx: &DocContext) -> TyParamBound {
|
||||
match *self {
|
||||
|
@ -835,7 +864,9 @@ impl<'tcx> Clean<Type> for ty::ProjectionTy<'tcx> {
|
|||
fn clean(&self, cx: &DocContext) -> Type {
|
||||
let trait_ = match self.trait_ref.clean(cx) {
|
||||
TyParamBound::TraitBound(t, _) => t.trait_,
|
||||
TyParamBound::RegionBound(_) => panic!("cleaning a trait got a region??"),
|
||||
TyParamBound::RegionBound(_) => {
|
||||
panic!("cleaning a trait got a region")
|
||||
}
|
||||
};
|
||||
Type::QPath {
|
||||
name: self.item_name.clean(cx),
|
||||
|
@ -868,28 +899,8 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics<'tcx>,
|
|||
subst::ParamSpace) {
|
||||
fn clean(&self, cx: &DocContext) -> Generics {
|
||||
use std::collections::HashSet;
|
||||
use syntax::ast::TraitBoundModifier as TBM;
|
||||
use self::WherePredicate as WP;
|
||||
|
||||
fn has_sized_bound(bounds: &[TyParamBound], cx: &DocContext) -> bool {
|
||||
if let Some(tcx) = cx.tcx_opt() {
|
||||
let sized_did = match tcx.lang_items.sized_trait() {
|
||||
Some(did) => did,
|
||||
None => return false
|
||||
};
|
||||
for bound in bounds {
|
||||
if let TyParamBound::TraitBound(PolyTrait {
|
||||
trait_: Type::ResolvedPath { did, .. }, ..
|
||||
}, TBM::None) = *bound {
|
||||
if did == sized_did {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
let (gens, preds, space) = *self;
|
||||
|
||||
// Bounds in the type_params and lifetimes fields are repeated in the
|
||||
|
@ -904,34 +915,38 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics<'tcx>,
|
|||
srp.clean(cx)
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
let where_predicates = preds.predicates.get_slice(space)
|
||||
.to_vec().clean(cx);
|
||||
let mut where_predicates = preds.predicates.get_slice(space)
|
||||
.to_vec().clean(cx);
|
||||
|
||||
// Type parameters have a Sized bound by default unless removed with
|
||||
// Type parameters and have a Sized bound by default unless removed with
|
||||
// ?Sized. Scan through the predicates and mark any type parameter with
|
||||
// a Sized bound, removing the bounds as we find them.
|
||||
//
|
||||
// Note that associated types also have a sized bound by default, but we
|
||||
// don't actually konw the set of associated types right here so that's
|
||||
// handled in cleaning associated types
|
||||
let mut sized_params = HashSet::new();
|
||||
let mut where_predicates = where_predicates.into_iter().filter_map(|pred| {
|
||||
if let WP::BoundPredicate { ty: Type::Generic(ref g), ref bounds } = pred {
|
||||
if has_sized_bound(&**bounds, cx) {
|
||||
sized_params.insert(g.clone());
|
||||
return None
|
||||
where_predicates.retain(|pred| {
|
||||
match *pred {
|
||||
WP::BoundPredicate { ty: Generic(ref g), ref bounds } => {
|
||||
if bounds.iter().any(|b| b.is_sized_bound(cx)) {
|
||||
sized_params.insert(g.clone());
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
_ => true,
|
||||
}
|
||||
Some(pred)
|
||||
}).collect::<Vec<_>>();
|
||||
});
|
||||
|
||||
// Finally, run through the type parameters again and insert a ?Sized
|
||||
// Run through the type parameters again and insert a ?Sized
|
||||
// unbound for any we didn't find to be Sized.
|
||||
for tp in &stripped_typarams {
|
||||
if !sized_params.contains(&tp.name) {
|
||||
let mut sized_bound = ty::BuiltinBound::BoundSized.clean(cx);
|
||||
if let TyParamBound::TraitBound(_, ref mut tbm) = sized_bound {
|
||||
*tbm = TBM::Maybe
|
||||
};
|
||||
where_predicates.push(WP::BoundPredicate {
|
||||
ty: Type::Generic(tp.name.clone()),
|
||||
bounds: vec![sized_bound]
|
||||
bounds: vec![TyParamBound::maybe_sized(cx)],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1597,17 +1612,7 @@ impl<'tcx> Clean<Type> for ty::Ty<'tcx> {
|
|||
}
|
||||
ty::ty_tup(ref t) => Tuple(t.clean(cx)),
|
||||
|
||||
ty::ty_projection(ref data) => {
|
||||
let trait_ref = match data.trait_ref.clean(cx) {
|
||||
TyParamBound::TraitBound(t, _) => t.trait_,
|
||||
TyParamBound::RegionBound(_) => panic!("cleaning a trait got a region??"),
|
||||
};
|
||||
Type::QPath {
|
||||
name: data.item_name.clean(cx),
|
||||
self_type: box data.trait_ref.self_ty().clean(cx),
|
||||
trait_: box trait_ref,
|
||||
}
|
||||
}
|
||||
ty::ty_projection(ref data) => data.clean(cx),
|
||||
|
||||
ty::ty_param(ref p) => Generic(token::get_name(p.name).to_string()),
|
||||
|
||||
|
@ -1881,6 +1886,22 @@ pub struct Path {
|
|||
pub segments: Vec<PathSegment>,
|
||||
}
|
||||
|
||||
impl Path {
|
||||
pub fn singleton(name: String) -> Path {
|
||||
Path {
|
||||
global: false,
|
||||
segments: vec![PathSegment {
|
||||
name: name,
|
||||
params: PathParameters::AngleBracketed {
|
||||
lifetimes: Vec::new(),
|
||||
types: Vec::new(),
|
||||
bindings: Vec::new()
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clean<Path> for ast::Path {
|
||||
fn clean(&self, cx: &DocContext) -> Path {
|
||||
Path {
|
||||
|
@ -2516,21 +2537,66 @@ impl Clean<Stability> for attr::Stability {
|
|||
|
||||
impl Clean<Item> for ty::AssociatedType {
|
||||
fn clean(&self, cx: &DocContext) -> Item {
|
||||
// When loading a cross-crate associated type, the bounds for this type
|
||||
// are actually located on the trait/impl itself, so we need to load
|
||||
// all of the generics from there and then look for bounds that are
|
||||
// applied to this associated type in question.
|
||||
let predicates = ty::lookup_predicates(cx.tcx(), self.container.id());
|
||||
let generics = match self.container {
|
||||
ty::TraitContainer(did) => {
|
||||
let def = ty::lookup_trait_def(cx.tcx(), did);
|
||||
(&def.generics, &predicates, subst::TypeSpace).clean(cx)
|
||||
}
|
||||
ty::ImplContainer(did) => {
|
||||
let ty = ty::lookup_item_type(cx.tcx(), did);
|
||||
(&ty.generics, &predicates, subst::TypeSpace).clean(cx)
|
||||
}
|
||||
};
|
||||
let my_name = self.name.clean(cx);
|
||||
let mut bounds = generics.where_predicates.iter().filter_map(|pred| {
|
||||
let (name, self_type, trait_, bounds) = match *pred {
|
||||
WherePredicate::BoundPredicate {
|
||||
ty: QPath { ref name, ref self_type, ref trait_ },
|
||||
ref bounds
|
||||
} => (name, self_type, trait_, bounds),
|
||||
_ => return None,
|
||||
};
|
||||
if *name != my_name { return None }
|
||||
match **trait_ {
|
||||
ResolvedPath { did, .. } if did == self.container.id() => {}
|
||||
_ => return None,
|
||||
}
|
||||
match **self_type {
|
||||
Generic(ref s) if *s == "Self" => {}
|
||||
_ => return None,
|
||||
}
|
||||
Some(bounds)
|
||||
}).flat_map(|i| i.iter().cloned()).collect::<Vec<_>>();
|
||||
|
||||
// Our Sized/?Sized bound didn't get handled when creating the generics
|
||||
// because we didn't actually get our whole set of bounds until just now
|
||||
// (some of them may have come from the trait). If we do have a sized
|
||||
// bound, we remove it, and if we don't then we add the `?Sized` bound
|
||||
// at the end.
|
||||
match bounds.iter().position(|b| b.is_sized_bound(cx)) {
|
||||
Some(i) => { bounds.remove(i); }
|
||||
None => bounds.push(TyParamBound::maybe_sized(cx)),
|
||||
}
|
||||
|
||||
Item {
|
||||
source: DUMMY_SP.clean(cx),
|
||||
name: Some(self.name.clean(cx)),
|
||||
attrs: Vec::new(),
|
||||
// FIXME(#20727): bounds are missing and need to be filled in from the
|
||||
// predicates on the trait itself
|
||||
inner: AssociatedTypeItem(vec![], None),
|
||||
visibility: None,
|
||||
attrs: inline::load_attrs(cx, cx.tcx(), self.def_id),
|
||||
inner: AssociatedTypeItem(bounds, None),
|
||||
visibility: self.vis.clean(cx),
|
||||
def_id: self.def_id,
|
||||
stability: None,
|
||||
stability: stability::lookup(cx.tcx(), self.def_id).clean(cx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Clean<Typedef> for (ty::TypeScheme<'a>, ty::GenericPredicates<'a>, ParamSpace) {
|
||||
impl<'a> Clean<Typedef> for (ty::TypeScheme<'a>, ty::GenericPredicates<'a>,
|
||||
ParamSpace) {
|
||||
fn clean(&self, cx: &DocContext) -> Typedef {
|
||||
let (ref ty_scheme, ref predicates, ps) = *self;
|
||||
Typedef {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue