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

@ -1264,7 +1264,8 @@ fn encode_info_for_item(ecx: &EncodeContext,
encode_paren_sugar(rbml_w, trait_def.paren_sugar);
encode_defaulted(rbml_w, ty::trait_has_default_impl(tcx, def_id));
encode_associated_type_names(rbml_w, &trait_def.associated_type_names);
encode_generics(rbml_w, ecx, &trait_def.generics, &trait_predicates, tag_item_generics);
encode_generics(rbml_w, ecx, &trait_def.generics, &trait_predicates,
tag_item_generics);
encode_predicates(rbml_w, ecx, &ty::lookup_super_predicates(tcx, def_id),
tag_item_super_predicates);
encode_trait_ref(rbml_w, ecx, &*trait_def.trait_ref, tag_item_trait_ref);

View File

@ -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)
}

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 {

View File

@ -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, "&lt;{} as {}&gt;::{}", 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),

View File

@ -0,0 +1,38 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
pub trait Deref {
type Target: ?Sized;
fn deref<'a>(&'a self) -> &'a Self::Target;
}
pub trait Add<RHS = Self> {
type Output;
fn add(self, rhs: RHS) -> Self::Output;
}
pub trait Bar {}
pub trait Deref2 {
type Target: Bar;
fn deref(&self) -> Self::Target;
}
pub trait Index<Idx: ?Sized> {
type Output: ?Sized;
fn index(&self, index: Idx) -> &Self::Output;
}
pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
fn index_mut(&mut self, index: Idx) -> &mut Self::Output;
}

View File

@ -0,0 +1,32 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:issue-20727.rs
extern crate issue_20727;
// @has issue_20727_2/trait.Add.html
pub trait Add<RHS = Self> {
// @has - '//*[@class="rust trait"]' 'trait Add<RHS = Self> {'
// @has - '//*[@class="rust trait"]' 'type Output;'
type Output;
// @has - '//*[@class="rust trait"]' 'fn add(self, rhs: RHS) -> Self::Output;'
fn add(self, rhs: RHS) -> Self::Output;
}
// @has issue_20727_2/reexport/trait.Add.html
pub mod reexport {
// @has - '//*[@class="rust trait"]' 'trait Add<RHS = Self> {'
// @has - '//*[@class="rust trait"]' 'type Output;'
// @has - '//*[@class="rust trait"]' 'fn add(self, rhs: RHS) -> Self::Output;'
pub use issue_20727::Add;
}

View File

@ -0,0 +1,33 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:issue-20727.rs
extern crate issue_20727;
pub trait Bar {}
// @has issue_20727_3/trait.Deref2.html
pub trait Deref2 {
// @has - '//*[@class="rust trait"]' 'trait Deref2 {'
// @has - '//*[@class="rust trait"]' 'type Target: Bar;'
type Target: Bar;
// @has - '//*[@class="rust trait"]' 'fn deref(&self) -> Self::Target;'
fn deref(&self) -> Self::Target;
}
// @has issue_20727_3/reexport/trait.Deref2.html
pub mod reexport {
// @has - '//*[@class="rust trait"]' 'trait Deref2 {'
// @has - '//*[@class="rust trait"]' 'type Target: Bar;'
// @has - '//*[@class="rust trait"]' 'fn deref(&self) -> Self::Target;'
pub use issue_20727::Deref2;
}

View File

@ -0,0 +1,49 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:issue-20727.rs
extern crate issue_20727;
// @has issue_20727_4/trait.Index.html
pub trait Index<Idx: ?Sized> {
// @has - '//*[@class="rust trait"]' 'trait Index<Idx: ?Sized> {'
// @has - '//*[@class="rust trait"]' 'type Output: ?Sized'
type Output: ?Sized;
// @has - '//*[@class="rust trait"]' \
// 'fn index(&self, index: Idx) -> &Self::Output'
fn index(&self, index: Idx) -> &Self::Output;
}
// @has issue_20727_4/trait.IndexMut.html
pub trait IndexMut<Idx: ?Sized>: Index<Idx> {
// @has - '//*[@class="rust trait"]' \
// 'trait IndexMut<Idx: ?Sized>: Index<Idx> {'
// @has - '//*[@class="rust trait"]' \
// 'fn index_mut(&mut self, index: Idx) -> &mut Self::Output;'
fn index_mut(&mut self, index: Idx) -> &mut Self::Output;
}
pub mod reexport {
// @has issue_20727_4/reexport/trait.Index.html
// @has - '//*[@class="rust trait"]' 'trait Index<Idx> where Idx: ?Sized {'
// @has - '//*[@class="rust trait"]' 'type Output: ?Sized'
// @has - '//*[@class="rust trait"]' \
// 'fn index(&self, index: Idx) -> &Self::Output'
pub use issue_20727::Index;
// @has issue_20727_4/reexport/trait.IndexMut.html
// @has - '//*[@class="rust trait"]' \
// 'trait IndexMut<Idx>: Index<Idx> where Idx: ?Sized {'
// @has - '//*[@class="rust trait"]' \
// 'fn index_mut(&mut self, index: Idx) -> &mut Self::Output;'
pub use issue_20727::IndexMut;
}

View File

@ -0,0 +1,33 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:issue-20727.rs
extern crate issue_20727;
// @has issue_20727/trait.Deref.html
pub trait Deref {
// @has - '//*[@class="rust trait"]' 'trait Deref {'
// @has - '//*[@class="rust trait"]' 'type Target: ?Sized;'
type Target: ?Sized;
// @has - '//*[@class="rust trait"]' \
// "fn deref<'a>(&'a self) -> &'a Self::Target;"
fn deref<'a>(&'a self) -> &'a Self::Target;
}
// @has issue_20727/reexport/trait.Deref.html
pub mod reexport {
// @has - '//*[@class="rust trait"]' 'trait Deref {'
// @has - '//*[@class="rust trait"]' 'type Target: ?Sized;'
// @has - '//*[@class="rust trait"]' \
// "fn deref(&'a self) -> &'a Self::Target;"
pub use issue_20727::Deref;
}