From 3425901d934f9e897b76f42309670285293df902 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 7 Jan 2014 18:46:16 -0800 Subject: [PATCH] Inline reexports in rustdoc If a reexport comes from a non-public module, then the documentation for the reexport will be inlined into the module that exports it, but if the reexport is targeted at a public type (like the prelude), then it is not inlined but rather hyperlinked. --- src/librustc/driver/driver.rs | 9 +- src/librustc/middle/privacy.rs | 31 ++- src/librustdoc/clean.rs | 2 +- src/librustdoc/core.rs | 17 +- src/librustdoc/test.rs | 2 +- src/librustdoc/visit_ast.rs | 385 +++++++++++++++++++++------------ 6 files changed, 293 insertions(+), 153 deletions(-) diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 0fccf50e009..46bdbab2257 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -209,6 +209,7 @@ pub fn phase_2_configure_and_expand(sess: Session, pub struct CrateAnalysis { exp_map2: middle::resolve::ExportMap2, exported_items: middle::privacy::ExportedItems, + public_items: middle::privacy::PublicItems, ty_cx: ty::ctxt, maps: astencode::Maps, reachable: @RefCell> @@ -268,9 +269,10 @@ pub fn phase_3_run_analysis_passes(sess: Session, method_map, ty_cx)); let maps = (external_exports, last_private_map); - let exported_items = time(time_passes, "privacy checking", maps, |(a, b)| - middle::privacy::check_crate(ty_cx, &method_map, &exp_map2, - a, b, crate)); + let (exported_items, public_items) = + time(time_passes, "privacy checking", maps, |(a, b)| + middle::privacy::check_crate(ty_cx, &method_map, &exp_map2, + a, b, crate)); time(time_passes, "effect checking", (), |_| middle::effect::check_crate(ty_cx, method_map, crate)); @@ -322,6 +324,7 @@ pub fn phase_3_run_analysis_passes(sess: Session, exp_map2: exp_map2, ty_cx: ty_cx, exported_items: exported_items, + public_items: public_items, maps: astencode::Maps { root_map: root_map, method_map: method_map, diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index 5e095061b42..58b35c0b1d8 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -35,6 +35,11 @@ type Context<'a> = (&'a method_map, &'a resolve::ExportMap2); /// A set of AST nodes exported by the crate. pub type ExportedItems = HashSet; +/// A set of AST nodes that are fully public in the crate. This map is used for +/// documentation purposes (reexporting a private struct inlines the doc, +/// reexporting a public struct doesn't inline the doc). +pub type PublicItems = HashSet; + //////////////////////////////////////////////////////////////////////////////// /// The parent visitor, used to determine what's the parent of what (node-wise) //////////////////////////////////////////////////////////////////////////////// @@ -165,6 +170,12 @@ struct EmbargoVisitor<'a> { // means that the destination of the reexport is exported, and hence the // destination must also be exported. reexports: HashSet, + + // These two fields are closely related to one another in that they are only + // used for generation of the 'PublicItems' set, not for privacy checking at + // all + public_items: PublicItems, + prev_public: bool, } impl<'a> EmbargoVisitor<'a> { @@ -186,7 +197,13 @@ impl<'a> EmbargoVisitor<'a> { impl<'a> Visitor<()> for EmbargoVisitor<'a> { fn visit_item(&mut self, item: &ast::item, _: ()) { - let orig_all_pub = self.prev_exported; + let orig_all_pub = self.prev_public; + self.prev_public = orig_all_pub && item.vis == ast::public; + if self.prev_public { + self.public_items.insert(item.id); + } + + let orig_all_exported = self.prev_exported; match item.node { // impls/extern blocks do not break the "public chain" because they // cannot have visibility qualifiers on them anyway @@ -202,7 +219,7 @@ impl<'a> Visitor<()> for EmbargoVisitor<'a> { // `pub` is explicitly listed. _ => { self.prev_exported = - (orig_all_pub && item.vis == ast::public) || + (orig_all_exported && item.vis == ast::public) || self.reexports.contains(&item.id); } } @@ -304,7 +321,8 @@ impl<'a> Visitor<()> for EmbargoVisitor<'a> { visit::walk_item(self, item, ()); - self.prev_exported = orig_all_pub; + self.prev_exported = orig_all_exported; + self.prev_public = orig_all_pub; } fn visit_foreign_item(&mut self, a: &ast::foreign_item, _: ()) { @@ -1002,7 +1020,7 @@ pub fn check_crate(tcx: ty::ctxt, exp_map2: &resolve::ExportMap2, external_exports: resolve::ExternalExports, last_private_map: resolve::LastPrivateMap, - crate: &ast::Crate) -> ExportedItems { + crate: &ast::Crate) -> (ExportedItems, PublicItems) { // Figure out who everyone's parent is let mut visitor = ParentVisitor { parents: HashMap::new(), @@ -1038,9 +1056,11 @@ pub fn check_crate(tcx: ty::ctxt, let mut visitor = EmbargoVisitor { tcx: tcx, exported_items: HashSet::new(), + public_items: HashSet::new(), reexports: HashSet::new(), exp_map2: exp_map2, prev_exported: true, + prev_public: true, }; loop { let before = visitor.exported_items.len(); @@ -1050,5 +1070,6 @@ pub fn check_crate(tcx: ty::ctxt, } } - return visitor.exported_items; + let EmbargoVisitor { exported_items, public_items, .. } = visitor; + return (exported_items, public_items); } diff --git a/src/librustdoc/clean.rs b/src/librustdoc/clean.rs index ad7fcd6b0c8..8e63dca57b0 100644 --- a/src/librustdoc/clean.rs +++ b/src/librustdoc/clean.rs @@ -72,7 +72,7 @@ pub struct Crate { externs: HashMap, } -impl Clean for visit_ast::RustdocVisitor { +impl<'a> Clean for visit_ast::RustdocVisitor<'a> { fn clean(&self) -> Crate { use syntax::attr::find_crateid; let cx = local_data::get(super::ctxtkey, |x| *x.unwrap()); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 5762e8570df..947038bc8d8 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -34,6 +34,7 @@ pub struct DocContext { pub struct CrateAnalysis { exported_items: privacy::ExportedItems, + public_items: privacy::PublicItems, } /// Parses, resolves, and typechecks the given crate @@ -75,12 +76,15 @@ fn get_ast_and_resolve(cpath: &Path, let crate = phase_1_parse_input(sess, cfg.clone(), &input); let (crate, ast_map) = phase_2_configure_and_expand(sess, cfg, crate); let driver::driver::CrateAnalysis { - exported_items, ty_cx, .. + exported_items, public_items, ty_cx, .. } = phase_3_run_analysis_passes(sess, &crate, ast_map); debug!("crate: {:?}", crate); return (DocContext { crate: crate, tycx: Some(ty_cx), sess: sess }, - CrateAnalysis { exported_items: exported_items }); + CrateAnalysis { + exported_items: exported_items, + public_items: public_items, + }); } pub fn run_core (libs: HashSet, cfgs: ~[~str], path: &Path) -> (clean::Crate, CrateAnalysis) { @@ -88,8 +92,11 @@ pub fn run_core (libs: HashSet, cfgs: ~[~str], path: &Path) -> (clean::Cra let ctxt = @ctxt; local_data::set(super::ctxtkey, ctxt); - let mut v = RustdocVisitor::new(); - v.visit(&ctxt.crate); + let crate = { + let mut v = RustdocVisitor::new(ctxt, Some(&analysis)); + v.visit(&ctxt.crate); + v.clean() + }; - (v.clean(), analysis) + (crate, analysis) } diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index a1ef5a62994..31a500718ee 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -67,7 +67,7 @@ pub fn run(input: &str, matches: &getopts::Matches) -> int { }; local_data::set(super::ctxtkey, ctx); - let mut v = RustdocVisitor::new(); + let mut v = RustdocVisitor::new(ctx, None); v.visit(&ctx.crate); let crate = v.clean(); let (crate, _) = passes::unindent_comments(crate); diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 9b6e809f835..60df2402e9e 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -13,169 +13,278 @@ use syntax::abi::AbiSet; use syntax::ast; +use syntax::ast_util; +use syntax::ast_map; use syntax::codemap::Span; +use core; use doctree::*; -pub struct RustdocVisitor { +pub struct RustdocVisitor<'a> { module: Module, attrs: ~[ast::Attribute], + cx: &'a core::DocContext, + analysis: Option<&'a core::CrateAnalysis>, } -impl RustdocVisitor { - pub fn new() -> RustdocVisitor { +impl<'a> RustdocVisitor<'a> { + pub fn new<'b>(cx: &'b core::DocContext, + analysis: Option<&'b core::CrateAnalysis>) -> RustdocVisitor<'b> { RustdocVisitor { module: Module::new(None), attrs: ~[], + cx: cx, + analysis: analysis, } } -} -impl RustdocVisitor { pub fn visit(&mut self, crate: &ast::Crate) { self.attrs = crate.attrs.clone(); - fn visit_struct_def(item: &ast::item, sd: @ast::struct_def, generics: - &ast::Generics) -> Struct { - debug!("Visiting struct"); - let struct_type = struct_type_from_def(sd); - Struct { - id: item.id, - struct_type: struct_type, - name: item.ident, - vis: item.vis, - attrs: item.attrs.clone(), - generics: generics.clone(), - fields: sd.fields.clone(), - where: item.span - } - } - fn visit_enum_def(it: &ast::item, def: &ast::enum_def, params: &ast::Generics) -> Enum { - debug!("Visiting enum"); - let mut vars: ~[Variant] = ~[]; - for x in def.variants.iter() { - vars.push(Variant { - name: x.node.name, - attrs: x.node.attrs.clone(), - vis: x.node.vis, - id: x.node.id, - kind: x.node.kind.clone(), - where: x.span, - }); - } - Enum { - name: it.ident, - variants: vars, - vis: it.vis, - generics: params.clone(), - attrs: it.attrs.clone(), - id: it.id, - where: it.span, - } - } + self.module = self.visit_mod_contents(crate.span, crate.attrs.clone(), + ast::public, ast::CRATE_NODE_ID, + &crate.module, None); + } - fn visit_fn(item: &ast::item, fd: &ast::fn_decl, purity: &ast::purity, - _abi: &AbiSet, gen: &ast::Generics) -> Function { - debug!("Visiting fn"); - Function { - id: item.id, - vis: item.vis, - attrs: item.attrs.clone(), - decl: fd.clone(), - name: item.ident, - where: item.span, - generics: gen.clone(), - purity: *purity, - } - } + pub fn visit_struct_def(&mut self, item: &ast::item, sd: @ast::struct_def, - fn visit_mod_contents(span: Span, attrs: ~[ast::Attribute], vis: - ast::visibility, id: ast::NodeId, m: &ast::_mod, + generics: &ast::Generics) -> Struct { + debug!("Visiting struct"); + let struct_type = struct_type_from_def(sd); + Struct { + id: item.id, + struct_type: struct_type, + name: item.ident, + vis: item.vis, + attrs: item.attrs.clone(), + generics: generics.clone(), + fields: sd.fields.clone(), + where: item.span + } + } + + pub fn visit_enum_def(&mut self, it: &ast::item, def: &ast::enum_def, + params: &ast::Generics) -> Enum { + debug!("Visiting enum"); + let mut vars: ~[Variant] = ~[]; + for x in def.variants.iter() { + vars.push(Variant { + name: x.node.name, + attrs: x.node.attrs.clone(), + vis: x.node.vis, + id: x.node.id, + kind: x.node.kind.clone(), + where: x.span, + }); + } + Enum { + name: it.ident, + variants: vars, + vis: it.vis, + generics: params.clone(), + attrs: it.attrs.clone(), + id: it.id, + where: it.span, + } + } + + pub fn visit_fn(&mut self, item: &ast::item, fd: &ast::fn_decl, + purity: &ast::purity, _abi: &AbiSet, + gen: &ast::Generics) -> Function { + debug!("Visiting fn"); + Function { + id: item.id, + vis: item.vis, + attrs: item.attrs.clone(), + decl: fd.clone(), + name: item.ident, + where: item.span, + generics: gen.clone(), + purity: *purity, + } + } + + pub fn visit_mod_contents(&mut self, span: Span, attrs: ~[ast::Attribute], + vis: ast::visibility, id: ast::NodeId, + m: &ast::_mod, name: Option) -> Module { - let mut om = Module::new(name); - om.view_items = m.view_items.clone(); - om.where = span; - om.attrs = attrs; - om.vis = vis; - om.id = id; - for i in m.items.iter() { - visit_item(*i, &mut om); - } - om + let mut om = Module::new(name); + for item in m.view_items.iter() { + self.visit_view_item(item, &mut om); } + om.where = span; + om.attrs = attrs; + om.vis = vis; + om.id = id; + for i in m.items.iter() { + self.visit_item(*i, &mut om); + } + om + } - fn visit_item(item: &ast::item, om: &mut Module) { - debug!("Visiting item {:?}", item); - match item.node { - ast::item_mod(ref m) => { - om.mods.push(visit_mod_contents(item.span, item.attrs.clone(), - item.vis, item.id, m, - Some(item.ident))); - }, - ast::item_enum(ref ed, ref gen) => om.enums.push(visit_enum_def(item, ed, gen)), - ast::item_struct(sd, ref gen) => om.structs.push(visit_struct_def(item, sd, gen)), - ast::item_fn(fd, ref pur, ref abi, ref gen, _) => - om.fns.push(visit_fn(item, fd, pur, abi, gen)), - ast::item_ty(ty, ref gen) => { - let t = Typedef { - ty: ty, - gen: gen.clone(), - name: item.ident, - id: item.id, - attrs: item.attrs.clone(), - where: item.span, - vis: item.vis, - }; - om.typedefs.push(t); - }, - ast::item_static(ty, ref mut_, ref exp) => { - let s = Static { - type_: ty, - mutability: mut_.clone(), - expr: exp.clone(), - id: item.id, - name: item.ident, - attrs: item.attrs.clone(), - where: item.span, - vis: item.vis, - }; - om.statics.push(s); - }, - ast::item_trait(ref gen, ref tr, ref met) => { - let t = Trait { - name: item.ident, - methods: met.clone(), - generics: gen.clone(), - parents: tr.clone(), - id: item.id, - attrs: item.attrs.clone(), - where: item.span, - vis: item.vis, - }; - om.traits.push(t); - }, - ast::item_impl(ref gen, ref tr, ty, ref meths) => { - let i = Impl { - generics: gen.clone(), - trait_: tr.clone(), - for_: ty, - methods: meths.clone(), - attrs: item.attrs.clone(), - id: item.id, - where: item.span, - vis: item.vis, - }; - om.impls.push(i); - }, - ast::item_foreign_mod(ref fm) => { - om.foreigns.push(fm.clone()); + pub fn visit_view_item(&mut self, item: &ast::view_item, om: &mut Module) { + if item.vis != ast::public { + return om.view_items.push(item.clone()); + } + let item = match item.node { + ast::view_item_use(ref paths) => { + // rustc no longer supports "use foo, bar;" + assert_eq!(paths.len(), 1); + match self.visit_view_path(paths[0], om) { + None => return, + Some(path) => { + ast::view_item { + node: ast::view_item_use(~[path]), + .. item.clone() + } + } } - _ => (), + } + ast::view_item_extern_mod(..) => item.clone() + }; + om.view_items.push(item); + } + + fn visit_view_path(&mut self, path: @ast::view_path, + om: &mut Module) -> Option<@ast::view_path> { + match path.node { + ast::view_path_simple(_, _, id) => { + if self.resolve_id(id, false, om) { return None } + } + ast::view_path_list(ref p, ref paths, ref b) => { + let mut mine = ~[]; + for path in paths.iter() { + if !self.resolve_id(path.node.id, false, om) { + mine.push(path.clone()); + } + } + + if mine.len() == 0 { return None } + return Some(@::syntax::codemap::Spanned { + node: ast::view_path_list(p.clone(), mine, b.clone()), + span: path.span, + }) + } + + // these are feature gated anyway + ast::view_path_glob(_, id) => { + if self.resolve_id(id, true, om) { return None } } } + return Some(path); + } - self.module = visit_mod_contents(crate.span, crate.attrs.clone(), - ast::public, ast::CRATE_NODE_ID, - &crate.module, None); + fn resolve_id(&mut self, id: ast::NodeId, glob: bool, + om: &mut Module) -> bool { + let def = { + let dm = match self.cx.tycx { + Some(tcx) => tcx.def_map.borrow(), + None => return false, + }; + ast_util::def_id_of_def(*dm.get().get(&id)) + }; + if !ast_util::is_local(def) { return false } + let analysis = match self.analysis { + Some(analysis) => analysis, None => return false + }; + if analysis.public_items.contains(&def.node) { return false } + + let item = { + let items = self.cx.tycx.unwrap().items.borrow(); + *items.get().get(&def.node) + }; + match item { + ast_map::node_item(it, _) => { + if glob { + match it.node { + ast::item_mod(ref m) => { + for vi in m.view_items.iter() { + self.visit_view_item(vi, om); + } + for i in m.items.iter() { + self.visit_item(*i, om); + } + } + _ => { fail!("glob not mapped to a module"); } + } + } else { + self.visit_item(it, om); + } + true + } + _ => false, + } + } + + pub fn visit_item(&mut self, item: &ast::item, om: &mut Module) { + debug!("Visiting item {:?}", item); + match item.node { + ast::item_mod(ref m) => { + om.mods.push(self.visit_mod_contents(item.span, item.attrs.clone(), + item.vis, item.id, m, + Some(item.ident))); + }, + ast::item_enum(ref ed, ref gen) => + om.enums.push(self.visit_enum_def(item, ed, gen)), + ast::item_struct(sd, ref gen) => + om.structs.push(self.visit_struct_def(item, sd, gen)), + ast::item_fn(fd, ref pur, ref abi, ref gen, _) => + om.fns.push(self.visit_fn(item, fd, pur, abi, gen)), + ast::item_ty(ty, ref gen) => { + let t = Typedef { + ty: ty, + gen: gen.clone(), + name: item.ident, + id: item.id, + attrs: item.attrs.clone(), + where: item.span, + vis: item.vis, + }; + om.typedefs.push(t); + }, + ast::item_static(ty, ref mut_, ref exp) => { + let s = Static { + type_: ty, + mutability: mut_.clone(), + expr: exp.clone(), + id: item.id, + name: item.ident, + attrs: item.attrs.clone(), + where: item.span, + vis: item.vis, + }; + om.statics.push(s); + }, + ast::item_trait(ref gen, ref tr, ref met) => { + let t = Trait { + name: item.ident, + methods: met.clone(), + generics: gen.clone(), + parents: tr.clone(), + id: item.id, + attrs: item.attrs.clone(), + where: item.span, + vis: item.vis, + }; + om.traits.push(t); + }, + ast::item_impl(ref gen, ref tr, ty, ref meths) => { + let i = Impl { + generics: gen.clone(), + trait_: tr.clone(), + for_: ty, + methods: meths.clone(), + attrs: item.attrs.clone(), + id: item.id, + where: item.span, + vis: item.vis, + }; + om.impls.push(i); + }, + ast::item_foreign_mod(ref fm) => { + om.foreigns.push(fm.clone()); + } + _ => (), + } } }