Skip to content

Commit 75ef083

Browse files
committed
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
1 parent 11f26f9 commit 75ef083

File tree

9 files changed

+382
-69
lines changed

9 files changed

+382
-69
lines changed

src/librustc/metadata/encoder.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1264,7 +1264,8 @@ fn encode_info_for_item(ecx: &EncodeContext,
12641264
encode_paren_sugar(rbml_w, trait_def.paren_sugar);
12651265
encode_defaulted(rbml_w, ty::trait_has_default_impl(tcx, def_id));
12661266
encode_associated_type_names(rbml_w, &trait_def.associated_type_names);
1267-
encode_generics(rbml_w, ecx, &trait_def.generics, &trait_predicates, tag_item_generics);
1267+
encode_generics(rbml_w, ecx, &trait_def.generics, &trait_predicates,
1268+
tag_item_generics);
12681269
encode_predicates(rbml_w, ecx, &ty::lookup_super_predicates(tcx, def_id),
12691270
tag_item_super_predicates);
12701271
encode_trait_ref(rbml_w, ecx, &*trait_def.trait_ref, tag_item_trait_ref);

src/librustdoc/clean/inline.rs

+50-2
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,14 @@ pub fn build_external_trait(cx: &DocContext, tcx: &ty::ctxt,
150150
let def = ty::lookup_trait_def(tcx, did);
151151
let trait_items = ty::trait_items(tcx, did).clean(cx);
152152
let predicates = ty::lookup_predicates(tcx, did);
153+
let generics = (&def.generics, &predicates, subst::TypeSpace).clean(cx);
154+
let generics = filter_non_trait_generics(did, generics);
155+
let (generics, supertrait_bounds) = separate_supertrait_bounds(generics);
153156
clean::Trait {
154157
unsafety: def.unsafety,
155-
generics: (&def.generics, &predicates, subst::TypeSpace).clean(cx),
158+
generics: generics,
156159
items: trait_items,
157-
bounds: vec![], // supertraits can be found in the list of predicates
160+
bounds: supertrait_bounds,
158161
}
159162
}
160163

@@ -447,3 +450,48 @@ fn build_static(cx: &DocContext, tcx: &ty::ctxt,
447450
expr: "\n\n\n".to_string(), // trigger the "[definition]" links
448451
}
449452
}
453+
454+
/// A trait's generics clause actually contains all of the predicates for all of
455+
/// its associated types as well. We specifically move these clauses to the
456+
/// associated types instead when displaying, so when we're genering the
457+
/// generics for the trait itself we need to be sure to remove them.
458+
///
459+
/// The inverse of this filtering logic can be found in the `Clean`
460+
/// implementation for `AssociatedType`
461+
fn filter_non_trait_generics(trait_did: ast::DefId, mut g: clean::Generics)
462+
-> clean::Generics {
463+
g.where_predicates.retain(|pred| {
464+
match *pred {
465+
clean::WherePredicate::BoundPredicate {
466+
ty: clean::QPath {
467+
self_type: box clean::Generic(ref s),
468+
trait_: box clean::ResolvedPath { did, .. },
469+
name: ref _name,
470+
}, ..
471+
} => *s != "Self" || did != trait_did,
472+
_ => true,
473+
}
474+
});
475+
return g;
476+
}
477+
478+
/// Supertrait bounds for a trait are also listed in the generics coming from
479+
/// the metadata for a crate, so we want to separate those out and create a new
480+
/// list of explicit supertrait bounds to render nicely.
481+
fn separate_supertrait_bounds(mut g: clean::Generics)
482+
-> (clean::Generics, Vec<clean::TyParamBound>) {
483+
let mut ty_bounds = Vec::new();
484+
g.where_predicates.retain(|pred| {
485+
match *pred {
486+
clean::WherePredicate::BoundPredicate {
487+
ty: clean::Generic(ref s),
488+
ref bounds
489+
} if *s == "Self" => {
490+
ty_bounds.extend(bounds.iter().cloned());
491+
false
492+
}
493+
_ => true,
494+
}
495+
});
496+
(g, ty_bounds)
497+
}

src/librustdoc/clean/mod.rs

+121-55
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,35 @@ pub enum TyParamBound {
498498
TraitBound(PolyTrait, ast::TraitBoundModifier)
499499
}
500500

