diff --git a/src/doc/guide-unsafe.md b/src/doc/guide-unsafe.md index a85e889ed486ddda1ce5a9a0048ef00d0a3a55f9..4e1a96710be84b044834a51305331e9f199975f5 100644 --- a/src/doc/guide-unsafe.md +++ b/src/doc/guide-unsafe.md @@ -511,7 +511,7 @@ function is never called. With the above techniques, we've got a bare-metal executable running some Rust code. There is a good deal of functionality provided by the standard library, however, that is necessary to be productive in Rust. If the standard library is -not sufficient, then [libcore](../core/index.html) is designed to be used +not sufficient, then [libcore](core/index.html) is designed to be used instead. The core library has very few dependencies and is much more portable than the diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 18cf928f04ce593b829649f0f6e22421a1a93f1f..22aab89ef6279c1841223523413f4310fd658cd5 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -104,12 +104,27 @@ fn clean(&self) -> Crate { let id = link::find_crate_id(self.attrs.as_slice(), t_outputs.out_filestem.as_slice()); - // Clean the module, translating the entire libsyntax AST to one that is + // Clean the crate, translating the entire libsyntax AST to one that is // understood by rustdoc. let mut module = self.module.clean(); // Collect all inner modules which are tagged as implementations of // primitives. + // + // Note that this loop only searches the top-level items of the crate, + // and this is intentional. If we were to search the entire crate for an + // item tagged with `#[doc(primitive)]` then we we would also have to + // search the entirety of external modules for items tagged + // `#[doc(primitive)]`, which is a pretty inefficient process (decoding + // all that metadata unconditionally). + // + // In order to keep the metadata load under control, the + // `#[doc(primitive)]` feature is explicitly designed to only allow the + // primitive tags to show up as the top level items in a crate. + // + // Also note that this does not attempt to deal with modules tagged + // duplicately for the same primitive. This is handled later on when + // rendering by delegating everything to a hash map. let mut primitives = Vec::new(); { let m = match module.inner { diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 54900ab0ab8416741a2dedcfa24385fc14c23cd0..41d84deea6f4105661c96cdf66cfb742ef8064a7 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -424,14 +424,8 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { decl.decl) } clean::Tuple(ref typs) => { - try!(f.write("(".as_bytes())); - for (i, typ) in typs.iter().enumerate() { - if i > 0 { - try!(f.write(", ".as_bytes())) - } - try!(write!(f, "{}", *typ)); - } - f.write(")".as_bytes()) + primitive_link(f, clean::PrimitiveTuple, + format!("({:#})", typs).as_slice()) } clean::Vector(ref t) => { primitive_link(f, clean::Slice, format!("[{}]", **t).as_slice()) diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index f4a8541c6d22b6af404552af21e138c2c193bf4c..80653878247fad444f20c419a7db4d955731c736 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -130,3 +130,17 @@ pub fn render( fn nonestr<'a>(s: &'a str) -> &'a str { if s == "" { "none" } else { s } } + +pub fn redirect(dst: &mut io::Writer, url: &str) -> io::IoResult<()> { + write!(dst, +r##" + + + + + + +"##, + url = url, + ) +} diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index fa93a0261070efb11e5d79fe014d263de2ab46b6..16becde164ff3e2a3dd27fd16517fbdc83e1c596 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -70,7 +70,7 @@ pub struct Context { /// Current hierarchy of components leading down to what's currently being /// rendered - pub current: Vec , + pub current: Vec, /// String representation of how to get back to the root path of the 'doc/' /// folder in terms of a relative URL. pub root_path: String, @@ -90,6 +90,10 @@ pub struct Context { /// the source files are present in the html rendering, then this will be /// `true`. pub include_sources: bool, + /// A flag, which when turned off, will render pages which redirect to the + /// real location of an item. This is used to allow external links to + /// publicly reused items to redirect to the right location. + pub render_redirect_pages: bool, } /// Indicates where an external crate can be found. @@ -227,6 +231,7 @@ pub fn run(mut krate: clean::Crate, dst: Path) -> io::IoResult<()> { krate: krate.name.clone(), }, include_sources: true, + render_redirect_pages: false, }; try!(mkdir(&cx.dst)); @@ -493,7 +498,17 @@ fn collect(path: &Path, krate: &str, let dst = cx.dst.join("implementors"); try!(mkdir(&dst)); for (&did, imps) in cache.implementors.iter() { - let &(ref remote_path, remote_item_type) = cache.paths.get(&did); + // Private modules can leak through to this phase of rustdoc, which + // could contain implementations for otherwise private types. In some + // rare cases we could find an implementation for an item which wasn't + // indexed, so we just skip this step in that case. + // + // FIXME: this is a vague explanation for why this can't be a `get`, in + // theory it should be... + let &(ref remote_path, remote_item_type) = match cache.paths.find(&did) { + Some(p) => p, + None => continue, + }; let mut mydst = dst.clone(); for part in remote_path.slice_to(remote_path.len() - 1).iter() { @@ -823,7 +838,7 @@ fn fold_item(&mut self, item: clean::Item) -> Option { clean::StructItem(..) | clean::EnumItem(..) | clean::TypedefItem(..) | clean::TraitItem(..) | clean::FunctionItem(..) | clean::ModuleItem(..) | - clean::ForeignFunctionItem(..) => { + clean::ForeignFunctionItem(..) if !self.privmod => { // 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 @@ -840,7 +855,7 @@ fn fold_item(&mut self, item: clean::Item) -> Option { } // link variants to their parent enum because pages aren't emitted // for each variant - clean::VariantItem(..) => { + clean::VariantItem(..) if !self.privmod => { let mut stack = self.stack.clone(); stack.pop(); self.paths.insert(item.def_id, (stack, item_type::Enum)); @@ -932,14 +947,6 @@ fn fold_item(&mut self, item: clean::Item) -> Option { } None } - // Private modules may survive the strip-private pass if - // they contain impls for public types, but those will get - // stripped here - clean::Item { inner: clean::ModuleItem(ref m), - visibility, .. } - if (m.items.len() == 0 && - item.doc_value().is_none()) || - visibility != Some(ast::Public) => None, i => Some(i), } @@ -1020,7 +1027,7 @@ fn krate(self, mut krate: clean::Crate) -> io::IoResult<()> { /// The rendering driver uses this closure to queue up more work. fn item(&mut self, item: clean::Item, f: |&mut Context, clean::Item|) -> io::IoResult<()> { - fn render(w: io::File, cx: &mut Context, it: &clean::Item, + fn render(w: io::File, cx: &Context, it: &clean::Item, pushname: bool) -> io::IoResult<()> { info!("Rendering an item to {}", w.path().display()); // A little unfortunate that this is done like this, but it sure @@ -1047,9 +1054,24 @@ fn render(w: io::File, cx: &mut Context, it: &clean::Item, // of the pain by using a buffered writer instead of invoking the // write sycall all the time. let mut writer = BufferedWriter::new(w); - try!(layout::render(&mut writer as &mut Writer, &cx.layout, &page, - &Sidebar{ cx: cx, item: it }, - &Item{ cx: cx, item: it })); + if !cx.render_redirect_pages { + try!(layout::render(&mut writer, &cx.layout, &page, + &Sidebar{ cx: cx, item: it }, + &Item{ cx: cx, item: it })); + } else { + let mut url = "../".repeat(cx.current.len()); + match cache_key.get().unwrap().paths.find(&it.def_id) { + Some(&(ref names, _)) => { + for name in names.slice_to(names.len() - 1).iter() { + url.push_str(name.as_slice()); + url.push_str("/"); + } + url.push_str(item_path(it).as_slice()); + try!(layout::redirect(&mut writer, url.as_slice())); + } + None => {} + } + } writer.flush() } @@ -1057,6 +1079,17 @@ fn render(w: io::File, cx: &mut Context, it: &clean::Item, // modules are special because they add a namespace. We also need to // recurse into the items of the module as well. clean::ModuleItem(..) => { + // Private modules may survive the strip-private pass if they + // contain impls for public types. These modules can also + // contain items such as publicly reexported structures. + // + // External crates will provide links to these structures, so + // these modules are recursed into, but not rendered normally (a + // flag on the context). + if !self.render_redirect_pages { + self.render_redirect_pages = ignore_private_module(&item); + } + let name = item.name.get_ref().to_string(); let mut item = Some(item); self.recurse(name, |this| { @@ -1289,8 +1322,9 @@ fn document(w: &mut fmt::Formatter, item: &clean::Item) -> fmt::Result { fn item_module(w: &mut fmt::Formatter, cx: &Context, item: &clean::Item, items: &[clean::Item]) -> fmt::Result { try!(document(w, item)); - debug!("{:?}", items); - let mut indices = Vec::from_fn(items.len(), |i| i); + let mut indices = range(0, items.len()).filter(|i| { + !ignore_private_module(&items[*i]) + }).collect::>(); fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: uint, idx2: uint) -> Ordering { if shortty(i1) == shortty(i2) { @@ -1332,7 +1366,6 @@ fn cmp(i1: &clean::Item, i2: &clean::Item, idx1: uint, idx2: uint) -> Ordering { } } - debug!("{:?}", indices); indices.sort_by(|&i1, &i2| cmp(&items[i1], &items[i2], i1, i2)); debug!("{:?}", indices); @@ -1976,6 +2009,8 @@ fn block(w: &mut fmt::Formatter, short: &str, longty: &str, fn build_sidebar(m: &clean::Module) -> HashMap> { let mut map = HashMap::new(); for item in m.items.iter() { + if ignore_private_module(item) { continue } + let short = shortty(item).to_static_str(); let myname = match item.name { None => continue, @@ -2023,3 +2058,13 @@ fn item_primitive(w: &mut fmt::Formatter, try!(document(w, it)); render_methods(w, it) } + +fn ignore_private_module(it: &clean::Item) -> bool { + match it.inner { + clean::ModuleItem(ref m) => { + (m.items.len() == 0 && it.doc_value().is_none()) || + it.visibility != Some(ast::Public) + } + _ => false, + } +} diff --git a/src/librustdoc/passes.rs b/src/librustdoc/passes.rs index 60b2a82f007ef9367cf00cd72e732c3fd0ed94ab..7176ad1a6c151020c975da1ca637187c1f68ad17 100644 --- a/src/librustdoc/passes.rs +++ b/src/librustdoc/passes.rs @@ -70,7 +70,7 @@ fn fold_item(&mut self, i: Item) -> Option { for_: clean::ResolvedPath{ did, .. }, ref trait_, .. }) => { - // Impls for stripped don't need to exist + // Impls for stripped types don't need to exist if self.stripped.contains(&did.node) { return None; }