about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-05-22 22:00:18 -0700
committerAlex Crichton <alex@alexcrichton.com>2014-05-25 01:17:14 -0700
commit712118b9c0030762fa8942c0ad52a652875bee02 (patch)
treee167d3d8a24852b4d71187f7aaff8d72eeb18fb7
parent7d76d0ad44e1ec203d235f22eb3514247b8cbfe5 (diff)
downloadrust-712118b9c0030762fa8942c0ad52a652875bee02.tar.gz
rust-712118b9c0030762fa8942c0ad52a652875bee02.zip
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.
-rw-r--r--src/librustdoc/clean.rs131
-rw-r--r--src/librustdoc/html/render.rs62
2 files changed, 140 insertions, 53 deletions
diff --git a/src/librustdoc/clean.rs b/src/librustdoc/clean.rs
index 68af35acdbe..8f98ccc0405 100644
--- a/src/librustdoc/clean.rs
+++ b/src/librustdoc/clean.rs
@@ -15,7 +15,7 @@ use syntax;
 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 @@ impl Clean<Item> for doctree::Module {
             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 @@ impl Clean<TraitMethod> for ty::Method {
             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 @@ impl Clean<TraitMethod> for ty::Method {
             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,
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index 53e271dafa2..76b7a7a2101 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -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 @@ impl DocFolder for Cache {
                     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 @@ impl DocFolder for Cache {
                         });
                     }
                     (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 @@ impl DocFolder for Cache {
             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 @@ impl DocFolder for Cache {
         // 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
                 }