From dd4c7c00d85a82d3ddda1b453ba2ce649a4d1b41 Mon Sep 17 00:00:00 2001 From: Tom Jakubowski Date: Thu, 20 Nov 2014 11:22:09 -0800 Subject: [PATCH 1/2] 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/2] 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") }