From dd4c7c00d85a82d3ddda1b453ba2ce649a4d1b41 Mon Sep 17 00:00:00 2001 From: Tom Jakubowski Date: Thu, 20 Nov 2014 11:22:09 -0800 Subject: [PATCH 1/3] rustdoc: Render associated types on traits and impls --- src/librustdoc/clean/mod.rs | 24 +++++++- src/librustdoc/html/format.rs | 3 +- src/librustdoc/html/item_type.rs | 2 +- src/librustdoc/html/render.rs | 92 ++++++++++++++++++++--------- src/librustdoc/html/static/main.css | 13 ++-- src/librustdoc/lib.rs | 2 +- 6 files changed, 96 insertions(+), 40 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index f9c509cce1461..3228c32a73393 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -336,7 +336,7 @@ pub enum ItemEnum { ForeignStaticItem(Static), MacroItem(Macro), PrimitiveItem(PrimitiveType), - AssociatedTypeItem, + AssociatedTypeItem(TyParam), } #[deriving(Clone, Encodable, Decodable)] @@ -982,6 +982,8 @@ impl Clean for ast::PolyTraitRef { } } +/// An item belonging to a trait, whether a method or associated. Could be named +/// TraitItem except that's already taken by an exported enum variant. #[deriving(Clone, Encodable, Decodable)] pub enum TraitMethod { RequiredMethod(Item), @@ -1002,6 +1004,12 @@ impl TraitMethod { _ => false, } } + pub fn is_type(&self) -> bool { + match self { + &TypeTraitItem(..) => true, + _ => false, + } + } pub fn item<'a>(&'a self) -> &'a Item { match *self { RequiredMethod(ref item) => item, @@ -2211,7 +2219,7 @@ impl Clean for ast::AssociatedType { source: self.ty_param.span.clean(cx), name: Some(self.ty_param.ident.clean(cx)), attrs: self.attrs.clean(cx), - inner: AssociatedTypeItem, + inner: AssociatedTypeItem(self.ty_param.clean(cx)), visibility: None, def_id: ast_util::local_def(self.ty_param.id), stability: None, @@ -2225,7 +2233,17 @@ impl Clean for ty::AssociatedType { source: DUMMY_SP.clean(cx), name: Some(self.name.clean(cx)), attrs: Vec::new(), - inner: AssociatedTypeItem, + // FIXME(#18048): this is wrong, but cross-crate associated types are broken + // anyway, for the time being. + inner: AssociatedTypeItem(TyParam { + name: self.name.clean(cx), + did: ast::DefId { + krate: 0, + node: ast::DUMMY_NODE_ID + }, + bounds: vec![], + default: None + }), visibility: None, def_id: self.def_id, stability: None, diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 2b521d1da06b3..221caa64ef569 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -46,9 +46,8 @@ pub struct Stability<'a>(pub &'a Option); pub struct ConciseStability<'a>(pub &'a Option); /// Wrapper struct for emitting a where clause from Generics. pub struct WhereClause<'a>(pub &'a clean::Generics); - /// Wrapper struct for emitting type parameter bounds. -struct TyParamBounds<'a>(pub &'a [clean::TyParamBound]); +pub struct TyParamBounds<'a>(pub &'a [clean::TyParamBound]); impl VisSpace { pub fn get(&self) -> Option { diff --git a/src/librustdoc/html/item_type.rs b/src/librustdoc/html/item_type.rs index daa5f155d511e..cb3ad9d063f3a 100644 --- a/src/librustdoc/html/item_type.rs +++ b/src/librustdoc/html/item_type.rs @@ -95,7 +95,7 @@ pub fn shortty(item: &clean::Item) -> ItemType { clean::ForeignStaticItem(..) => ForeignStatic, clean::MacroItem(..) => Macro, clean::PrimitiveItem(..) => Primitive, - clean::AssociatedTypeItem => AssociatedType, + clean::AssociatedTypeItem(..) => AssociatedType, } } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 466af36898e0a..24b5904b6d3b0 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -59,7 +59,7 @@ use clean; use doctree; use fold::DocFolder; use html::format::{VisSpace, Method, FnStyleSpace, MutableSpace, Stability}; -use html::format::{ConciseStability, WhereClause}; +use html::format::{ConciseStability, TyParamBounds, WhereClause}; use html::highlight; use html::item_type::{ItemType, shortty}; use html::item_type; @@ -1685,27 +1685,23 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, t.generics, bounds, WhereClause(&t.generics))); - let required = t.items.iter() - .filter(|m| { - match **m { - clean::RequiredMethod(_) => true, - _ => false, - } - }) - .collect::>(); - let provided = t.items.iter() - .filter(|m| { - match **m { - clean::ProvidedMethod(_) => true, - _ => false, - } - }) - .collect::>(); + + let types = t.items.iter().filter(|m| m.is_type()).collect::>(); + let required = t.items.iter().filter(|m| m.is_req()).collect::>(); + let provided = t.items.iter().filter(|m| m.is_def()).collect::>(); if t.items.len() == 0 { try!(write!(w, "{{ }}")); } else { try!(write!(w, "{{\n")); + for t in types.iter() { + try!(write!(w, " ")); + try!(render_method(w, t.item())); + try!(write!(w, ";\n")); + } + if types.len() > 0 && required.len() > 0 { + try!(w.write("\n".as_bytes())); + } for m in required.iter() { try!(write!(w, " ")); try!(render_method(w, m.item())); @@ -1738,6 +1734,17 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, Ok(()) } + if types.len() > 0 { + try!(write!(w, " +