501+
impl TyParamBound {
502+
fn maybe_sized(cx: &DocContext) -> TyParamBound {
503+
use syntax::ast::TraitBoundModifier as TBM;
504+
let mut sized_bound = ty::BuiltinBound::BoundSized.clean(cx);
505+
if let TyParamBound::TraitBound(_, ref mut tbm) = sized_bound {
506+
*tbm = TBM::Maybe
507+
};
508+
sized_bound
509+
}
510+
511+
fn is_sized_bound(&self, cx: &DocContext) -> bool {
512+
use syntax::ast::TraitBoundModifier as TBM;
513+
if let Some(tcx) = cx.tcx_opt() {
514+
let sized_did = match tcx.lang_items.sized_trait() {
515+
Some(did) => did,
516+
None => return false
517+
};
518+
if let TyParamBound::TraitBound(PolyTrait {
519+
trait_: Type::ResolvedPath { did, .. }, ..
520+
}, TBM::None) = *self {
521+
if did == sized_did {
522+
return true
523+
}
524+
}
525+
}
526+
false
527+
}
528+
}
529+
501530
impl Clean<TyParamBound> for ast::TyParamBound {
502531
fn clean(&self, cx: &DocContext) -> TyParamBound {
503532
match *self {
@@ -835,7 +864,9 @@ impl<'tcx> Clean<Type> for ty::ProjectionTy<'tcx> {
835864
fn clean(&self, cx: &DocContext) -> Type {
836865
let trait_ = match self.trait_ref.clean(cx) {
837866
TyParamBound::TraitBound(t, _) => t.trait_,
838-
TyParamBound::RegionBound(_) => panic!("cleaning a trait got a region??"),
867+
TyParamBound::RegionBound(_) => {
868+
panic!("cleaning a trait got a region")
869+
}
839870
};
840871
Type::QPath {
841872
name: self.item_name.clean(cx),
@@ -868,28 +899,8 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics<'tcx>,
868899
subst::ParamSpace) {
869900
fn clean(&self, cx: &DocContext) -> Generics {
870901
use std::collections::HashSet;
871-
use syntax::ast::TraitBoundModifier as TBM;
872902
use self::WherePredicate as WP;
873903

874-
fn has_sized_bound(bounds: &[TyParamBound], cx: &DocContext) -> bool {
875-
if let Some(tcx) = cx.tcx_opt() {
876-
let sized_did = match tcx.lang_items.sized_trait() {
877-
Some(did) => did,
878-
None => return false
879-
};
880-
for bound in bounds {
881-
if let TyParamBound::TraitBound(PolyTrait {
882-
trait_: Type::ResolvedPath { did, .. }, ..
883-
}, TBM::None) = *bound {
884-
if did == sized_did {
885-
return true
886-
}
887-
}
888-
}
889-
}
890-
false
891-
}
892-
893904
let (gens, preds, space) = *self;
894905

895906
// 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>,
904915
srp.clean(cx)
905916
}).collect::<Vec<_>>();
906917

907-
let where_predicates = preds.predicates.get_slice(space)
908-
.to_vec().clean(cx);
918+
let mut where_predicates = preds.predicates.get_slice(space)
919+
.to_vec().clean(cx);
909920

910-
// Type parameters have a Sized bound by default unless removed with
921+
// Type parameters and have a Sized bound by default unless removed with
911922
// ?Sized. Scan through the predicates and mark any type parameter with
912923
// a Sized bound, removing the bounds as we find them.
924+
//
925+
// Note that associated types also have a sized bound by default, but we
926+
// don't actually konw the set of associated types right here so that's
927+
// handled in cleaning associated types
913928
let mut sized_params = HashSet::new();
914-
let mut where_predicates = where_predicates.into_iter().filter_map(|pred| {
915-
if let WP::BoundPredicate { ty: Type::Generic(ref g), ref bounds } = pred {
916-
if has_sized_bound(&**bounds, cx) {
917-
sized_params.insert(g.clone());
918-
return None
929+
where_predicates.retain(|pred| {
930+
match *pred {
931+
WP::BoundPredicate { ty: Generic(ref g), ref bounds } => {
932+
if bounds.iter().any(|b| b.is_sized_bound(cx)) {
933+
sized_params.insert(g.clone());
934+
false
935+
} else {
936+
true
937+
}
919938
}
939+
_ => true,
920940
}
921-
Some(pred)
922-
}).collect::<Vec<_>>();
941+
});
923942

924-
// Finally, run through the type parameters again and insert a ?Sized
943+
// Run through the type parameters again and insert a ?Sized
925944
// unbound for any we didn't find to be Sized.
926945
for tp in &stripped_typarams {
927946
if !sized_params.contains(&tp.name) {
928-
let mut sized_bound = ty::BuiltinBound::BoundSized.clean(cx);
929-
if let TyParamBound::TraitBound(_, ref mut tbm) = sized_bound {
930-
*tbm = TBM::Maybe
931-
};
932947
where_predicates.push(WP::BoundPredicate {
933948
ty: Type::Generic(tp.name.clone()),
934-
bounds: vec![sized_bound]
949+
bounds: vec![TyParamBound::maybe_sized(cx)],
935950
})
936951
}
937952
}
@@ -1597,17 +1612,7 @@ impl<'tcx> Clean<Type> for ty::Ty<'tcx> {
15971612
}
15981613
ty::ty_tup(ref t) => Tuple(t.clean(cx)),
15991614

1600-
ty::ty_projection(ref data) => {
1601-
let trait_ref = match data.trait_ref.clean(cx) {
1602-
TyParamBound::TraitBound(t, _) => t.trait_,
1603-
TyParamBound::RegionBound(_) => panic!("cleaning a trait got a region??"),
1604-
};
1605-
Type::QPath {
1606-
name: data.item_name.clean(cx),
1607-
self_type: box data.trait_ref.self_ty().clean(cx),
1608-
trait_: box trait_ref,
1609-
}
1610-
}
1615+
ty::ty_projection(ref data) => data.clean(cx),
16111616

16121617
ty::ty_param(ref p) => Generic(token::get_name(p.name).to_string()),
16131618

@@ -1881,6 +1886,22 @@ pub struct Path {
18811886
pub segments: Vec<PathSegment>,
18821887
}
18831888

1889+
impl Path {
1890+
pub fn singleton(name: String) -> Path {
1891+
Path {
1892+
global: false,
1893+
segments: vec![PathSegment {
1894+
name: name,
1895+
params: PathParameters::AngleBracketed {
1896+
lifetimes: Vec::new(),
1897+
types: Vec::new(),
1898+
bindings: Vec::new()
1899+
}
1900+
}]
1901+
}
1902+
}
1903+
}
1904+
18841905
impl Clean<Path> for ast::Path {
18851906
fn clean(&self, cx: &DocContext) -> Path {
18861907
Path {
@@ -2516,21 +2537,66 @@ impl Clean<Stability> for attr::Stability {
25162537

25172538
impl Clean<Item> for ty::AssociatedType {
25182539
fn clean(&self, cx: &DocContext) -> Item {
2540+
// When loading a cross-crate associated type, the bounds for this type
2541+
// are actually located on the trait/impl itself, so we need to load
2542+
// all of the generics from there and then look for bounds that are
2543+
// applied to this associated type in question.
2544+
let predicates = ty::lookup_predicates(cx.tcx(), self.container.id());
2545+
let generics = match self.container {
2546+
ty::TraitContainer(did) => {
2547+
let def = ty::lookup_trait_def(cx.tcx(), did);
2548+
(&def.generics, &predicates, subst::TypeSpace).clean(cx)
2549+
}
2550+
ty::ImplContainer(did) => {
2551+
let ty = ty::lookup_item_type(cx.tcx(), did);
2552+
(&ty.generics, &predicates, subst::TypeSpace).clean(cx)
2553+
}
2554+
};
2555+
let my_name = self.name.clean(cx);
2556+
let mut bounds = generics.where_predicates.iter().filter_map(|pred| {
2557+
let (name, self_type, trait_, bounds) = match *pred {
2558+
WherePredicate::BoundPredicate {
2559+
ty: QPath { ref name, ref self_type, ref trait_ },
2560+
ref bounds
2561+
} => (name, self_type, trait_, bounds),
2562+
_ => return None,
2563+
};
2564+
if *name != my_name { return None }
2565+
match **trait_ {
2566+
ResolvedPath { did, .. } if did == self.container.id() => {}
2567+
_ => return None,
2568+
}
2569+
match **self_type {
2570+
Generic(ref s) if *s == "Self" => {}
2571+
_ => return None,
2572+
}
2573+
Some(bounds)
2574+
}).flat_map(|i| i.iter().cloned()).collect::<Vec<_>>();
2575+
2576+
// Our Sized/?Sized bound didn't get handled when creating the generics
2577+
// because we didn't actually get our whole set of bounds until just now
2578+
// (some of them may have come from the trait). If we do have a sized
2579+
// bound, we remove it, and if we don't then we add the `?Sized` bound
2580+
// at the end.
2581+
match bounds.iter().position(|b| b.is_sized_bound(cx)) {
2582+
Some(i) => { bounds.remove(i); }
2583+
None => bounds.push(TyParamBound::maybe_sized(cx)),
2584+
}
2585+
25192586
Item {
25202587
source: DUMMY_SP.clean(cx),
25212588
name: Some(self.name.clean(cx)),
2522-
attrs: Vec::new(),
2523-
// FIXME(#20727): bounds are missing and need to be filled in from the
2524-
// predicates on the trait itself
2525-
inner: AssociatedTypeItem(vec![], None),
2526-
visibility: None,
2589+
attrs: inline::load_attrs(cx, cx.tcx(), self.def_id),
2590+
inner: AssociatedTypeItem(bounds, None),
2591+
visibility: self.vis.clean(cx),
25272592
def_id: self.def_id,
2528-
stability: None,
2593+
stability: stability::lookup(cx.tcx(), self.def_id).clean(cx),
25292594
}
25302595
}
25312596
}
25322597

2533-
impl<'a> Clean<Typedef> for (ty::TypeScheme<'a>, ty::GenericPredicates<'a>, ParamSpace) {
2598+
impl<'a> Clean<Typedef> for (ty::TypeScheme<'a>, ty::GenericPredicates<'a>,
2599+
ParamSpace) {
25342600
fn clean(&self, cx: &DocContext) -> Typedef {
25352601
let (ref ty_scheme, ref predicates, ps) = *self;
25362602
Typedef {

src/librustdoc/html/format.rs

+24-11
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,29 @@ impl fmt::Display for clean::Type {
502502
}
503503
Ok(())
504504
}
505+
// It's pretty unsightly to look at `<A as B>::C` in output, and
506+
// we've got hyperlinking on our side, so try to avoid longer
507+
// notation as much as possible by making `C` a hyperlink to trait
508+
// `B` to disambiguate.
509+
//
510+
// FIXME: this is still a lossy conversion and there should probably
511+
// be a better way of representing this in general? Most of
512+
// the ugliness comes from inlining across crates where
513+
// everything comes in as a fully resolved QPath (hard to
514+
// look at).
515+
clean::QPath {
516+
ref name,
517+
ref self_type,
518+
trait_: box clean::ResolvedPath { did, ref typarams, .. },
519+
} => {
520+
try!(write!(f, "{}::", self_type));
521+
let path = clean::Path::singleton(name.clone());
522+
try!(resolved_path(f, did, &path, false));
523+
524+
// FIXME: `typarams` are not rendered, and this seems bad?
525+
drop(typarams);
526+
Ok(())
527+
}
505528
clean::QPath { ref name, ref self_type, ref trait_ } => {
506529
write!(f, "&lt;{} as {}&gt;::{}", self_type, trait_, name)
507530
}
@@ -636,17 +659,7 @@ impl fmt::Display for clean::ViewListIdent {
636659
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
637660
match self.source {
638661
Some(did) => {
639-
let path = clean::Path {
640-
global: false,
641-
segments: vec!(clean::PathSegment {
642-
name: self.name.clone(),
643-
params: clean::PathParameters::AngleBracketed {
644-
lifetimes: Vec::new(),
645-
types: Vec::new(),
646-
bindings: Vec::new()
647-
}
648-
})
649-
};
662+
let path = clean::Path::singleton(self.name.clone());
650663
resolved_path(f, did, &path, false)
651664
}
652665
_ => write!(f, "{}", self.name),

0 commit comments

Comments
 (0)