提交 712118b9 编写于 作者: A Alex Crichton

rustdoc: Inline documentation of `pub use`

This commit teaches rustdoc to inline the documentation for the destination of a
`pub use` statement across crate boundaries. This is intended for the standard
library's facade to show the fact that the facade is just an implementation
detail rather than the api of the standard library itself.

This starts out by inlining traits and functions, but more items will come soon.
The current drawback of this system is that hyperlinks across crates sill go to
the original item's definition rather than the reexported location.
上级 7d76d0ad
......@@ -15,7 +15,7 @@
use syntax::ast;
use syntax::ast_util;
use syntax::attr;
use syntax::attr::AttributeMethods;
use syntax::attr::{AttributeMethods, AttrMetaMethods};
use syntax::codemap::Pos;
use syntax::parse::token::InternedString;
use syntax::parse::token;
......@@ -250,7 +250,8 @@ fn clean(&self) -> Item {
self.statics.clean().move_iter().collect(),
self.traits.clean().move_iter().collect(),
self.impls.clean().move_iter().collect(),
self.view_items.clean().move_iter().collect(),
self.view_items.clean().move_iter()
.flat_map(|s| s.move_iter()).collect(),
self.macros.clean().move_iter().collect()
);
......@@ -832,10 +833,6 @@ fn clean(&self) -> TraitMethod {
core::Typed(ref tcx) => tcx,
core::NotTyped(_) => fail!(),
};
let mut attrs = Vec::new();
csearch::get_item_attrs(&tcx.sess.cstore, self.def_id, |v| {
attrs.extend(v.move_iter().map(|i| i.clean()));
});
let (self_, sig) = match self.explicit_self {
ast::SelfStatic => (ast::SelfStatic.clean(), self.fty.sig.clone()),
s => {
......@@ -861,7 +858,7 @@ fn clean(&self) -> TraitMethod {
name: Some(self.ident.clean()),
visibility: Some(ast::Inherited),
def_id: self.def_id,
attrs: attrs,
attrs: load_attrs(tcx, self.def_id),
source: Span {
filename: "".to_strbuf(),
loline: 0, locol: 0, hiline: 0, hicol: 0,
......@@ -1404,21 +1401,105 @@ pub struct ViewItem {
pub inner: ViewItemInner,
}
impl Clean<Item> for ast::ViewItem {
fn clean(&self) -> Item {
Item {
name: None,
attrs: self.attrs.clean().move_iter().collect(),
source: self.span.clean(),
def_id: ast_util::local_def(0),
visibility: self.vis.clean(),
inner: ViewItemItem(ViewItem {
inner: self.node.clean()
}),
impl Clean<Vec<Item>> for ast::ViewItem {
fn clean(&self) -> Vec<Item> {
let denied = self.vis != ast::Public || self.attrs.iter().any(|a| {
a.name().get() == "doc" && match a.meta_item_list() {
Some(l) => attr::contains_name(l, "noinline"),
None => false,
}
});
let convert = |node: &ast::ViewItem_| {
Item {
name: None,
attrs: self.attrs.clean().move_iter().collect(),
source: self.span.clean(),
def_id: ast_util::local_def(0),
visibility: self.vis.clean(),
inner: ViewItemItem(ViewItem { inner: node.clean() }),
}
};
let mut ret = Vec::new();
match self.node {
ast::ViewItemUse(ref path) if !denied => {
match path.node {
ast::ViewPathGlob(..) => ret.push(convert(&self.node)),
ast::ViewPathList(ref a, ref list, ref b) => {
let remaining = list.iter().filter(|path| {
match try_inline(path.node.id) {
Some(item) => { ret.push(item); false }
None => true,
}
}).map(|a| a.clone()).collect::<Vec<ast::PathListIdent>>();
if remaining.len() > 0 {
let path = ast::ViewPathList(a.clone(),
remaining,
b.clone());
let path = syntax::codemap::dummy_spanned(path);
ret.push(convert(&ast::ViewItemUse(@path)));
}
}
ast::ViewPathSimple(_, _, id) => {
match try_inline(id) {
Some(item) => ret.push(item),
None => ret.push(convert(&self.node)),
}
}
}
}
ref n => ret.push(convert(n)),
}
return ret;
}
}
fn try_inline(id: ast::NodeId) -> Option<Item> {
let cx = super::ctxtkey.get().unwrap();
let tcx = match cx.maybe_typed {
core::Typed(ref tycx) => tycx,
core::NotTyped(_) => return None,
};
let def = match tcx.def_map.borrow().find(&id) {
Some(def) => *def,
None => return None,
};
let did = ast_util::def_id_of_def(def);
if ast_util::is_local(did) { return None }
let inner = match def {
ast::DefTrait(did) => TraitItem(build_external_trait(tcx, did)),
ast::DefFn(did, style) =>
FunctionItem(build_external_function(tcx, did, style)),
_ => return None,
};
let fqn = csearch::get_item_path(tcx, did);
Some(Item {
source: Span {
filename: "".to_strbuf(), loline: 0, locol: 0, hiline: 0, hicol: 0,
},
name: Some(fqn.last().unwrap().to_str().to_strbuf()),
attrs: load_attrs(tcx, did),
inner: inner,
visibility: Some(ast::Public),
def_id: did,
})
}
fn load_attrs(tcx: &ty::ctxt, did: ast::DefId) -> Vec<Attribute> {
let mut attrs = Vec::new();
csearch::get_item_attrs(&tcx.sess.cstore, did, |v| {
attrs.extend(v.move_iter().map(|item| {
let mut a = attr::mk_attr_outer(item);
// FIXME this isn't quite always true, it's just true about 99% of
// the time when dealing with documentation
if a.name().get() == "doc" && a.value_str().is_some() {
a.node.is_sugared_doc = true;
}
a.clean()
}));
});
attrs
}
#[deriving(Clone, Encodable, Decodable)]
pub enum ViewItemInner {
ExternCrate(String, Option<String>, ast::NodeId),
......@@ -1654,6 +1735,20 @@ fn build_external_trait(tcx: &ty::ctxt, did: ast::DefId) -> Trait {
}
}
fn build_external_function(tcx: &ty::ctxt,
did: ast::DefId,
style: ast::FnStyle) -> Function {
let t = csearch::get_type(tcx, did);
Function {
decl: match ty::get(t.ty).sty {
ty::ty_bare_fn(ref f) => f.sig.clean(),
_ => fail!("bad function"),
},
generics: t.generics.clean(),
fn_style: style,
}
}
fn resolve_use_source(path: Path, id: ast::NodeId) -> ImportSource {
ImportSource {
path: path,
......
......@@ -157,9 +157,9 @@ pub struct Cache {
// Private fields only used when initially crawling a crate to build a cache
stack: Vec<String> ,
parent_stack: Vec<ast::NodeId> ,
search_index: Vec<IndexItem> ,
stack: Vec<String>,
parent_stack: Vec<ast::DefId>,
search_index: Vec<IndexItem>,
privmod: bool,
public_items: NodeSet,
......@@ -198,7 +198,7 @@ struct IndexItem {
name: String,
path: String,
desc: String,
parent: Option<ast::NodeId>,
parent: Option<ast::DefId>,
}
// TLS keys used to carry information around during rendering.
......@@ -302,7 +302,7 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
path: fqp.slice_to(fqp.len() - 1).connect("::")
.to_strbuf(),
desc: shorter(item.doc_value()).to_strbuf(),
parent: Some(pid),
parent: Some(did),
});
},
None => {}
......@@ -360,9 +360,8 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> {
try!(write!(&mut w, r#"],"paths":["#));
for (i, &nodeid) in pathid_to_nodeid.iter().enumerate() {
let def = ast_util::local_def(nodeid);
let &(ref fqp, short) = cache.paths.find(&def).unwrap();
for (i, &did) in pathid_to_nodeid.iter().enumerate() {
let &(ref fqp, short) = cache.paths.find(&did).unwrap();
if i > 0 {
try!(write!(&mut w, ","));
}
......@@ -730,14 +729,13 @@ fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
clean::VariantItem(..) => {
(Some(*self.parent_stack.last().unwrap()),
Some(self.stack.slice_to(self.stack.len() - 1)))
}
clean::MethodItem(..) => {
if self.parent_stack.len() == 0 {
(None, None)
} else {
let last = self.parent_stack.last().unwrap();
let did = ast_util::local_def(*last);
let did = *last;
let path = match self.paths.find(&did) {
Some(&(_, item_type::Trait)) =>
Some(self.stack.slice_to(self.stack.len() - 1)),
......@@ -766,9 +764,11 @@ fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
});
}
(Some(parent), None) if !self.privmod => {
// We have a parent, but we don't know where they're
// defined yet. Wait for later to index this item.
self.orphan_methods.push((parent, item.clone()))
if ast_util::is_local(parent) {
// We have a parent, but we don't know where they're
// defined yet. Wait for later to index this item.
self.orphan_methods.push((parent.node, item.clone()))
}
}
_ => {}
}
......@@ -789,19 +789,17 @@ fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
clean::TypedefItem(..) | clean::TraitItem(..) |
clean::FunctionItem(..) | clean::ModuleItem(..) |
clean::ForeignFunctionItem(..) => {
if ast_util::is_local(item.def_id) {
// Reexported items mean that the same id can show up twice
// in the rustdoc ast that we're looking at. We know,
// however, that a reexported item doesn't show up in the
// `public_items` map, so we can skip inserting into the
// paths map if there was already an entry present and we're
// not a public item.
let id = item.def_id.node;
if !self.paths.contains_key(&item.def_id) ||
self.public_items.contains(&id) {
self.paths.insert(item.def_id,
(self.stack.clone(), shortty(&item)));
}
// Reexported items mean that the same id can show up twice
// in the rustdoc ast that we're looking at. We know,
// however, that a reexported item doesn't show up in the
// `public_items` map, so we can skip inserting into the
// paths map if there was already an entry present and we're
// not a public item.
let id = item.def_id.node;
if !self.paths.contains_key(&item.def_id) ||
self.public_items.contains(&id) {
self.paths.insert(item.def_id,
(self.stack.clone(), shortty(&item)));
}
}
// link variants to their parent enum because pages aren't emitted
......@@ -817,20 +815,14 @@ fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
// Maintain the parent stack
let parent_pushed = match item.inner {
clean::TraitItem(..) | clean::EnumItem(..) | clean::StructItem(..) => {
if ast_util::is_local(item.def_id) {
self.parent_stack.push(item.def_id.node);
}
self.parent_stack.push(item.def_id);
true
}
clean::ImplItem(ref i) => {
match i.for_ {
clean::ResolvedPath{ did, .. } => {
if ast_util::is_local(did) {
self.parent_stack.push(did.node);
true
} else {
false
}
self.parent_stack.push(did);
true
}
_ => false
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册