From 3d693d74b814eed6140b9ca29ff01eb3411255a2 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Sat, 12 Oct 2013 14:40:41 -0700 Subject: [PATCH] rustdoc: Use privacy visibility for pruning This commit ends rustdoc's approximation of privacy and instead uses the result of the various compiler passes instead. Closes #9827 --- src/librustc/driver/driver.rs | 2 + src/librustdoc/core.rs | 38 +++++- src/librustdoc/html/render.rs | 2 +- src/librustdoc/passes.rs | 232 +++++++++++++++++++++------------- src/librustdoc/rustdoc.rs | 5 +- 5 files changed, 179 insertions(+), 100 deletions(-) diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 0c896007fc3..ef8b859efd6 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -199,6 +199,7 @@ pub fn phase_2_configure_and_expand(sess: Session, pub struct CrateAnalysis { exp_map2: middle::resolve::ExportMap2, + exported_items: middle::privacy::ExportedItems, ty_cx: ty::ctxt, maps: astencode::Maps, reachable: @mut HashSet @@ -310,6 +311,7 @@ pub fn phase_3_run_analysis_passes(sess: Session, CrateAnalysis { exp_map2: exp_map2, ty_cx: ty_cx, + exported_items: exported_items, maps: astencode::Maps { root_map: root_map, method_map: method_map, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 7537366014e..69d9f8f5946 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -10,14 +10,17 @@ use rustc; use rustc::{driver, middle}; +use rustc::middle::privacy; use syntax::ast; +use syntax::ast_util::is_local; use syntax::diagnostic; use syntax::parse; use syntax; use std::os; use std::local_data; +use std::hashmap::HashMap; use visit_ast::RustdocVisitor; use clean; @@ -29,10 +32,19 @@ pub struct DocContext { sess: driver::session::Session } +pub struct CrateAnalysis { + exported_items: privacy::ExportedItems, + reexports: HashMap, +} + /// Parses, resolves, and typechecks the given crate -fn get_ast_and_resolve(cpath: &Path, libs: ~[Path]) -> DocContext { +fn get_ast_and_resolve(cpath: &Path, + libs: ~[Path]) -> (DocContext, CrateAnalysis) { use syntax::codemap::dummy_spanned; - use rustc::driver::driver::*; + use rustc::driver::driver::{file_input, build_configuration, + phase_1_parse_input, + phase_2_configure_and_expand, + phase_3_run_analysis_passes}; let parsesess = parse::new_parse_sess(None); let input = file_input(cpath.clone()); @@ -60,14 +72,26 @@ fn get_ast_and_resolve(cpath: &Path, libs: ~[Path]) -> DocContext { let mut crate = phase_1_parse_input(sess, cfg.clone(), &input); crate = phase_2_configure_and_expand(sess, cfg, crate); - let analysis = phase_3_run_analysis_passes(sess, &crate); + let driver::driver::CrateAnalysis { + exported_items, ty_cx, exp_map2, _ + } = phase_3_run_analysis_passes(sess, &crate); + + let mut reexports = HashMap::new(); + for (&module, nodes) in exp_map2.iter() { + reexports.insert(module, nodes.iter() + .filter(|e| e.reexport && is_local(e.def_id)) + .map(|e| e.def_id.node) + .to_owned_vec()); + } debug2!("crate: {:?}", crate); - DocContext { crate: crate, tycx: analysis.ty_cx, sess: sess } + return (DocContext { crate: crate, tycx: ty_cx, sess: sess }, + CrateAnalysis { reexports: reexports, exported_items: exported_items }); } -pub fn run_core (libs: ~[Path], path: &Path) -> clean::Crate { - let ctxt = @get_ast_and_resolve(path, libs); +pub fn run_core (libs: ~[Path], path: &Path) -> (clean::Crate, CrateAnalysis) { + let (ctxt, analysis) = get_ast_and_resolve(path, libs); + let ctxt = @ctxt; debug2!("defmap:"); for (k, v) in ctxt.tycx.def_map.iter() { debug2!("{:?}: {:?}", k, v); @@ -77,5 +101,5 @@ pub fn run_core (libs: ~[Path], path: &Path) -> clean::Crate { let v = @mut RustdocVisitor::new(); v.visit(&ctxt.crate); - v.clean() + (v.clean(), analysis) } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index bb78b5aabb5..5b27adb2967 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -582,7 +582,7 @@ impl DocFolder for Cache { clean::StructItem(*) | clean::EnumItem(*) | clean::TypedefItem(*) | clean::TraitItem(*) | clean::FunctionItem(*) | clean::ModuleItem(*) | - clean::VariantItem(*) => { + clean::ForeignFunctionItem(*) | clean::VariantItem(*) => { self.paths.insert(item.id, (self.stack.clone(), shortty(&item))); } _ => {} diff --git a/src/librustdoc/passes.rs b/src/librustdoc/passes.rs index 43f2896a0d2..aa419a90563 100644 --- a/src/librustdoc/passes.rs +++ b/src/librustdoc/passes.rs @@ -9,11 +9,14 @@ // except according to those terms. use std::num; +use std::cell::Cell; use std::uint; use std::hashmap::HashSet; +use std::local_data; use syntax::ast; +use core; use clean; use clean::Item; use plugins; @@ -51,103 +54,35 @@ pub fn strip_hidden(crate: clean::Crate) -> plugins::PluginResult { /// Strip private items from the point of view of a crate or externally from a /// crate, specified by the `xcrate` flag. -pub fn strip_private(mut crate: clean::Crate) -> plugins::PluginResult { +pub fn strip_private(crate: clean::Crate) -> plugins::PluginResult { // This stripper collects all *retained* nodes. - struct Stripper<'self>(&'self mut HashSet); - impl<'self> fold::DocFolder for Stripper<'self> { - fn fold_item(&mut self, i: Item) -> Option { - match i.inner { - // These items can all get re-exported - clean::TypedefItem(*) | clean::StaticItem(*) | - clean::StructItem(*) | clean::EnumItem(*) | - clean::TraitItem(*) | clean::FunctionItem(*) | - clean::ViewItemItem(*) | clean::MethodItem(*) | - clean::ForeignFunctionItem(*) | clean::ForeignStaticItem(*) => { - // XXX: re-exported items should get surfaced in the docs as - // well (using the output of resolve analysis) - if i.visibility != Some(ast::public) { - return None; - } - } - - // These are public-by-default (if the enum/struct was public) - clean::VariantItem(*) | clean::StructFieldItem(*) => { - if i.visibility == Some(ast::private) { - return None; - } - } - - // handled below - clean::ModuleItem(*) => {} - - // impls/tymethods have no control over privacy - clean::ImplItem(*) | clean::TyMethodItem(*) => {} - } - - let fastreturn = match i.inner { - // nothing left to do for traits (don't want to filter their - // methods out, visibility controlled by the trait) - clean::TraitItem(*) => true, - - // implementations of traits are always public. - clean::ImplItem(ref imp) if imp.trait_.is_some() => true, - - _ => false, - }; - - let i = if fastreturn { - self.insert(i.id); - return Some(i); - } else { - self.fold_item_recur(i) - }; - - match i { - Some(i) => { - match i.inner { - // emptied modules/impls have no need to exist - clean::ModuleItem(ref m) if m.items.len() == 0 => None, - clean::ImplItem(ref i) if i.methods.len() == 0 => None, - _ => { - self.insert(i.id); - Some(i) - } - } - } - None => None, - } - } - } - - // This stripper discards all private impls of traits - struct ImplStripper<'self>(&'self HashSet); - impl<'self> fold::DocFolder for ImplStripper<'self> { - fn fold_item(&mut self, i: Item) -> Option { - match i.inner { - clean::ImplItem(ref imp) => { - match imp.trait_ { - Some(clean::ResolvedPath{ id, _ }) => { - if !self.contains(&id) { - return None; - } - } - Some(*) | None => {} - } - } - _ => {} - } - self.fold_item_recur(i) - } - } - let mut retained = HashSet::new(); - // First, strip all private items + let crate = Cell::new(crate); + let exported_items = do local_data::get(super::analysiskey) |analysis| { + let analysis = analysis.unwrap(); + let mut exported_items = analysis.exported_items.clone(); + { + let mut finder = ExportedItemsFinder { + exported_items: &mut exported_items, + analysis: analysis, + }; + let c = finder.fold_crate(crate.take()); + crate.put_back(c); + } + exported_items + }; + let mut crate = crate.take(); + + // strip all private items { - let mut stripper = Stripper(&mut retained); + let mut stripper = Stripper { + retained: &mut retained, + exported_items: &exported_items, + }; crate = stripper.fold_crate(crate); } - // Next, strip all private implementations of traits + // strip all private implementations of traits { let mut stripper = ImplStripper(&retained); crate = stripper.fold_crate(crate); @@ -155,6 +90,121 @@ pub fn strip_private(mut crate: clean::Crate) -> plugins::PluginResult { (crate, None) } +struct ExportedItemsFinder<'self> { + exported_items: &'self mut HashSet, + analysis: &'self core::CrateAnalysis, +} + +impl<'self> fold::DocFolder for ExportedItemsFinder<'self> { + fn fold_item(&mut self, i: Item) -> Option { + match i.inner { + clean::ModuleItem(*) => { + if self.analysis.exported_items.contains(&i.id) { + match self.analysis.reexports.find(&i.id) { + Some(l) => { + for &id in l.iter() { + self.exported_items.insert(id); + } + } + None => {} + } + } + } + _ => {} + } + return self.fold_item_recur(i); + } +} + +struct Stripper<'self> { + retained: &'self mut HashSet, + exported_items: &'self HashSet, +} + +impl<'self> fold::DocFolder for Stripper<'self> { + fn fold_item(&mut self, i: Item) -> Option { + match i.inner { + // These items can all get re-exported + clean::TypedefItem(*) | clean::StaticItem(*) | + clean::StructItem(*) | clean::EnumItem(*) | + clean::TraitItem(*) | clean::FunctionItem(*) | + clean::VariantItem(*) | clean::MethodItem(*) | + clean::ForeignFunctionItem(*) | clean::ForeignStaticItem(*) => { + if !self.exported_items.contains(&i.id) { + return None; + } + } + + clean::ViewItemItem(*) | clean::StructFieldItem(*) => { + if i.visibility != Some(ast::public) { + return None; + } + } + + // handled below + clean::ModuleItem(*) => {} + + // impls/tymethods have no control over privacy + clean::ImplItem(*) | clean::TyMethodItem(*) => {} + } + + let fastreturn = match i.inner { + // nothing left to do for traits (don't want to filter their + // methods out, visibility controlled by the trait) + clean::TraitItem(*) => true, + + // implementations of traits are always public. + clean::ImplItem(ref imp) if imp.trait_.is_some() => true, + + _ => false, + }; + + let i = if fastreturn { + self.retained.insert(i.id); + return Some(i); + } else { + self.fold_item_recur(i) + }; + + match i { + Some(i) => { + match i.inner { + // emptied modules/impls have no need to exist + clean::ModuleItem(ref m) if m.items.len() == 0 => None, + clean::ImplItem(ref i) if i.methods.len() == 0 => None, + _ => { + self.retained.insert(i.id); + Some(i) + } + } + } + None => None, + } + } +} + +// This stripper discards all private impls of traits +struct ImplStripper<'self>(&'self HashSet); +impl<'self> fold::DocFolder for ImplStripper<'self> { + fn fold_item(&mut self, i: Item) -> Option { + match i.inner { + clean::ImplItem(ref imp) => { + match imp.trait_ { + Some(clean::ResolvedPath{ id, _ }) => { + if !self.contains(&id) { + return None; + } + } + Some(*) | None => {} + } + } + _ => {} + } + self.fold_item_recur(i) + } +} + + pub fn unindent_comments(crate: clean::Crate) -> plugins::PluginResult { struct CommentCleaner; impl fold::DocFolder for CommentCleaner { diff --git a/src/librustdoc/rustdoc.rs b/src/librustdoc/rustdoc.rs index a724ff3bf74..e989796ae88 100644 --- a/src/librustdoc/rustdoc.rs +++ b/src/librustdoc/rustdoc.rs @@ -24,6 +24,7 @@ extern mod rustc; extern mod extra; use std::cell::Cell; +use std::local_data; use std::rt::io::Writer; use std::rt::io::file::FileInfo; use std::rt::io; @@ -73,6 +74,7 @@ static DEFAULT_PASSES: &'static [&'static str] = &[ ]; local_data_key!(pub ctxtkey: @core::DocContext) +local_data_key!(pub analysiskey: core::CrateAnalysis) type Output = (clean::Crate, ~[plugins::PluginJson]); @@ -191,11 +193,12 @@ fn rust_input(cratefile: &str, matches: &getopts::Matches) -> Output { let libs = Cell::new(matches.opt_strs("L").map(|s| Path(*s))); let cr = Cell::new(Path(cratefile)); info2!("starting to run rustc"); - let crate = do std::task::try { + let (crate, analysis) = do std::task::try { let cr = cr.take(); core::run_core(libs.take(), &cr) }.unwrap(); info2!("finished with rustc"); + local_data::set(analysiskey, analysis); // Process all of the crate attributes, extracting plugin metadata along // with the passes which we are supposed to run.