diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2015-04-07 00:16:35 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2015-04-07 17:54:34 -0700 |
| commit | 75ef0832ae00477f837a73356ea7f12f64134c7c (patch) | |
| tree | 589e3b6fb2f10c5d5905650bbd936b8fd249a04f /src/librustdoc | |
| parent | 11f26f999581e639233e64f4f1fbc210a2bb856a (diff) | |
| download | rust-75ef0832ae00477f837a73356ea7f12f64134c7c.tar.gz rust-75ef0832ae00477f837a73356ea7f12f64134c7c.zip | |
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
Diffstat (limited to 'src/librustdoc')
| -rw-r--r-- | src/librustdoc/clean/inline.rs | 52 | ||||
| -rw-r--r-- | src/librustdoc/clean/mod.rs | 176 | ||||
| -rw-r--r-- | src/librustdoc/html/format.rs | 35 |
3 files changed, 195 insertions, 68 deletions
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index e4b2e82b21b..24184bae95b 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -150,11 +150,14 @@ pub fn build_external_trait(cx: &DocContext, tcx: &ty::ctxt, let def = ty::lookup_trait_def(tcx, did); let trait_items = ty::trait_items(tcx, did).clean(cx); let predicates = ty::lookup_predicates(tcx, did); + let generics = (&def.generics, &predicates, subst::TypeSpace).clean(cx); + let generics = filter_non_trait_generics(did, generics); + let (generics, supertrait_bounds) = separate_supertrait_bounds(generics); clean::Trait { unsafety: def.unsafety, - generics: (&def.generics, &predicates, subst::TypeSpace).clean(cx), + generics: generics, items: trait_items, - bounds: vec![], // supertraits can be found in the list of predicates + bounds: supertrait_bounds, } } @@ -447,3 +450,48 @@ fn build_static(cx: &DocContext, tcx: &ty::ctxt, expr: "\n\n\n".to_string(), // trigger the "[definition]" links } } + +/// A trait's generics clause actually contains all of the predicates for all of +/// its associated types as well. We specifically move these clauses to the +/// associated types instead when displaying, so when we're genering the +/// generics for the trait itself we need to be sure to remove them. +/// +/// The inverse of this filtering logic can be found in the `Clean` +/// implementation for `AssociatedType` +fn filter_non_trait_generics(trait_did: ast::DefId, mut g: clean::Generics) + -> clean::Generics { + g.where_predicates.retain(|pred| { + match *pred { + clean::WherePredicate::BoundPredicate { + ty: clean::QPath { + self_type: box clean::Generic(ref s), + trait_: box clean::ResolvedPath { did, .. }, + name: ref _name, + }, .. + } => *s != "Self" || did != trait_did, + _ => true, + } + }); + return g; +} + +/// Supertrait bounds for a trait are also listed in the generics coming from +/// the metadata for a crate, so we want to separate those out and create a new +/// list of explicit supertrait bounds to render nicely. +fn separate_supertrait_bounds(mut g: clean::Generics) + -> (clean::Generics, Vec<clean::TyParamBound>) { + let mut ty_bounds = Vec::new(); + g.where_predicates.retain(|pred| { + match *pred { + clean::WherePredicate::BoundPredicate { + ty: clean::Generic(ref s), + ref bounds + } if *s == "Self" => { + ty_bounds.extend(bounds.iter().cloned()); + false + } + _ => true, + } + }); + (g, ty_bounds) +} diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 630dc18dca7..65778114fe0 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -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 { diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 365e34476aa..ae9d9761001 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -502,6 +502,29 @@ impl fmt::Display for clean::Type { } Ok(()) } + // It's pretty unsightly to look at `<A as B>::C` in output, and + // we've got hyperlinking on our side, so try to avoid longer + // notation as much as possible by making `C` a hyperlink to trait + // `B` to disambiguate. + // + // FIXME: this is still a lossy conversion and there should probably + // be a better way of representing this in general? Most of + // the ugliness comes from inlining across crates where + // everything comes in as a fully resolved QPath (hard to + // look at). + clean::QPath { + ref name, + ref self_type, + trait_: box clean::ResolvedPath { did, ref typarams, .. }, + } => { + try!(write!(f, "{}::", self_type)); + let path = clean::Path::singleton(name.clone()); + try!(resolved_path(f, did, &path, false)); + + // FIXME: `typarams` are not rendered, and this seems bad? + drop(typarams); + Ok(()) + } clean::QPath { ref name, ref self_type, ref trait_ } => { write!(f, "<{} as {}>::{}", self_type, trait_, name) } @@ -636,17 +659,7 @@ impl fmt::Display for clean::ViewListIdent { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.source { Some(did) => { - let path = clean::Path { - global: false, - segments: vec!(clean::PathSegment { - name: self.name.clone(), - params: clean::PathParameters::AngleBracketed { - lifetimes: Vec::new(), - types: Vec::new(), - bindings: Vec::new() - } - }) - }; + let path = clean::Path::singleton(self.name.clone()); resolved_path(f, did, &path, false) } _ => write!(f, "{}", self.name), |
