From ea83349c9f06e13aae4a70e81d5dc470bf051bca Mon Sep 17 00:00:00 2001 From: mitaa Date: Thu, 7 Apr 2016 05:59:02 +0200 Subject: [PATCH 1/3] Retire rustdocs `ANALYSISKEY` The thread-local isn't needed and consists of mostly empty fields which were just used to move the data into `html::render::CACHE_KEY`. --- src/librustc/middle/privacy.rs | 9 ++++- src/librustdoc/clean/inline.rs | 6 +-- src/librustdoc/clean/mod.rs | 21 +++++++--- src/librustdoc/core.rs | 70 +++++++++++----------------------- src/librustdoc/html/render.rs | 50 ++++++++++++++---------- src/librustdoc/lib.rs | 21 +++------- src/librustdoc/passes.rs | 5 +-- src/librustdoc/test.rs | 9 ++--- src/librustdoc/visit_ast.rs | 10 +---- 9 files changed, 90 insertions(+), 111 deletions(-) diff --git a/src/librustc/middle/privacy.rs b/src/librustc/middle/privacy.rs index c1dc727449ac0..478f662d0962a 100644 --- a/src/librustc/middle/privacy.rs +++ b/src/librustc/middle/privacy.rs @@ -15,10 +15,11 @@ use util::nodemap::{DefIdSet, FnvHashMap}; use std::hash::Hash; +use std::fmt; use syntax::ast::NodeId; // Accessibility levels, sorted in ascending order -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum AccessLevel { // Exported items + items participating in various kinds of public interfaces, // but not directly nameable. For example, if function `fn f() -> T {...}` is @@ -56,6 +57,12 @@ impl Default for AccessLevels { } } +impl fmt::Debug for AccessLevels { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.map, f) + } +} + /// A set containing all exported definitions from external crates. /// The set does not contain any entries from local crates. pub type ExternalExports = DefIdSet; diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 850975498269b..6cca602466a3d 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -116,7 +116,7 @@ fn try_inline_def(cx: &DocContext, tcx: &TyCtxt, } _ => return None, }; - cx.inlined.borrow_mut().as_mut().unwrap().insert(did); + cx.renderinfo.borrow_mut().inlined.insert(did); ret.push(clean::Item { source: clean::Span::empty(), name: Some(tcx.item_name(did).to_string()), @@ -146,7 +146,7 @@ pub fn record_extern_fqn(cx: &DocContext, did: DefId, kind: clean::TypeKind) { elem.data.to_string() }); let fqn = once(crate_name).chain(relative).collect(); - cx.external_paths.borrow_mut().as_mut().unwrap().insert(did, (fqn, kind)); + cx.renderinfo.borrow_mut().external_paths.insert(did, (fqn, kind)); } } @@ -295,7 +295,7 @@ pub fn build_impl(cx: &DocContext, tcx: &TyCtxt, did: DefId, ret: &mut Vec) { - if !cx.inlined.borrow_mut().as_mut().unwrap().insert(did) { + if !cx.renderinfo.borrow_mut().inlined.insert(did) { return } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index d8d1472560d82..bba394bb90dc6 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -35,6 +35,7 @@ use syntax::ptr::P; use rustc_trans::back::link; use rustc::middle::cstore::{self, CrateStore}; +use rustc::middle::privacy::AccessLevels; use rustc::hir::def::Def; use rustc::hir::def_id::{DefId, DefIndex}; use rustc::ty::subst::{self, ParamSpace, VecPerParamSpace}; @@ -46,8 +47,10 @@ use rustc::hir; use std::collections::{HashMap, HashSet}; use std::path::PathBuf; use std::rc::Rc; +use std::sync::Arc; use std::u32; use std::env::current_dir; +use std::mem; use core::DocContext; use doctree; @@ -112,13 +115,16 @@ impl, U> Clean> for P<[T]> { } } -#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] +#[derive(Clone, Debug)] pub struct Crate { pub name: String, pub src: PathBuf, pub module: Option, pub externs: Vec<(ast::CrateNum, ExternalCrate)>, pub primitives: Vec, + pub access_levels: Arc>, + // These are later on moved into `CACHEKEY`, leaving the map empty. + // Only here so that they can be filtered through the rustdoc passes. pub external_traits: HashMap, } @@ -130,6 +136,7 @@ impl<'a, 'tcx> Clean for visit_ast::RustdocVisitor<'a, 'tcx> { if let Some(t) = cx.tcx_opt() { cx.deref_trait_did.set(t.lang_items.deref_trait()); + cx.renderinfo.borrow_mut().deref_trait_did = cx.deref_trait_did.get(); } let mut externs = Vec::new(); @@ -204,14 +211,17 @@ impl<'a, 'tcx> Clean for visit_ast::RustdocVisitor<'a, 'tcx> { Input::Str { ref name, .. } => PathBuf::from(name.clone()), }; + let mut access_levels = cx.access_levels.borrow_mut(); + let mut external_traits = cx.external_traits.borrow_mut(); + Crate { name: name.to_string(), src: src, module: Some(module), externs: externs, primitives: primitives, - external_traits: cx.external_traits.borrow_mut().take() - .unwrap_or(HashMap::new()), + access_levels: Arc::new(mem::replace(&mut access_levels, Default::default())), + external_traits: mem::replace(&mut external_traits, Default::default()), } } } @@ -540,8 +550,7 @@ impl Clean for hir::TyParam { impl<'tcx> Clean for ty::TypeParameterDef<'tcx> { fn clean(&self, cx: &DocContext) -> TyParam { - cx.external_typarams.borrow_mut().as_mut().unwrap() - .insert(self.def_id, self.name.clean(cx)); + cx.renderinfo.borrow_mut().external_typarams.insert(self.def_id, self.name.clean(cx)); TyParam { name: self.name.clean(cx), did: self.def_id, @@ -2668,7 +2677,7 @@ fn register_def(cx: &DocContext, def: Def) -> DefId { inline::record_extern_fqn(cx, did, kind); if let TypeTrait = kind { let t = inline::build_external_trait(cx, tcx, did); - cx.external_traits.borrow_mut().as_mut().unwrap().insert(did, t); + cx.external_traits.borrow_mut().insert(did, t); } did } diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 6b7aa103e1d0a..c9773ebccb4ee 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -29,12 +29,13 @@ use syntax::feature_gate::UnstableFeatures; use syntax::parse::token; use std::cell::{RefCell, Cell}; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::rc::Rc; use visit_ast::RustdocVisitor; use clean; use clean::Clean; +use html::render::RenderInfo; pub use rustc::session::config::Input; pub use rustc::session::search_paths::SearchPaths; @@ -45,19 +46,20 @@ pub enum MaybeTyped<'a, 'tcx: 'a> { NotTyped(&'a session::Session) } -pub type ExternalPaths = RefCell, clean::TypeKind)>>>; +pub type ExternalPaths = HashMap, clean::TypeKind)>; pub struct DocContext<'a, 'tcx: 'a> { pub map: &'a hir_map::Map<'tcx>, pub maybe_typed: MaybeTyped<'a, 'tcx>, pub input: Input, - pub external_paths: ExternalPaths, - pub external_traits: RefCell>>, - pub external_typarams: RefCell>>, - pub inlined: RefCell>>, pub all_crate_impls: RefCell>>, + // Later on moved into `clean::Crate` + pub access_levels: RefCell>, + // Later on moved into `html::render::CACHE_KEY` + pub renderinfo: RefCell, pub deref_trait_did: Cell>, + // Later on moved through `clean::Crate` into `html::render::CACHE_KEY` + pub external_traits: RefCell>, } impl<'b, 'tcx> DocContext<'b, 'tcx> { @@ -81,20 +83,14 @@ impl<'b, 'tcx> DocContext<'b, 'tcx> { } } -pub struct CrateAnalysis { - pub access_levels: AccessLevels, - pub external_paths: ExternalPaths, - pub external_typarams: RefCell>>, - pub inlined: RefCell>>, - pub deref_trait_did: Option, -} - pub type Externs = HashMap>; -pub fn run_core(search_paths: SearchPaths, cfgs: Vec, externs: Externs, - input: Input, triple: Option) - -> (clean::Crate, CrateAnalysis) { - +pub fn run_core(search_paths: SearchPaths, + cfgs: Vec, + externs: Externs, + input: Input, + triple: Option) -> (clean::Crate, RenderInfo) +{ // Parse, resolve, and typecheck the given crate. let cpath = match input { @@ -148,7 +144,7 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec, externs: Externs, let arenas = ty::CtxtArenas::new(); let hir_map = driver::make_map(&sess, &mut hir_forest); - let krate_and_analysis = abort_on_err(driver::phase_3_run_analysis_passes(&sess, + abort_on_err(driver::phase_3_run_analysis_passes(&sess, &cstore, hir_map, &arenas, @@ -175,42 +171,20 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec, externs: Externs, map: &tcx.map, maybe_typed: Typed(tcx), input: input, - external_traits: RefCell::new(Some(HashMap::new())), - external_typarams: RefCell::new(Some(HashMap::new())), - external_paths: RefCell::new(Some(HashMap::new())), - inlined: RefCell::new(Some(HashSet::new())), all_crate_impls: RefCell::new(HashMap::new()), deref_trait_did: Cell::new(None), + access_levels: RefCell::new(access_levels), + external_traits: RefCell::new(HashMap::new()), + renderinfo: RefCell::new(Default::default()), }; debug!("crate: {:?}", ctxt.map.krate()); - let mut analysis = CrateAnalysis { - access_levels: access_levels, - external_paths: RefCell::new(None), - external_typarams: RefCell::new(None), - inlined: RefCell::new(None), - deref_trait_did: None, - }; - let krate = { - let mut v = RustdocVisitor::new(&ctxt, Some(&analysis)); + let mut v = RustdocVisitor::new(&ctxt); v.visit(ctxt.map.krate()); v.clean(&ctxt) }; - let external_paths = ctxt.external_paths.borrow_mut().take(); - *analysis.external_paths.borrow_mut() = external_paths; - - let map = ctxt.external_typarams.borrow_mut().take(); - *analysis.external_typarams.borrow_mut() = map; - - let map = ctxt.inlined.borrow_mut().take(); - *analysis.inlined.borrow_mut() = map; - - analysis.deref_trait_did = ctxt.deref_trait_did.get(); - - Some((krate, analysis)) - }), &sess); - - krate_and_analysis.unwrap() + Some((krate, ctxt.renderinfo.into_inner())) + }), &sess).unwrap() } diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index c5850089578cd..2d59747d00c5b 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -253,7 +253,7 @@ pub struct Cache { parent_is_trait_impl: bool, search_index: Vec, stripped_mod: bool, - access_levels: AccessLevels, + access_levels: Arc>, deref_trait_did: Option, // In rare case where a structure is defined in one module but implemented @@ -264,6 +264,16 @@ pub struct Cache { orphan_methods: Vec<(DefId, clean::Item)>, } +/// Temporary storage for data obtained during `RustdocVisitor::clean()`. +/// Later on moved into `CACHE_KEY`. +#[derive(Default)] +pub struct RenderInfo { + pub inlined: HashSet, + pub external_paths: ::core::ExternalPaths, + pub external_typarams: HashMap, + pub deref_trait_did: Option, +} + /// Helper struct to render all source code to HTML pages struct SourceCollector<'a> { scx: &'a mut SharedContext, @@ -415,7 +425,8 @@ pub fn run(mut krate: clean::Crate, external_html: &ExternalHtml, dst: PathBuf, passes: HashSet, - css_file_extension: Option) -> Result<(), Error> { + css_file_extension: Option, + renderinfo: RenderInfo) -> Result<(), Error> { let src_root = match krate.src.parent() { Some(p) => p.to_path_buf(), None => PathBuf::new(), @@ -482,19 +493,20 @@ pub fn run(mut krate: clean::Crate, }; // Crawl the crate to build various caches used for the output - let analysis = ::ANALYSISKEY.with(|a| a.clone()); - let analysis = analysis.borrow(); - let access_levels = analysis.as_ref().map(|a| a.access_levels.clone()); - let access_levels = access_levels.unwrap_or(Default::default()); - let paths: HashMap, ItemType)> = - analysis.as_ref().map(|a| { - let paths = a.external_paths.borrow_mut().take().unwrap(); - paths.into_iter().map(|(k, (v, t))| (k, (v, ItemType::from_type_kind(t)))).collect() - }).unwrap_or(HashMap::new()); + let RenderInfo { + inlined, + external_paths, + external_typarams, + deref_trait_did, + } = renderinfo; + + let paths = external_paths.into_iter() + .map(|(k, (v, t))| (k, (v, ItemType::from_type_kind(t)))) + .collect::>(); + let mut cache = Cache { impls: HashMap::new(), - external_paths: paths.iter().map(|(&k, v)| (k, v.0.clone())) - .collect(), + external_paths: paths.iter().map(|(&k, v)| (k, v.0.clone())).collect(), paths: paths, implementors: HashMap::new(), stack: Vec::new(), @@ -504,16 +516,12 @@ pub fn run(mut krate: clean::Crate, extern_locations: HashMap::new(), primitive_locations: HashMap::new(), stripped_mod: false, - access_levels: access_levels, + access_levels: krate.access_levels.clone(), orphan_methods: Vec::new(), traits: mem::replace(&mut krate.external_traits, HashMap::new()), - deref_trait_did: analysis.as_ref().and_then(|a| a.deref_trait_did), - typarams: analysis.as_ref().map(|a| { - a.external_typarams.borrow_mut().take().unwrap() - }).unwrap_or(HashMap::new()), - inlined: analysis.as_ref().map(|a| { - a.inlined.borrow_mut().take().unwrap() - }).unwrap_or(HashSet::new()), + deref_trait_did: deref_trait_did, + typarams: external_typarams, + inlined: inlined, }; // Cache where all our extern crates are located diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index bc7c7c5e0caf0..0d80e70281603 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -48,14 +48,11 @@ extern crate rustc_unicode; extern crate serialize as rustc_serialize; // used by deriving -use std::cell::RefCell; use std::collections::HashMap; use std::default::Default; use std::env; -use std::io::Read; use std::path::PathBuf; use std::process; -use std::rc::Rc; use std::sync::mpsc::channel; use externalfiles::ExternalHtml; @@ -113,12 +110,9 @@ const DEFAULT_PASSES: &'static [&'static str] = &[ "unindent-comments", ]; -thread_local!(pub static ANALYSISKEY: Rc>> = { - Rc::new(RefCell::new(None)) -}); - struct Output { krate: clean::Crate, + renderinfo: html::render::RenderInfo, passes: Vec, } @@ -302,14 +296,15 @@ pub fn main_args(args: &[String]) -> isize { return 1; } }; - let Output { krate, passes, } = out; + let Output { krate, passes, renderinfo } = out; info!("going to format"); match matches.opt_str("w").as_ref().map(|s| &**s) { Some("html") | None => { html::render::run(krate, &external_html, output.unwrap_or(PathBuf::from("doc")), passes.into_iter().collect(), - css_file_extension) + css_file_extension, + renderinfo) .expect("failed to generate documentation") } Some(s) => { @@ -380,12 +375,8 @@ fn rust_input(cratefile: &str, externs: core::Externs, matches: &getopts::Matche tx.send(core::run_core(paths, cfgs, externs, Input::File(cr), triple)).unwrap(); }); - let (mut krate, analysis) = rx.recv().unwrap(); + let (mut krate, renderinfo) = rx.recv().unwrap(); info!("finished with rustc"); - let mut analysis = Some(analysis); - ANALYSISKEY.with(|s| { - *s.borrow_mut() = analysis.take(); - }); if let Some(name) = matches.opt_str("crate-name") { krate.name = name @@ -443,5 +434,5 @@ fn rust_input(cratefile: &str, externs: core::Externs, matches: &getopts::Matche // Run everything! info!("Executing passes/plugins"); let krate = pm.run_plugins(krate); - Output { krate: krate, passes: passes } + Output { krate: krate, renderinfo: renderinfo, passes: passes } } diff --git a/src/librustdoc/passes.rs b/src/librustdoc/passes.rs index adc39b69986d6..b9a2acb02e802 100644 --- a/src/librustdoc/passes.rs +++ b/src/librustdoc/passes.rs @@ -88,10 +88,7 @@ pub fn strip_hidden(krate: clean::Crate) -> plugins::PluginResult { pub fn strip_private(mut krate: clean::Crate) -> plugins::PluginResult { // This stripper collects all *retained* nodes. let mut retained = DefIdSet(); - let analysis = super::ANALYSISKEY.with(|a| a.clone()); - let analysis = analysis.borrow(); - let analysis = analysis.as_ref().unwrap(); - let access_levels = analysis.access_levels.clone(); + let access_levels = krate.access_levels.clone(); // strip all private items { diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 982f477fc4ae1..5a7050fb42f30 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -108,15 +108,14 @@ pub fn run(input: &str, map: &map, maybe_typed: core::NotTyped(&sess), input: input, - external_paths: RefCell::new(Some(HashMap::new())), - external_traits: RefCell::new(None), - external_typarams: RefCell::new(None), - inlined: RefCell::new(None), + external_traits: RefCell::new(HashMap::new()), all_crate_impls: RefCell::new(HashMap::new()), deref_trait_did: Cell::new(None), + access_levels: Default::default(), + renderinfo: Default::default(), }; - let mut v = RustdocVisitor::new(&ctx, None); + let mut v = RustdocVisitor::new(&ctx); v.visit(ctx.map.krate()); let mut krate = v.clean(&ctx); if let Some(name) = crate_name { diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 5c36c38abc587..a406c843b82e4 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -41,14 +41,12 @@ pub struct RustdocVisitor<'a, 'tcx: 'a> { pub module: Module, pub attrs: hir::HirVec, pub cx: &'a core::DocContext<'a, 'tcx>, - pub analysis: Option<&'a core::CrateAnalysis>, view_item_stack: HashSet, inlining_from_glob: bool, } impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { - pub fn new(cx: &'a core::DocContext<'a, 'tcx>, - analysis: Option<&'a core::CrateAnalysis>) -> RustdocVisitor<'a, 'tcx> { + pub fn new(cx: &'a core::DocContext<'a, 'tcx>) -> RustdocVisitor<'a, 'tcx> { // If the root is reexported, terminate all recursion. let mut stack = HashSet::new(); stack.insert(ast::CRATE_NODE_ID); @@ -56,7 +54,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { module: Module::new(None), attrs: hir::HirVec::new(), cx: cx, - analysis: analysis, view_item_stack: stack, inlining_from_glob: false, } @@ -247,13 +244,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let def_node_id = match tcx.map.as_local_node_id(def) { Some(n) => n, None => return false }; - let analysis = match self.analysis { - Some(analysis) => analysis, None => return false - }; let use_attrs = tcx.map.attrs(id).clean(self.cx); - let is_private = !analysis.access_levels.is_public(def); + let is_private = !self.cx.access_levels.borrow().is_public(def); let is_hidden = inherits_doc_hidden(self.cx, def_node_id); let is_no_inline = use_attrs.list("doc").has_word("no_inline"); From cfad7ad947a9b6d6d5117c19618b1d230064dc73 Mon Sep 17 00:00:00 2001 From: mitaa Date: Fri, 15 Apr 2016 16:34:48 +0200 Subject: [PATCH 2/3] Perform doc-reachability check for inlined impls This changes the current rule that impls within `doc(hidden)` modules aren't inlined, to only inlining impls where the implemented trait and type are reachable in documentation. --- src/librustc/middle/cstore.rs | 1 + src/librustdoc/clean/inline.rs | 39 +++++-- src/librustdoc/clean/mod.rs | 2 +- src/librustdoc/core.rs | 19 +++- src/librustdoc/html/render.rs | 3 + src/librustdoc/lib.rs | 1 + src/librustdoc/test.rs | 3 +- src/librustdoc/visit_ast.rs | 40 +++++-- src/librustdoc/visit_lib.rs | 104 ++++++++++++++++++ .../auxiliary/rustdoc-nonreachable-impls.rs | 44 ++++++++ .../rustdoc/inline_cross/issue-31948-1.rs | 37 +++++++ .../rustdoc/inline_cross/issue-31948-2.rs | 31 ++++++ src/test/rustdoc/inline_cross/issue-31948.rs | 39 +++++++ 13 files changed, 337 insertions(+), 26 deletions(-) create mode 100644 src/librustdoc/visit_lib.rs create mode 100644 src/test/auxiliary/rustdoc-nonreachable-impls.rs create mode 100644 src/test/rustdoc/inline_cross/issue-31948-1.rs create mode 100644 src/test/rustdoc/inline_cross/issue-31948-2.rs create mode 100644 src/test/rustdoc/inline_cross/issue-31948.rs diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index d3db0804c2422..c1a8f747de14d 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -113,6 +113,7 @@ pub enum InlinedItemRef<'a> { /// LOCAL_CRATE in their DefId. pub const LOCAL_CRATE: ast::CrateNum = 0; +#[derive(Copy, Clone)] pub struct ChildItem { pub def: DefLike, pub name: ast::Name, diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 6cca602466a3d..345eb5cdee444 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -28,7 +28,7 @@ use rustc_const_eval::lookup_const_by_id; use core::DocContext; use doctree; -use clean::{self, Attributes, GetDefId}; +use clean::{self, GetDefId}; use super::{Clean, ToSource}; @@ -227,6 +227,15 @@ fn build_type(cx: &DocContext, tcx: &TyCtxt, did: DefId) -> clean::ItemEnum { }, false) } +fn is_item_doc_reachable(cx: &DocContext, did: DefId) -> bool { + use ::visit_lib::LibEmbargoVisitor; + + if cx.analyzed_crates.borrow_mut().insert(did.krate) { + LibEmbargoVisitor::new(cx).visit_lib(did.krate); + } + cx.access_levels.borrow().is_public(did) +} + pub fn build_impls(cx: &DocContext, tcx: &TyCtxt, did: DefId) -> Vec { @@ -260,11 +269,6 @@ pub fn build_impls(cx: &DocContext, match def { cstore::DlImpl(did) => build_impl(cx, tcx, did, impls), cstore::DlDef(Def::Mod(did)) => { - // Don't recurse if this is a #[doc(hidden)] module - if load_attrs(cx, tcx, did).list("doc").has_word("hidden") { - return; - } - for item in tcx.sess.cstore.item_children(did) { populate_impls(cx, tcx, item.def, impls) } @@ -301,10 +305,11 @@ pub fn build_impl(cx: &DocContext, let attrs = load_attrs(cx, tcx, did); let associated_trait = tcx.impl_trait_ref(did); - if let Some(ref t) = associated_trait { - // If this is an impl for a #[doc(hidden)] trait, be sure to not inline - let trait_attrs = load_attrs(cx, tcx, t.def_id); - if trait_attrs.list("doc").has_word("hidden") { + + // Only inline impl if the implemented trait is + // reachable in rustdoc generated documentation + if let Some(traitref) = associated_trait { + if !is_item_doc_reachable(cx, traitref.def_id) { return } } @@ -330,6 +335,17 @@ pub fn build_impl(cx: &DocContext, }); } + let ty = tcx.lookup_item_type(did); + let for_ = ty.ty.clean(cx); + + // Only inline impl if the implementing type is + // reachable in rustdoc generated documentation + if let Some(did) = for_.def_id() { + if !is_item_doc_reachable(cx, did) { + return + } + } + let predicates = tcx.lookup_predicates(did); let trait_items = tcx.sess.cstore.impl_items(did) .iter() @@ -412,7 +428,6 @@ pub fn build_impl(cx: &DocContext, } }).collect::>(); let polarity = tcx.trait_impl_polarity(did); - let ty = tcx.lookup_item_type(did); let trait_ = associated_trait.clean(cx).map(|bound| { match bound { clean::TraitBound(polyt, _) => polyt.trait_, @@ -436,7 +451,7 @@ pub fn build_impl(cx: &DocContext, derived: clean::detect_derived(&attrs), provided_trait_methods: provided, trait_: trait_, - for_: ty.ty.clean(cx), + for_: for_, generics: (&ty.generics, &predicates, subst::TypeSpace).clean(cx), items: trait_items, polarity: polarity.map(|p| { p.clean(cx) }), diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index bba394bb90dc6..019f7401b5450 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -57,7 +57,7 @@ use doctree; use visit_ast; use html::item_type::ItemType; -mod inline; +pub mod inline; mod simplify; // extract the stability index for a node from tcx, if possible diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index c9773ebccb4ee..3cb76ca2339ef 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -14,6 +14,7 @@ use rustc_driver::{driver, target_features, abort_on_err}; use rustc::dep_graph::DepGraph; use rustc::session::{self, config}; use rustc::hir::def_id::DefId; +use rustc::middle::cstore::LOCAL_CRATE; use rustc::middle::privacy::AccessLevels; use rustc::ty::{self, TyCtxt}; use rustc::hir::map as hir_map; @@ -29,7 +30,7 @@ use syntax::feature_gate::UnstableFeatures; use syntax::parse::token; use std::cell::{RefCell, Cell}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::rc::Rc; use visit_ast::RustdocVisitor; @@ -53,12 +54,17 @@ pub struct DocContext<'a, 'tcx: 'a> { pub maybe_typed: MaybeTyped<'a, 'tcx>, pub input: Input, pub all_crate_impls: RefCell>>, - // Later on moved into `clean::Crate` + pub deref_trait_did: Cell>, + /// Crates which have already been processed for `Self.access_levels` + pub analyzed_crates: RefCell>, + // Note that external items for which `doc(hidden)` applies to are shown as + // non-reachable while local items aren't. This is because we're reusing + // the access levels from crateanalysis. + /// Later on moved into `clean::Crate` pub access_levels: RefCell>, - // Later on moved into `html::render::CACHE_KEY` + /// Later on moved into `html::render::CACHE_KEY` pub renderinfo: RefCell, - pub deref_trait_did: Cell>, - // Later on moved through `clean::Crate` into `html::render::CACHE_KEY` + /// Later on moved through `clean::Crate` into `html::render::CACHE_KEY` pub external_traits: RefCell>, } @@ -166,6 +172,8 @@ pub fn run_core(search_paths: SearchPaths, .map(|(k, v)| (tcx.map.local_def_id(k), v)) .collect() }; + let mut analyzed_crates = HashSet::new(); + analyzed_crates.insert(LOCAL_CRATE); let ctxt = DocContext { map: &tcx.map, @@ -173,6 +181,7 @@ pub fn run_core(search_paths: SearchPaths, input: input, all_crate_impls: RefCell::new(HashMap::new()), deref_trait_did: Cell::new(None), + analyzed_crates: RefCell::new(analyzed_crates), access_levels: RefCell::new(access_levels), external_traits: RefCell::new(HashMap::new()), renderinfo: RefCell::new(Default::default()), diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 2d59747d00c5b..c83029107c30c 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -253,6 +253,9 @@ pub struct Cache { parent_is_trait_impl: bool, search_index: Vec, stripped_mod: bool, + // Note that external items for which `doc(hidden)` applies to are shown as + // non-reachable while local items aren't. This is because we're reusing + // the access levels from crateanalysis. access_levels: Arc>, deref_trait_did: Option, diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 0d80e70281603..86aad10e02fc7 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -80,6 +80,7 @@ pub mod markdown; pub mod passes; pub mod plugins; pub mod visit_ast; +pub mod visit_lib; pub mod test; mod flock; diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 5a7050fb42f30..df3c81e7e3bd6 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -9,7 +9,7 @@ // except according to those terms. use std::cell::{RefCell, Cell}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::env; use std::ffi::OsString; use std::io::prelude::*; @@ -111,6 +111,7 @@ pub fn run(input: &str, external_traits: RefCell::new(HashMap::new()), all_crate_impls: RefCell::new(HashMap::new()), deref_trait_did: Cell::new(None), + analyzed_crates: RefCell::new(HashSet::new()), access_levels: Default::default(), renderinfo: Default::default(), }; diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index a406c843b82e4..2bce8f4c2a127 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -21,12 +21,14 @@ use syntax::attr::AttrMetaMethods; use syntax::codemap::Span; use rustc::hir::map as hir_map; +use rustc::hir::def::Def; use rustc::middle::stability; +use rustc::middle::privacy::AccessLevel; use rustc::hir; use core; -use clean::{Clean, Attributes}; +use clean::{self, Clean, Attributes}; use doctree::*; // looks to me like the first two of these are actually @@ -240,16 +242,40 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { Some(tcx) => tcx, None => return false }; - let def = tcx.def_map.borrow()[&id].def_id(); - let def_node_id = match tcx.map.as_local_node_id(def) { - Some(n) => n, None => return false - }; + let def = tcx.def_map.borrow()[&id]; + let def_did = def.def_id(); let use_attrs = tcx.map.attrs(id).clean(self.cx); + let is_no_inline = use_attrs.list("doc").has_word("no_inline"); - let is_private = !self.cx.access_levels.borrow().is_public(def); + // For cross-crate impl inlining we need to know whether items are + // reachable in documentation - a previously nonreachable item can be + // made reachable by cross-crate inlining which we're checking here. + // (this is done here because we need to know this upfront) + if !def.def_id().is_local() && !is_no_inline { + let attrs = clean::inline::load_attrs(self.cx, tcx, def_did); + let self_is_hidden = attrs.list("doc").has_word("hidden"); + match def.base_def { + Def::Trait(did) | + Def::Struct(did) | + Def::Enum(did) | + Def::TyAlias(did) if !self_is_hidden => { + self.cx.access_levels.borrow_mut().map.insert(did, AccessLevel::Public); + }, + Def::Mod(did) => if !self_is_hidden { + ::visit_lib::LibEmbargoVisitor::new(self.cx).visit_mod(did); + }, + _ => {}, + } + return false + } + + let def_node_id = match tcx.map.as_local_node_id(def_did) { + Some(n) => n, None => return false + }; + + let is_private = !self.cx.access_levels.borrow().is_public(def_did); let is_hidden = inherits_doc_hidden(self.cx, def_node_id); - let is_no_inline = use_attrs.list("doc").has_word("no_inline"); // Only inline if requested or if the item would otherwise be stripped if (!please_inline && !is_private && !is_hidden) || is_no_inline { diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs new file mode 100644 index 0000000000000..54eae6e237eae --- /dev/null +++ b/src/librustdoc/visit_lib.rs @@ -0,0 +1,104 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::middle::cstore::{CrateStore, ChildItem, DefLike}; +use rustc::middle::privacy::{AccessLevels, AccessLevel}; +use rustc::hir::def::Def; +use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId}; +use rustc::ty::Visibility; +use syntax::ast; + +use std::cell::RefMut; + +use clean::{Attributes, Clean}; + +// FIXME: since this is only used for cross-crate impl inlining this only +// handles traits and items for which traits can be implemented + +/// Similar to `librustc_privacy::EmbargoVisitor`, but also takes +/// specific rustdoc annotations into account (i.e. `doc(hidden)`) +pub struct LibEmbargoVisitor<'a, 'b: 'a, 'tcx: 'b> { + cx: &'a ::core::DocContext<'b, 'tcx>, + cstore: &'a CrateStore<'tcx>, + // Accessibility levels for reachable nodes + access_levels: RefMut<'a, AccessLevels>, + // Previous accessibility level, None means unreachable + prev_level: Option, +} + +impl<'a, 'b, 'tcx> LibEmbargoVisitor<'a, 'b, 'tcx> { + pub fn new(cx: &'a ::core::DocContext<'b, 'tcx>) -> LibEmbargoVisitor<'a, 'b, 'tcx> { + LibEmbargoVisitor { + cx: cx, + cstore: &*cx.sess().cstore, + access_levels: cx.access_levels.borrow_mut(), + prev_level: Some(AccessLevel::Public), + } + } + + pub fn visit_lib(&mut self, cnum: ast::CrateNum) { + let did = DefId { krate: cnum, index: CRATE_DEF_INDEX }; + self.visit_mod(did); + } + + // Updates node level and returns the updated level + fn update(&mut self, did: DefId, level: Option) -> Option { + let attrs: Vec<_> = self.cx.tcx().get_attrs(did).iter() + .map(|a| a.clean(self.cx)) + .collect(); + let is_hidden = attrs.list("doc").has_word("hidden"); + + let old_level = self.access_levels.map.get(&did).cloned(); + // Accessibility levels can only grow + if level > old_level && !is_hidden { + self.access_levels.map.insert(did, level.unwrap()); + level + } else { + old_level + } + } + + pub fn visit_mod(&mut self, did: DefId) { + for item in self.cstore.item_children(did) { + if let DefLike::DlDef(def) = item.def { + match def { + Def::Trait(did) | + Def::Struct(did) | + Def::Mod(did) | + Def::Enum(did) | + Def::TyAlias(did) => self.visit_item(did, item), + _ => {} + } + } + } + } + + fn visit_item(&mut self, did: DefId, item: ChildItem) { + let inherited_item_level = match item.def { + DefLike::DlImpl(..) | DefLike::DlField => unreachable!(), + DefLike::DlDef(def) => { + match def { + Def::ForeignMod(..) => self.prev_level, + _ => if item.vis == Visibility::Public { self.prev_level } else { None } + } + } + }; + + let item_level = self.update(did, inherited_item_level); + + if let DefLike::DlDef(Def::Mod(did)) = item.def { + let orig_level = self.prev_level; + + self.prev_level = item_level; + self.visit_mod(did); + self.prev_level = orig_level; + } + } +} diff --git a/src/test/auxiliary/rustdoc-nonreachable-impls.rs b/src/test/auxiliary/rustdoc-nonreachable-impls.rs new file mode 100644 index 0000000000000..22a311d579745 --- /dev/null +++ b/src/test/auxiliary/rustdoc-nonreachable-impls.rs @@ -0,0 +1,44 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub struct Foo; + +pub trait Woof {} +pub trait Bark {} + +mod private { + // should be shown + impl ::Woof for ::Foo {} + + pub trait Bar {} + pub struct Wibble; + + // these should not be shown + impl Bar for ::Foo {} + impl Bar for Wibble {} + impl ::Bark for Wibble {} + impl ::Woof for Wibble {} +} + +#[doc(hidden)] +pub mod hidden { + // should be shown + impl ::Bark for ::Foo {} + + pub trait Qux {} + pub struct Wobble; + + + // these should only be shown if they're reexported correctly + impl Qux for ::Foo {} + impl Qux for Wobble {} + impl ::Bark for Wobble {} + impl ::Woof for Wobble {} +} diff --git a/src/test/rustdoc/inline_cross/issue-31948-1.rs b/src/test/rustdoc/inline_cross/issue-31948-1.rs new file mode 100644 index 0000000000000..e953b66fce15d --- /dev/null +++ b/src/test/rustdoc/inline_cross/issue-31948-1.rs @@ -0,0 +1,37 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:rustdoc-nonreachable-impls.rs +// build-aux-docs +// ignore-cross-compile + +extern crate rustdoc_nonreachable_impls; + +// @has issue_31948_1/struct.Wobble.html +// @has - '//*[@class="impl"]//code' 'Bark for' +// @has - '//*[@class="impl"]//code' 'Woof for' +// @!has - '//*[@class="impl"]//code' 'Bar for' +// @!has - '//*[@class="impl"]//code' 'Qux for' +pub use rustdoc_nonreachable_impls::hidden::Wobble; + +// @has issue_31948_1/trait.Bark.html +// FIXME(33025): has - '//code' 'for Foo' +// @has - '//code' 'for Wobble' +// @!has - '//code' 'for Wibble' +pub use rustdoc_nonreachable_impls::Bark; + +// @has issue_31948_1/trait.Woof.html +// FIXME(33025): has - '//code' 'for Foo' +// @has - '//code' 'for Wobble' +// @!has - '//code' 'for Wibble' +pub use rustdoc_nonreachable_impls::Woof; + +// @!has issue_31948_1/trait.Bar.html +// @!has issue_31948_1/trait.Qux.html diff --git a/src/test/rustdoc/inline_cross/issue-31948-2.rs b/src/test/rustdoc/inline_cross/issue-31948-2.rs new file mode 100644 index 0000000000000..0423fa1de3b4c --- /dev/null +++ b/src/test/rustdoc/inline_cross/issue-31948-2.rs @@ -0,0 +1,31 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:rustdoc-nonreachable-impls.rs +// build-aux-docs +// ignore-cross-compile + +extern crate rustdoc_nonreachable_impls; + +// @has issue_31948_2/struct.Wobble.html +// @has - '//*[@class="impl"]//code' 'Qux for' +// @has - '//*[@class="impl"]//code' 'Bark for' +// @has - '//*[@class="impl"]//code' 'Woof for' +// @!has - '//*[@class="impl"]//code' 'Bar for' +pub use rustdoc_nonreachable_impls::hidden::Wobble; + +// @has issue_31948_2/trait.Qux.html +// FIXME(33025): has - '//code' 'for Foo' +// @has - '//code' 'for Wobble' +pub use rustdoc_nonreachable_impls::hidden::Qux; + +// @!has issue_31948_2/trait.Bar.html +// @!has issue_31948_2/trait.Woof.html +// @!has issue_31948_2/trait.Bark.html diff --git a/src/test/rustdoc/inline_cross/issue-31948.rs b/src/test/rustdoc/inline_cross/issue-31948.rs new file mode 100644 index 0000000000000..073e8eb50fb25 --- /dev/null +++ b/src/test/rustdoc/inline_cross/issue-31948.rs @@ -0,0 +1,39 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:rustdoc-nonreachable-impls.rs +// build-aux-docs +// ignore-cross-compile + +extern crate rustdoc_nonreachable_impls; + +// @has issue_31948/struct.Foo.html +// @has - '//*[@class="impl"]//code' 'Bark for' +// @has - '//*[@class="impl"]//code' 'Woof for' +// @!has - '//*[@class="impl"]//code' 'Bar for' +// @!has - '//*[@class="impl"]//code' 'Qux for' +pub use rustdoc_nonreachable_impls::Foo; + +// @has issue_31948/trait.Bark.html +// @has - '//code' 'for Foo' +// @!has - '//code' 'for Wibble' +// @!has - '//code' 'for Wobble' +pub use rustdoc_nonreachable_impls::Bark; + +// @has issue_31948/trait.Woof.html +// @has - '//code' 'for Foo' +// @!has - '//code' 'for Wibble' +// @!has - '//code' 'for Wobble' +pub use rustdoc_nonreachable_impls::Woof; + +// @!has issue_31948/trait.Bar.html +// @!has issue_31948/trait.Qux.html +// @!has issue_31948/struct.Wibble.html +// @!has issue_31948/struct.Wobble.html From 77b409a674f62e3be13ec9339b5c8272c9b5c0a9 Mon Sep 17 00:00:00 2001 From: mitaa Date: Sun, 17 Apr 2016 08:54:48 +0200 Subject: [PATCH 3/3] Reachability check cross-crate links --- src/librustdoc/clean/inline.rs | 15 +++---------- src/librustdoc/clean/mod.rs | 5 +++++ src/librustdoc/core.rs | 20 ++++++++++------- src/librustdoc/html/format.rs | 4 ++++ src/librustdoc/html/render.rs | 9 ++++---- src/librustdoc/test.rs | 3 +-- src/librustdoc/visit_lib.rs | 12 ++++++---- src/test/auxiliary/rustdoc-hidden-sig.rs | 22 +++++++++++++++++++ src/test/rustdoc/inline_cross/issue-28480.rs | 23 ++++++++++++++++++++ 9 files changed, 83 insertions(+), 30 deletions(-) create mode 100644 src/test/auxiliary/rustdoc-hidden-sig.rs create mode 100644 src/test/rustdoc/inline_cross/issue-28480.rs diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 345eb5cdee444..a178f071f6cbb 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -26,7 +26,7 @@ use rustc::middle::stability; use rustc_const_eval::lookup_const_by_id; -use core::DocContext; +use core::{DocContext, DocAccessLevels}; use doctree; use clean::{self, GetDefId}; @@ -227,15 +227,6 @@ fn build_type(cx: &DocContext, tcx: &TyCtxt, did: DefId) -> clean::ItemEnum { }, false) } -fn is_item_doc_reachable(cx: &DocContext, did: DefId) -> bool { - use ::visit_lib::LibEmbargoVisitor; - - if cx.analyzed_crates.borrow_mut().insert(did.krate) { - LibEmbargoVisitor::new(cx).visit_lib(did.krate); - } - cx.access_levels.borrow().is_public(did) -} - pub fn build_impls(cx: &DocContext, tcx: &TyCtxt, did: DefId) -> Vec { @@ -309,7 +300,7 @@ pub fn build_impl(cx: &DocContext, // Only inline impl if the implemented trait is // reachable in rustdoc generated documentation if let Some(traitref) = associated_trait { - if !is_item_doc_reachable(cx, traitref.def_id) { + if !cx.access_levels.borrow().is_doc_reachable(traitref.def_id) { return } } @@ -341,7 +332,7 @@ pub fn build_impl(cx: &DocContext, // Only inline impl if the implementing type is // reachable in rustdoc generated documentation if let Some(did) = for_.def_id() { - if !is_item_doc_reachable(cx, did) { + if !cx.access_levels.borrow().is_doc_reachable(did) { return } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 019f7401b5450..070b3ca908917 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -133,6 +133,7 @@ struct CrateNum(ast::CrateNum); impl<'a, 'tcx> Clean for visit_ast::RustdocVisitor<'a, 'tcx> { fn clean(&self, cx: &DocContext) -> Crate { use rustc::session::config::Input; + use ::visit_lib::LibEmbargoVisitor; if let Some(t) = cx.tcx_opt() { cx.deref_trait_did.set(t.lang_items.deref_trait()); @@ -142,6 +143,10 @@ impl<'a, 'tcx> Clean for visit_ast::RustdocVisitor<'a, 'tcx> { let mut externs = Vec::new(); for cnum in cx.sess().cstore.crates() { externs.push((cnum, CrateNum(cnum).clean(cx))); + if cx.tcx_opt().is_some() { + // Analyze doc-reachability for extern items + LibEmbargoVisitor::new(cx).visit_lib(cnum); + } } externs.sort_by(|&(a, _), &(b, _)| a.cmp(&b)); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 3cb76ca2339ef..b7c60b8a524c0 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -14,7 +14,6 @@ use rustc_driver::{driver, target_features, abort_on_err}; use rustc::dep_graph::DepGraph; use rustc::session::{self, config}; use rustc::hir::def_id::DefId; -use rustc::middle::cstore::LOCAL_CRATE; use rustc::middle::privacy::AccessLevels; use rustc::ty::{self, TyCtxt}; use rustc::hir::map as hir_map; @@ -30,7 +29,7 @@ use syntax::feature_gate::UnstableFeatures; use syntax::parse::token; use std::cell::{RefCell, Cell}; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::rc::Rc; use visit_ast::RustdocVisitor; @@ -47,6 +46,7 @@ pub enum MaybeTyped<'a, 'tcx: 'a> { NotTyped(&'a session::Session) } +pub type Externs = HashMap>; pub type ExternalPaths = HashMap, clean::TypeKind)>; pub struct DocContext<'a, 'tcx: 'a> { @@ -55,8 +55,6 @@ pub struct DocContext<'a, 'tcx: 'a> { pub input: Input, pub all_crate_impls: RefCell>>, pub deref_trait_did: Cell>, - /// Crates which have already been processed for `Self.access_levels` - pub analyzed_crates: RefCell>, // Note that external items for which `doc(hidden)` applies to are shown as // non-reachable while local items aren't. This is because we're reusing // the access levels from crateanalysis. @@ -89,7 +87,16 @@ impl<'b, 'tcx> DocContext<'b, 'tcx> { } } -pub type Externs = HashMap>; +pub trait DocAccessLevels { + fn is_doc_reachable(&self, DefId) -> bool; +} + +impl DocAccessLevels for AccessLevels { + fn is_doc_reachable(&self, did: DefId) -> bool { + self.is_public(did) + } +} + pub fn run_core(search_paths: SearchPaths, cfgs: Vec, @@ -172,8 +179,6 @@ pub fn run_core(search_paths: SearchPaths, .map(|(k, v)| (tcx.map.local_def_id(k), v)) .collect() }; - let mut analyzed_crates = HashSet::new(); - analyzed_crates.insert(LOCAL_CRATE); let ctxt = DocContext { map: &tcx.map, @@ -181,7 +186,6 @@ pub fn run_core(search_paths: SearchPaths, input: input, all_crate_impls: RefCell::new(HashMap::new()), deref_trait_did: Cell::new(None), - analyzed_crates: RefCell::new(analyzed_crates), access_levels: RefCell::new(access_levels), external_traits: RefCell::new(HashMap::new()), renderinfo: RefCell::new(Default::default()), diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index ce20ad05acb0e..39be00b61cd66 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -24,6 +24,7 @@ use syntax::abi::Abi; use rustc::hir; use clean; +use core::DocAccessLevels; use html::item_type::ItemType; use html::render; use html::render::{cache, CURRENT_LOCATION_KEY}; @@ -298,6 +299,9 @@ pub fn href(did: DefId) -> Option<(String, ItemType, Vec)> { let mut url = if did.is_local() || cache.inlined.contains(&did) { repeat("../").take(loc.len()).collect::() } else { + if !cache.access_levels.is_doc_reachable(did) { + return None + } match cache.extern_locations[&did.krate] { (_, render::Remote(ref s)) => s.to_string(), (_, render::Local) => repeat("../").take(loc.len()).collect(), diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index c83029107c30c..7dcbb08a4ecf8 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -246,6 +246,11 @@ pub struct Cache { /// Set of definitions which have been inlined from external crates. pub inlined: HashSet, + // Note that external items for which `doc(hidden)` applies to are shown as + // non-reachable while local items aren't. This is because we're reusing + // the access levels from crateanalysis. + pub access_levels: Arc>, + // Private fields only used when initially crawling a crate to build a cache stack: Vec, @@ -253,10 +258,6 @@ pub struct Cache { parent_is_trait_impl: bool, search_index: Vec, stripped_mod: bool, - // Note that external items for which `doc(hidden)` applies to are shown as - // non-reachable while local items aren't. This is because we're reusing - // the access levels from crateanalysis. - access_levels: Arc>, deref_trait_did: Option, // In rare case where a structure is defined in one module but implemented diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index df3c81e7e3bd6..5a7050fb42f30 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -9,7 +9,7 @@ // except according to those terms. use std::cell::{RefCell, Cell}; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::env; use std::ffi::OsString; use std::io::prelude::*; @@ -111,7 +111,6 @@ pub fn run(input: &str, external_traits: RefCell::new(HashMap::new()), all_crate_impls: RefCell::new(HashMap::new()), deref_trait_did: Cell::new(None), - analyzed_crates: RefCell::new(HashSet::new()), access_levels: Default::default(), renderinfo: Default::default(), }; diff --git a/src/librustdoc/visit_lib.rs b/src/librustdoc/visit_lib.rs index 54eae6e237eae..f56f4c306f90a 100644 --- a/src/librustdoc/visit_lib.rs +++ b/src/librustdoc/visit_lib.rs @@ -19,8 +19,7 @@ use std::cell::RefMut; use clean::{Attributes, Clean}; -// FIXME: since this is only used for cross-crate impl inlining this only -// handles traits and items for which traits can be implemented +// FIXME: this may not be exhaustive, but is sufficient for rustdocs current uses /// Similar to `librustc_privacy::EmbargoVisitor`, but also takes /// specific rustdoc annotations into account (i.e. `doc(hidden)`) @@ -69,11 +68,16 @@ impl<'a, 'b, 'tcx> LibEmbargoVisitor<'a, 'b, 'tcx> { for item in self.cstore.item_children(did) { if let DefLike::DlDef(def) = item.def { match def { + Def::Mod(did) | + Def::ForeignMod(did) | Def::Trait(did) | Def::Struct(did) | - Def::Mod(did) | Def::Enum(did) | - Def::TyAlias(did) => self.visit_item(did, item), + Def::TyAlias(did) | + Def::Fn(did) | + Def::Method(did) | + Def::Static(did, _) | + Def::Const(did) => self.visit_item(did, item), _ => {} } } diff --git a/src/test/auxiliary/rustdoc-hidden-sig.rs b/src/test/auxiliary/rustdoc-hidden-sig.rs new file mode 100644 index 0000000000000..e2bc153ce0d85 --- /dev/null +++ b/src/test/auxiliary/rustdoc-hidden-sig.rs @@ -0,0 +1,22 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub struct Bar; + +impl Bar { + pub fn bar(_: u8) -> hidden::Hidden { + hidden::Hidden + } +} + +#[doc(hidden)] +pub mod hidden { + pub struct Hidden; +} diff --git a/src/test/rustdoc/inline_cross/issue-28480.rs b/src/test/rustdoc/inline_cross/issue-28480.rs new file mode 100644 index 0000000000000..6b5c5b2014734 --- /dev/null +++ b/src/test/rustdoc/inline_cross/issue-28480.rs @@ -0,0 +1,23 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// aux-build:rustdoc-hidden-sig.rs +// build-aux-docs +// ignore-cross-compile + +// @has rustdoc_hidden_sig/struct.Bar.html +// @!has - '//a/@title' 'Hidden' +// @has - '//a' 'u8' +extern crate rustdoc_hidden_sig; + +// @has issue_28480/struct.Bar.html +// @!has - '//a/@title' 'Hidden' +// @has - '//a' 'u8' +pub use rustdoc_hidden_sig::Bar;