diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 345eb5cdee4..a178f071f6c 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 019f7401b54..070b3ca9089 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 3cb76ca2339..b7c60b8a524 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 ce20ad05acb..39be00b61cd 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 c83029107c3..7dcbb08a4ec 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 df3c81e7e3b..5a7050fb42f 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 54eae6e237e..f56f4c306f9 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 00000000000..e2bc153ce0d --- /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 00000000000..6b5c5b20147 --- /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;