1
Fork 0

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:
Alex Crichton 2015-04-07 00:16:35 -07:00
parent 11f26f9995
commit 75ef0832ae
9 changed files with 382 additions and 69 deletions

View file

@ -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 {