Associated Types

+
+ ")); + for t in types.iter() { + try!(trait_item(w, *t)); + } + try!(write!(w, "
")); + } + // Output the documentation for each function individually if required.len() > 0 { try!(write!(w, " @@ -1792,7 +1799,7 @@ fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, } fn render_method(w: &mut fmt::Formatter, meth: &clean::Item) -> fmt::Result { - fn fun(w: &mut fmt::Formatter, it: &clean::Item, fn_style: ast::FnStyle, + fn method(w: &mut fmt::Formatter, it: &clean::Item, fn_style: ast::FnStyle, g: &clean::Generics, selfty: &clean::SelfTy, d: &clean::FnDecl) -> fmt::Result { write!(w, "{}fn {name}\ @@ -1807,14 +1814,28 @@ fn render_method(w: &mut fmt::Formatter, meth: &clean::Item) -> fmt::Result { decl = Method(selfty, d), where_clause = WhereClause(g)) } + fn assoc_type(w: &mut fmt::Formatter, it: &clean::Item, + typ: &clean::TyParam) -> fmt::Result { + try!(write!(w, "type {}", it.name.as_ref().unwrap())); + if typ.bounds.len() > 0 { + try!(write!(w, ": {}", TyParamBounds(&*typ.bounds))) + } + if let Some(ref default) = typ.default { + try!(write!(w, " = {}", default)); + } + Ok(()) + } match meth.inner { clean::TyMethodItem(ref m) => { - fun(w, meth, m.fn_style, &m.generics, &m.self_, &m.decl) + method(w, meth, m.fn_style, &m.generics, &m.self_, &m.decl) } clean::MethodItem(ref m) => { - fun(w, meth, m.fn_style, &m.generics, &m.self_, &m.decl) + method(w, meth, m.fn_style, &m.generics, &m.self_, &m.decl) } - _ => unreachable!() + clean::AssociatedTypeItem(ref typ) => { + assoc_type(w, meth, typ) + } + _ => panic!("render_method called on non-method") } } @@ -2071,11 +2092,26 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl) -> fmt::Result { fn doctraititem(w: &mut fmt::Formatter, item: &clean::Item, dox: bool) -> fmt::Result { - try!(write!(w, "

{}", - *item.name.as_ref().unwrap(), - ConciseStability(&item.stability))); - try!(render_method(w, item)); - try!(write!(w, "

\n")); + match item.inner { + clean::MethodItem(..) | clean::TyMethodItem(..) => { + try!(write!(w, "

{}", + *item.name.as_ref().unwrap(), + shortty(item), + ConciseStability(&item.stability))); + try!(render_method(w, item)); + try!(write!(w, "

\n")); + } + clean::TypedefItem(ref tydef) => { + let name = item.name.as_ref().unwrap(); + try!(write!(w, "

{}", + *name, + shortty(item), + ConciseStability(&item.stability))); + try!(write!(w, "type {} = {}", name, tydef.type_)); + try!(write!(w, "

\n")); + } + _ => panic!("can't make docs for trait item with name {}", item.name) + } match item.doc_value() { Some(s) if dox => { try!(write!(w, "
{}
", Markdown(s))); @@ -2085,7 +2121,7 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl) -> fmt::Result { } } - try!(write!(w, "
")); + try!(write!(w, "
")); for trait_item in i.impl_.items.iter() { try!(doctraititem(w, trait_item, true)); } @@ -2107,6 +2143,8 @@ fn render_impl(w: &mut fmt::Formatter, i: &Impl) -> fmt::Result { // If we've implemented a trait, then also emit documentation for all // default methods which weren't overridden in the implementation block. + // FIXME: this also needs to be done for associated types, whenever defaults + // for them work. match i.impl_.trait_ { Some(clean::ResolvedPath { did, .. }) => { try!({ diff --git a/src/librustdoc/html/static/main.css b/src/librustdoc/html/static/main.css index 4c01955039547..dc62273364c20 100644 --- a/src/librustdoc/html/static/main.css +++ b/src/librustdoc/html/static/main.css @@ -84,7 +84,7 @@ h2 { h3 { font-size: 1.3em; } -h1, h2, h3:not(.impl):not(.method), h4:not(.method) { +h1, h2, h3:not(.impl):not(.method):not(.type), h4:not(.method):not(.type) { color: black; font-weight: 500; margin: 20px 0 15px 0; @@ -94,15 +94,15 @@ h1.fqn { border-bottom: 1px dashed #D5D5D5; margin-top: 0; } -h2, h3:not(.impl):not(.method), h4:not(.method) { +h2, h3:not(.impl):not(.method):not(.type), h4:not(.method):not(.type) { border-bottom: 1px solid #DDDDDD; } -h3.impl, h3.method, h4.method { +h3.impl, h3.method, h4.method, h3.type, h4.type { font-weight: 600; margin-top: 10px; margin-bottom: 10px; } -h3.impl, h3.method { +h3.impl, h3.method, h3.type { margin-top: 15px; } h1, h2, h3, h4, section.sidebar, a.source, .search-input, .content table :not(code)>a, .collapse-toggle { @@ -235,6 +235,7 @@ nav.sub { .content .highlighted.fn { background-color: #c6afb3; } .content .highlighted.method { background-color: #c6afb3; } .content .highlighted.tymethod { background-color: #c6afb3; } +.content .highlighted.type { background-color: #c6afb3; } .content .highlighted.ffi { background-color: #c6afb3; } .docblock.short.nowrap { @@ -307,7 +308,7 @@ nav.sub { } .content .methods .docblock { margin-left: 40px; } -.content .impl-methods .docblock { margin-left: 40px; } +.content .impl-items .docblock { margin-left: 40px; } nav { border-bottom: 1px solid #e0e0e0; @@ -442,7 +443,7 @@ h1 .stability { padding: 4px 10px; } -.impl-methods .stability, .methods .stability { +.impl-items .stability, .methods .stability { margin-right: 20px; } diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 204f866e82720..36e74d43e6417 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -16,7 +16,7 @@ #![crate_type = "rlib"] #![allow(unknown_features)] -#![feature(globs, macro_rules, phase, slicing_syntax, tuple_indexing)] +#![feature(globs, if_let, macro_rules, phase, slicing_syntax, tuple_indexing)] extern crate arena; extern crate getopts; From de94f0affb6e8f700ce1e9c67a9572c9f262a5fa Mon Sep 17 00:00:00 2001 From: Tom Jakubowski Date: Thu, 20 Nov 2014 21:45:05 -0800 Subject: [PATCH 2/3] rustdoc: render ast::QPath Fix #18594 --- src/librustdoc/clean/mod.rs | 16 ++++++++++++++++ src/librustdoc/html/format.rs | 3 +++ 2 files changed, 19 insertions(+) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 3228c32a73393..5985516a559f3 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1135,6 +1135,11 @@ pub enum Type { mutability: Mutability, type_: Box, }, + QPath { + name: String, + self_type: Box, + trait_: Box + }, // region, raw, other boxes, mutable } @@ -1260,6 +1265,7 @@ impl Clean for ast::Ty { TyProc(ref c) => Proc(box c.clean(cx)), TyBareFn(ref barefn) => BareFunction(box barefn.clean(cx)), TyParen(ref ty) => ty.clean(cx), + TyQPath(ref qp) => qp.clean(cx), ref x => panic!("Unimplemented type {}", x), } } @@ -1362,6 +1368,16 @@ impl<'tcx> Clean for ty::Ty<'tcx> { } } +impl Clean for ast::QPath { + fn clean(&self, cx: &DocContext) -> Type { + Type::QPath { + name: self.item_name.clean(cx), + self_type: box self.self_type.clean(cx), + trait_: box self.trait_ref.clean(cx) + } + } +} + #[deriving(Clone, Encodable, Decodable)] pub enum StructField { HiddenStructField, // inserted later by strip passes diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 221caa64ef569..43aef11ce5c2f 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -485,6 +485,9 @@ impl fmt::Show for clean::Type { } } } + clean::QPath { ref name, ref self_type, ref trait_ } => { + write!(f, "<{} as {}>::{}", self_type, trait_, name) + } clean::Unique(..) => { panic!("should have been cleaned") } From 59d13820c45ba86da1b45cfb6e41dadaeed2f07d Mon Sep 17 00:00:00 2001 From: Tom Jakubowski Date: Mon, 24 Nov 2014 10:14:46 -0800 Subject: [PATCH 3/3] rustdoc: Render Sized? on traits and generics Both `trait Foo for Sized?` and `` are handled correctly. Fix #18515 --- src/librustdoc/clean/inline.rs | 3 ++- src/librustdoc/clean/mod.rs | 37 ++++++++++++++++++++++++++-------- src/librustdoc/doctree.rs | 1 + src/librustdoc/html/format.rs | 3 +++ src/librustdoc/html/render.rs | 6 ++++++ src/librustdoc/visit_ast.rs | 3 ++- 6 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 16edccd154302..c671e8dcaf808 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -159,11 +159,12 @@ pub fn build_external_trait(cx: &DocContext, tcx: &ty::ctxt, } }); let trait_def = ty::lookup_trait_def(tcx, did); - let bounds = trait_def.bounds.clean(cx); + let (bounds, default_unbound) = trait_def.bounds.clean(cx); clean::Trait { generics: (&def.generics, subst::TypeSpace).clean(cx), items: items.collect(), bounds: bounds, + default_unbound: default_unbound } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 5985516a559f3..db23ec07a84ea 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -464,7 +464,9 @@ pub struct TyParam { pub name: String, pub did: ast::DefId, pub bounds: Vec, - pub default: Option + pub default: Option, + /// An optional default bound on the parameter which is unbound, like `Sized?` + pub default_unbound: Option } impl Clean for ast::TyParam { @@ -473,7 +475,8 @@ impl Clean for ast::TyParam { name: self.ident.clean(cx), did: ast::DefId { krate: ast::LOCAL_CRATE, node: self.id }, bounds: self.bounds.clean(cx), - default: self.default.clean(cx) + default: self.default.clean(cx), + default_unbound: self.unbound.clean(cx) } } } @@ -482,11 +485,13 @@ impl<'tcx> Clean for ty::TypeParameterDef<'tcx> { fn clean(&self, cx: &DocContext) -> TyParam { cx.external_typarams.borrow_mut().as_mut().unwrap() .insert(self.def_id, self.name.clean(cx)); + let (bounds, default_unbound) = self.bounds.clean(cx); TyParam { name: self.name.clean(cx), did: self.def_id, - bounds: self.bounds.clean(cx), - default: self.default.clean(cx) + bounds: bounds, + default: self.default.clean(cx), + default_unbound: default_unbound } } } @@ -588,12 +593,16 @@ impl<'tcx> Clean for ty::TraitRef<'tcx> { } } -impl<'tcx> Clean> for ty::ParamBounds<'tcx> { - fn clean(&self, cx: &DocContext) -> Vec { +// Returns (bounds, default_unbound) +impl<'tcx> Clean<(Vec, Option)> for ty::ParamBounds<'tcx> { + fn clean(&self, cx: &DocContext) -> (Vec, Option) { let mut v = Vec::new(); + let mut has_sized_bound = false; for b in self.builtin_bounds.iter() { if b != ty::BoundSized { v.push(b.clean(cx)); + } else { + has_sized_bound = true; } } for t in self.trait_bounds.iter() { @@ -602,7 +611,15 @@ impl<'tcx> Clean> for ty::ParamBounds<'tcx> { for r in self.region_bounds.iter().filter_map(|r| r.clean(cx)) { v.push(RegionBound(r)); } - return v; + if has_sized_bound { + (v, None) + } else { + let ty = match ty::BoundSized.clean(cx) { + TraitBound(ty) => ty, + _ => unreachable!() + }; + (v, Some(ty)) + } } } @@ -950,6 +967,8 @@ pub struct Trait { pub items: Vec, pub generics: Generics, pub bounds: Vec, + /// An optional default bound not required for `Self`, like `Sized?` + pub default_unbound: Option } impl Clean for doctree::Trait { @@ -965,6 +984,7 @@ impl Clean for doctree::Trait { items: self.items.clean(cx), generics: self.generics.clean(cx), bounds: self.bounds.clean(cx), + default_unbound: self.default_unbound.clean(cx) }), } } @@ -2258,7 +2278,8 @@ impl Clean for ty::AssociatedType { node: ast::DUMMY_NODE_ID }, bounds: vec![], - default: None + default: None, + default_unbound: None }), visibility: None, def_id: self.def_id, diff --git a/src/librustdoc/doctree.rs b/src/librustdoc/doctree.rs index b78ce21eb06e3..adfd9aa821328 100644 --- a/src/librustdoc/doctree.rs +++ b/src/librustdoc/doctree.rs @@ -177,6 +177,7 @@ pub struct Trait { pub whence: Span, pub vis: ast::Visibility, pub stab: Option, + pub default_unbound: Option // FIXME(tomjakubowski) } pub struct Impl { diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 43aef11ce5c2f..4d12701379002 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -94,6 +94,9 @@ impl fmt::Show for clean::Generics { if i > 0 { try!(f.write(", ".as_bytes())) } + if let Some(ref unbound) = tp.default_unbound { + try!(write!(f, "{}? ", unbound)); + }; try!(f.write(tp.name.as_bytes())); if tp.bounds.len() > 0 { diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 24b5904b6d3b0..3fbb2a8749f90 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -1670,7 +1670,13 @@ fn item_function(w: &mut fmt::Formatter, it: &clean::Item, fn item_trait(w: &mut fmt::Formatter, cx: &Context, it: &clean::Item, t: &clean::Trait) -> fmt::Result { let mut bounds = String::new(); + if let Some(ref ty) = t.default_unbound { + bounds.push_str(format!(" for {}?", ty).as_slice()); + } if t.bounds.len() > 0 { + if bounds.len() > 0 { + bounds.push(' '); + } bounds.push_str(": "); for (i, p) in t.bounds.iter().enumerate() { if i > 0 { bounds.push_str(" + "); } diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 8f0f19fe16d0c..b5b34ef6efe6a 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -322,7 +322,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { }; om.constants.push(s); }, - ast::ItemTrait(ref gen, _, ref b, ref items) => { + ast::ItemTrait(ref gen, ref def_ub, ref b, ref items) => { let t = Trait { name: name, items: items.clone(), @@ -333,6 +333,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { whence: item.span, vis: item.vis, stab: self.stability(item.id), + default_unbound: def_ub.clone() }; om.traits.push(t); },