From 062095ab5e9a1b37608d9b4d809c17aa6af998ad Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Sun, 24 Oct 2021 14:50:38 -0700 Subject: [PATCH 1/2] Factor our VisibilityScope from Visibility Visibility::print_with_space and Visibility::to_src_with_space had some duplicated code. This extracts the code into a shared enum, with a Display impl that will make reuse elsewhere easier. --- src/librustdoc/html/format.rs | 131 ++++++++++++++++++---------------- 1 file changed, 71 insertions(+), 60 deletions(-) diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index f2751947c7eb9..f9559bb19658b 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1221,50 +1221,80 @@ impl clean::FnDecl { } } +pub(crate) enum VisibilityScope { + Inherited, + Crate, + Super, + Path(Vec), +} + +impl VisibilityScope { + crate fn new<'tcx>(vis_did: DefId, item_did: DefId, tcx: TyCtxt<'tcx>) -> VisibilityScope { + // FIXME(camelid): This may not work correctly if `item_did` is a module. + // However, rustdoc currently never displays a module's + // visibility, so it shouldn't matter. + let parent_module = find_nearest_parent_module(tcx, item_did); + + if vis_did.index == CRATE_DEF_INDEX { + VisibilityScope::Crate + } else if parent_module == Some(vis_did) { + // `pub(in foo)` where `foo` is the parent module + // is the same as no visibility modifier + VisibilityScope::Inherited + } else if parent_module.map(|parent| find_nearest_parent_module(tcx, parent)).flatten() + == Some(vis_did) + { + VisibilityScope::Super + } else { + let path = tcx.def_path(vis_did); + debug!("path={:?}", path); + let mut components: Vec = vec![]; + for seg in &path.data { + components.push(seg.data.get_opt_name().unwrap().to_string()); + } + VisibilityScope::Path(components) + } + } +} + +impl fmt::Display for VisibilityScope { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + VisibilityScope::Inherited => f.write_str(""), + VisibilityScope::Crate => f.write_str("crate"), + VisibilityScope::Super => f.write_str("super"), + VisibilityScope::Path(components) => { + f.write_str("in ")?; + for (i, c) in components.iter().enumerate() { + if i > 0 { + f.write_str("::")?; + } + f.write_str(c)?; + } + Ok(()) + } + } + } +} + impl clean::Visibility { crate fn print_with_space<'a, 'tcx: 'a>( self, item_did: ItemId, cx: &'a Context<'tcx>, ) -> impl fmt::Display + 'a + Captures<'tcx> { - let to_print = match self { - clean::Public => "pub ".to_owned(), - clean::Inherited => String::new(), + display_fn(move |f| match self { + clean::Public => write!(f, "pub "), + clean::Inherited => write!(f, ""), clean::Visibility::Restricted(vis_did) => { - // FIXME(camelid): This may not work correctly if `item_did` is a module. - // However, rustdoc currently never displays a module's - // visibility, so it shouldn't matter. - let parent_module = find_nearest_parent_module(cx.tcx(), item_did.expect_def_id()); - - if vis_did.index == CRATE_DEF_INDEX { - "pub(crate) ".to_owned() - } else if parent_module == Some(vis_did) { - // `pub(in foo)` where `foo` is the parent module - // is the same as no visibility modifier - String::new() - } else if parent_module - .map(|parent| find_nearest_parent_module(cx.tcx(), parent)) - .flatten() - == Some(vis_did) - { - "pub(super) ".to_owned() - } else { - let path = cx.tcx().def_path(vis_did); - debug!("path={:?}", path); - // modified from `resolved_path()` to work with `DefPathData` - let last_name = path.data.last().unwrap().data.get_opt_name().unwrap(); - let anchor = anchor(vis_did, &last_name.as_str(), cx).to_string(); - - let mut s = "pub(in ".to_owned(); - for seg in &path.data[..path.data.len() - 1] { - s.push_str(&format!("{}::", seg.data.get_opt_name().unwrap())); - } - s.push_str(&format!("{}) ", anchor)); - s + let mut scope = VisibilityScope::new(vis_did, item_did.expect_def_id(), cx.tcx()); + if let VisibilityScope::Path(ref mut components) = scope { + let last_name = components.pop().unwrap(); + components.push(anchor(vis_did, &last_name.as_str(), cx).to_string()); } + write!(f, "pub({}) ", scope) } - }; - display_fn(move |f| f.write_str(&to_print)) + }) } /// This function is the same as print_with_space, except that it renders no links. @@ -1275,33 +1305,14 @@ impl clean::Visibility { tcx: TyCtxt<'tcx>, item_did: DefId, ) -> impl fmt::Display + 'a + Captures<'tcx> { - let to_print = match self { - clean::Public => "pub ".to_owned(), - clean::Inherited => String::new(), + display_fn(move |f| match self { + clean::Public => write!(f, "pub "), + clean::Inherited => write!(f, ""), clean::Visibility::Restricted(vis_did) => { - // FIXME(camelid): This may not work correctly if `item_did` is a module. - // However, rustdoc currently never displays a module's - // visibility, so it shouldn't matter. - let parent_module = find_nearest_parent_module(tcx, item_did); - - if vis_did.index == CRATE_DEF_INDEX { - "pub(crate) ".to_owned() - } else if parent_module == Some(vis_did) { - // `pub(in foo)` where `foo` is the parent module - // is the same as no visibility modifier - String::new() - } else if parent_module - .map(|parent| find_nearest_parent_module(tcx, parent)) - .flatten() - == Some(vis_did) - { - "pub(super) ".to_owned() - } else { - format!("pub(in {}) ", tcx.def_path_str(vis_did)) - } + let scope = VisibilityScope::new(vis_did, item_did, tcx); + write!(f, "pub({}) ", scope) } - }; - display_fn(move |f| f.write_str(&to_print)) + }) } } From 48de0c8f414f1df0fa8ce27101cabe4594fc5a63 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Sun, 24 Oct 2021 14:51:00 -0700 Subject: [PATCH 2/2] Add a visibility info tag, and remove pub For most documentation, the `pub` in front of each method is redundant: you know the method is public because you are reading the documentation. However, when running with `--document-private`, it's important to see which items are public and which are private. This adds an item-info tag for private items showing their visibility, and removes the `pub` from methods and fields. This doesn't currently touch the summary code block at the top of the page, which will still have `pub` or `pub(crate)` or whatever is appropriate. --- src/librustdoc/html/render/mod.rs | 56 ++++- src/librustdoc/html/static/css/rustdoc.css | 9 +- .../html/static/css/themes/light.css | 1 + src/test/rustdoc/document-private.rs | 213 ++++++++++++++++++ 4 files changed, 271 insertions(+), 8 deletions(-) create mode 100644 src/test/rustdoc/document-private.rs diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 69c5c2c4abc2a..f87ece0cbd5a1 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -66,7 +66,7 @@ use crate::formats::{AssocItemRender, Impl, RenderMode}; use crate::html::escape::Escape; use crate::html::format::{ href, print_abi_with_space, print_constness_with_space, print_default_space, - print_generic_bounds, print_where_clause, Buffer, HrefError, PrintWithSpace, + print_generic_bounds, print_where_clause, Buffer, HrefError, PrintWithSpace, VisibilityScope, }; use crate::html::markdown::{HeadingOffset, Markdown, MarkdownHtml, MarkdownSummaryLine}; @@ -589,6 +589,7 @@ fn document_full_inner( /// Add extra information about an item such as: /// +/// * Visibility /// * Stability /// * Deprecated /// * Required features (through the `doc_cfg` feature) @@ -619,6 +620,49 @@ fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option{}", cfg?.render_long_html())) } +fn visibility( + item: &clean::Item, + cx: &Context<'_>, + parent: Option<&clean::Item>, +) -> Option { + // For now, we only render visibility tags for methods and struct fields. + match *item.kind { + clean::ItemKind::MethodItem(_, _) | clean::ItemKind::StructFieldItem(_) => {} + _ => return None, + } + + fn tag(contents: impl fmt::Display) -> Option { + Some(format!( + r#"
🙈 Visibility: {}
"#, + contents + )) + } + const PRIVATE: &str = "private"; + + use clean::ItemKind::*; + use clean::Visibility::*; + + match item.visibility { + Public => None, + // By default, everything in Rust is private, with two exceptions: + // Associated items in a pub Trait are public by default; + // Enum variants in a pub enum are also public by default. + // https://doc.rust-lang.org/reference/visibility-and-privacy.html + // Therefore we don't put "private" annotations on traits, trait impls, + // or enum variant fields. + Inherited => match parent.map(|p| &*p.kind) { + Some(TraitItem(_) | ImplItem(_) | VariantItem(_)) => None, + _ => tag(PRIVATE), + }, + Restricted(vis_did) => { + match VisibilityScope::new(vis_did, item.def_id.expect_def_id(), cx.tcx()) { + VisibilityScope::Inherited => tag(PRIVATE), + scope => tag(scope), + } + } + } +} + /// Render the stability, deprecation and portability information that is displayed at the top of /// the item's documentation. fn short_item_info( @@ -629,6 +673,10 @@ fn short_item_info( let mut extra_info = vec![]; let error_codes = cx.shared.codes; + if let Some(visibility) = visibility(item, cx, parent) { + extra_info.push(visibility) + } + if let Some(depr @ Deprecation { note, since, is_since_rustc_version: _, suggestion: _ }) = item.deprecation(cx.tcx()) { @@ -903,7 +951,6 @@ fn render_assoc_item( } } }; - let vis = meth.visibility.print_with_space(meth.def_id, cx).to_string(); let constness = print_constness_with_space(&header.constness, meth.const_stability(cx.tcx())); let asyncness = header.asyncness.print_with_space(); @@ -914,7 +961,6 @@ fn render_assoc_item( // NOTE: `{:#}` does not print HTML formatting, `{}` does. So `g.print` can't be reused between the length calculation and `write!`. let generics_len = format!("{:#}", g.print(cx)).len(); let mut header_len = "fn ".len() - + vis.len() + constness.len() + asyncness.len() + unsafety.len() @@ -935,10 +981,9 @@ fn render_assoc_item( w.reserve(header_len + "{".len() + "".len()); write!( w, - "{indent}{vis}{constness}{asyncness}{unsafety}{defaultness}{abi}fn {name}\ + "{indent}{constness}{asyncness}{unsafety}{defaultness}{abi}fn {name}\ {generics}{decl}{notable_traits}{where_clause}", indent = indent_str, - vis = vis, constness = constness, asyncness = asyncness, unsafety = unsafety, @@ -1477,6 +1522,7 @@ fn render_impl( } w.push_buffer(info_buffer); + if toggled { w.write_str(""); w.push_buffer(doc_buffer); diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css index e41c993a5285d..74f20a8c2c564 100644 --- a/src/librustdoc/html/static/css/rustdoc.css +++ b/src/librustdoc/html/static/css/rustdoc.css @@ -956,10 +956,12 @@ body.blur > :not(#help) { padding: 0 20px 20px 17px;; } -.item-info .stab { +.item-info .stab, +.item-info .visibility { display: table; } -.stab { +.stab, +.visibility { border-width: 1px; border-style: solid; padding: 3px; @@ -971,7 +973,8 @@ body.blur > :not(#help) { display: inline; } -.stab .emoji { +.stab .emoji +.visibility .emoji { font-size: 1.5em; } diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index fba8231caac31..0dfc5e0336c50 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -222,6 +222,7 @@ details.undocumented > summary::before { color: #000; } +.visibility { background: #FFF5D6; border-color: #FFC600; } .stab.unstable { background: #FFF5D6; border-color: #FFC600; } .stab.deprecated { background: #ffc4c4; border-color: #db7b7b; } .stab.portability { background: #F3DFFF; border-color: #b07bdb; } diff --git a/src/test/rustdoc/document-private.rs b/src/test/rustdoc/document-private.rs new file mode 100644 index 0000000000000..36faf31fb24f1 --- /dev/null +++ b/src/test/rustdoc/document-private.rs @@ -0,0 +1,213 @@ +// compile-flags: --document-private-items +#![crate_name = "foo"] + +pub mod outmost_mod { + pub mod outer_mod { + pub mod inner_mod { + /// This function is visible within `outer_mod` + pub(in crate::outmost_mod::outer_mod) fn outer_mod_visible_fn() {} + + /// This function is visible to the entire crate + pub(crate) fn crate_visible_fn() {} + + /// This function is visible within `outer_mod` + pub(super) fn super_mod_visible_fn() { + /// This function is visible since we're in the same `mod` + inner_mod_visible_fn(); + } + + /// This function is visible only within `inner_mod`, + /// which is the same as leaving it private. + pub(self) fn inner_mod_visible_fn() {} + + pub mod inmost_mod { + // @has 'foo/outmost_mod/outer_mod/inner_mod/inmost_mod/struct.Foo.html' + // @count - '//*[@class="visibility"]' 7 + pub struct Boo { + /// rhubarb rhubarb rhubarb + // @matches - '//*[@class="visibility"]' 'Visibility: private' + alpha: usize, + /// durian durian durian + pub beta: usize, + /// sasquatch sasquatch sasquatch + pub(crate) gamma: usize, + } + + impl Boo { + /// Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium + /// doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore + /// veritatis et quasi architecto + pub fn new() -> Foo { + Foo(0) + } + + /// beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia + /// sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + /// qui ratione voluptatem sequi + /// + /// # Examples: + /// + /// + /// ```no_run + /// not_pub() + /// ``` + #[deprecated(since = "0.2.1", note = "The rust_foo version is more advanced")] + fn not_pub() {} + + /// beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia + /// sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + /// qui ratione voluptatem sequi + pub(crate) fn pub_crate() {} + + /// beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia + /// sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + /// qui ratione voluptatem sequi + pub(super) fn pub_super() {} + + /// beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia + /// sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + /// qui ratione voluptatem sequi + pub(self) fn pub_self() {} + + /// beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia + /// sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + /// qui ratione voluptatem sequi + pub(in crate::outmost_mod::outer_mod) fn pub_inner_mod() {} + } + + pub struct Foo( + /// rhubarb rhubarb rhubarb + usize, + /// durian durian durian + pub usize, + /// sasquatch sasqutch sasquatch + pub(crate) usize, + ); + + impl Foo { + /// Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium + /// doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore + /// veritatis et quasi architecto + pub fn new() -> Foo { + Foo(0) + } + + /// beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia + /// sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + /// qui ratione voluptatem sequi + /// + /// # Examples: + /// + /// + /// ```no_run + /// not_pub() + /// ``` + #[deprecated(since = "0.2.1", note = "The rust_foo version is more advanced")] + fn not_pub() {} + + /// beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia + /// sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + /// qui ratione voluptatem sequi + pub(crate) fn pub_crate() {} + + /// beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia + /// sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + /// qui ratione voluptatem sequi + pub(super) fn pub_super() {} + + /// beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia + /// sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + /// qui ratione voluptatem sequi + pub(self) fn pub_self() {} + + /// beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia + /// sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + /// qui ratione voluptatem sequi + pub(in crate::outmost_mod::outer_mod) fn pub_inner_mod() {} + } + + /// ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? + /// Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam + /// nihil molestiae consequatur, + pub enum Baz { + Size(usize), + } + + pub enum Bar { + /// ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi + /// Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam + /// nihil molestiae consequatur, + Fizz, + /// ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi + /// Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam + /// nihil molestiae consequatur, + Pop, + /// ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi + /// Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam + /// nihil molestiae consequatur, + Bang, + } + + impl Bar { + /// Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium + /// doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore + /// veritatis et quasi architecto + pub fn new() -> Bar { + Fizz + } + + /// beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia + /// sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + /// qui ratione voluptatem sequi + /// + /// # Examples: + /// + /// + /// ```no_run + /// not_pub() + /// ``` + #[deprecated(since = "0.2.1", note = "The rust_foo version is more advanced")] + fn not_pub() {} + + /// beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia + /// sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + /// qui ratione voluptatem sequi + pub(crate) fn pub_crate() {} + + /// beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia + /// sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + /// qui ratione voluptatem sequi + pub(super) fn pub_super() {} + + /// beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia + /// sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + /// qui ratione voluptatem sequi + pub(self) fn pub_self() {} + + /// beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia + /// sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + /// qui ratione voluptatem sequi + pub(in crate::outmost_mod::outer_mod) fn pub_inner_mod() {} + } + + pub trait Zepp { + fn required_method(); + + fn optional_method() {} + } + + /// beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas + /// sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + /// qui ratione voluptatem sequi + pub struct Zeppish; + + impl Zepp for Zeppish { + /// beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia + /// sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, + /// qui ratione voluptatem sequi + fn required_method() {} + } + } + } + } +}