diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 7a3adc157c26..4c03abac9e86 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -26,6 +26,7 @@ use rustc::middle::stability; use rustc_front::hir; use core; +use clean::{Clean, Attributes}; use doctree::*; // looks to me like the first two of these are actually @@ -182,7 +183,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { please_inline: bool) -> Option { match path { hir::ViewPathSimple(dst, base) => { - if self.resolve_id(id, Some(dst), false, om, please_inline) { + if self.maybe_inline_local(id, Some(dst), false, om, please_inline) { None } else { Some(hir::ViewPathSimple(dst, base)) @@ -190,7 +191,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } hir::ViewPathList(p, paths) => { let mine = paths.into_iter().filter(|path| { - !self.resolve_id(path.node.id(), None, false, om, + !self.maybe_inline_local(path.node.id(), None, false, om, please_inline) }).collect::>(); @@ -201,9 +202,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } } - // these are feature gated anyway hir::ViewPathGlob(base) => { - if self.resolve_id(id, None, true, om, please_inline) { + if self.maybe_inline_local(id, None, true, om, please_inline) { None } else { Some(hir::ViewPathGlob(base)) @@ -213,8 +213,32 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { } - fn resolve_id(&mut self, id: ast::NodeId, renamed: Option, + /// Tries to resolve the target of a `pub use` statement and inlines the + /// target if it is defined locally and would not be documented otherwise, + /// or when it is specifically requested with `please_inline`. + /// (the latter is the case when the import is marked `doc(inline)`) + /// + /// Cross-crate inlining occurs later on during crate cleaning + /// and follows different rules. + /// + /// Returns true if the target has been inlined. + fn maybe_inline_local(&mut self, id: ast::NodeId, renamed: Option, glob: bool, om: &mut Module, please_inline: bool) -> bool { + + fn inherits_doc_hidden(cx: &core::DocContext, mut node: ast::NodeId) -> bool { + while let Some(id) = cx.map.get_enclosing_scope(node) { + node = id; + let attrs = cx.map.attrs(node).clean(cx); + if attrs.list_def("doc").has_word("hidden") { + return true; + } + if node == ast::CRATE_NODE_ID { + break; + } + } + false + } + let tcx = match self.cx.tcx_opt() { Some(tcx) => tcx, None => return false @@ -226,9 +250,15 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let analysis = match self.analysis { Some(analysis) => analysis, None => return false }; - if !please_inline && analysis.access_levels.is_public(def) { + + let is_private = !analysis.access_levels.is_public(def); + let is_hidden = inherits_doc_hidden(self.cx, def_node_id); + + // Only inline if requested or if the item would otherwise be stripped + if !please_inline && !is_private && !is_hidden { return false } + if !self.view_item_stack.insert(def_node_id) { return false } let ret = match tcx.map.get(def_node_id) { diff --git a/src/test/rustdoc/inline_local/issue-28537.rs b/src/test/rustdoc/inline_local/issue-28537.rs new file mode 100644 index 000000000000..b38e104b7b4e --- /dev/null +++ b/src/test/rustdoc/inline_local/issue-28537.rs @@ -0,0 +1,27 @@ +// 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. + +#[doc(hidden)] +pub mod foo { + pub struct Foo; +} + +mod bar { + pub use self::bar::Bar; + mod bar { + pub struct Bar; + } +} + +// @has issue_28537/struct.Foo.html +pub use foo::Foo; + +// @has issue_28537/struct.Bar.html +pub use self::bar::Bar;