diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 8c7ab925577df..3301caf1cbf8d 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -760,6 +760,11 @@ impl Item { } }) .collect(); + + if !keep_as_is && self.is_doc_hidden() { + attrs.push("#[doc(hidden)]".into()); + } + if !keep_as_is && let Some(def_id) = self.def_id() && let ItemType::Struct | ItemType::Enum | ItemType::Union = self.type_() @@ -822,6 +827,7 @@ impl Item { attrs.push(format!("#[repr({})]", out.join(", "))); } } + attrs } diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 136002b8e155b..b5e1005fbbe87 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -1610,14 +1610,7 @@ pub(crate) fn visibility_print_with_space<'a, 'tcx: 'a>( } }; - let is_doc_hidden = item.is_doc_hidden(); - display_fn(move |f| { - if is_doc_hidden { - f.write_str("#[doc(hidden)] ")?; - } - - f.write_str(&vis) - }) + display_fn(move |f| f.write_str(&vis)) } pub(crate) trait PrintWithSpace { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 9a9ce31caaa4c..a99d3a87e6ad4 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -854,14 +854,22 @@ fn assoc_const( ty: &clean::Type, value: AssocConstValue<'_>, link: AssocItemLink<'_>, - indent: usize, + parent: Option, cx: &Context<'_>, ) { let tcx = cx.tcx(); + let (indent, indent_str, end_newline) = if parent == Some(ItemType::Trait) { + let indent_str = " "; + write!(w, "{}", render_attributes_in_pre(it, indent_str, cx)); + (4, indent_str, Ending::NoNewline) + } else { + render_attributes_in_code(w, it, cx); + (0, "", Ending::Newline) + }; write!( w, "{indent}{vis}const {name}{generics}: {ty}", - indent = " ".repeat(indent), + indent = indent_str, vis = visibility_print_with_space(it, cx), href = assoc_href_attr(it, link, cx), name = it.name.as_ref().unwrap(), @@ -883,7 +891,7 @@ fn assoc_const( write!(w, " = {}", Escape(&repr)); } } - write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline)); + write!(w, "{}", print_where_clause(generics, cx, indent, end_newline)); } fn assoc_type( @@ -893,13 +901,21 @@ fn assoc_type( bounds: &[clean::GenericBound], default: Option<&clean::Type>, link: AssocItemLink<'_>, - indent: usize, + parent: Option, cx: &Context<'_>, ) { + let (indent, indent_str, end_newline) = if parent == Some(ItemType::Trait) { + let indent_str = " "; + write!(w, "{}", render_attributes_in_pre(it, indent_str, cx)); + (4, indent_str, Ending::NoNewline) + } else { + render_attributes_in_code(w, it, cx); + (0, "", Ending::Newline) + }; write!( w, "{indent}{vis}type {name}{generics}", - indent = " ".repeat(indent), + indent = indent_str, vis = visibility_print_with_space(it, cx), href = assoc_href_attr(it, link, cx), name = it.name.as_ref().unwrap(), @@ -912,7 +928,7 @@ fn assoc_type( if let Some(default) = default { write!(w, " = {}", default.print(cx)) } - write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline)); + write!(w, "{}", print_where_clause(generics, cx, indent, end_newline)); } fn assoc_method( @@ -1098,16 +1114,9 @@ fn render_assoc_item( clean::MethodItem(m, _) => { assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode) } - clean::RequiredAssocConstItem(generics, ty) => assoc_const( - w, - item, - generics, - ty, - AssocConstValue::None, - link, - if parent == ItemType::Trait { 4 } else { 0 }, - cx, - ), + clean::RequiredAssocConstItem(generics, ty) => { + assoc_const(w, item, generics, ty, AssocConstValue::None, link, Some(parent), cx) + } clean::ProvidedAssocConstItem(ci) => assoc_const( w, item, @@ -1115,7 +1124,7 @@ fn render_assoc_item( &ci.type_, AssocConstValue::TraitDefault(&ci.kind), link, - if parent == ItemType::Trait { 4 } else { 0 }, + Some(parent), cx, ), clean::ImplAssocConstItem(ci) => assoc_const( @@ -1125,19 +1134,12 @@ fn render_assoc_item( &ci.type_, AssocConstValue::Impl(&ci.kind), link, - if parent == ItemType::Trait { 4 } else { 0 }, - cx, - ), - clean::RequiredAssocTypeItem(ref generics, ref bounds) => assoc_type( - w, - item, - generics, - bounds, - None, - link, - if parent == ItemType::Trait { 4 } else { 0 }, + Some(parent), cx, ), + clean::RequiredAssocTypeItem(ref generics, ref bounds) => { + assoc_type(w, item, generics, bounds, None, link, Some(parent), cx) + } clean::AssocTypeItem(ref ty, ref bounds) => assoc_type( w, item, @@ -1145,7 +1147,7 @@ fn render_assoc_item( bounds, Some(ty.item_type.as_ref().unwrap_or(&ty.type_)), link, - if parent == ItemType::Trait { 4 } else { 0 }, + Some(parent), cx, ), _ => panic!("render_assoc_item called on non-associated-item"), @@ -1167,6 +1169,20 @@ fn render_attributes_in_pre<'a, 'tcx: 'a>( }) } +// When an attribute is rendered inside a `
` tag, it is formatted using
+// a whitespace suffix.
+fn render_attributes_in_pre_same_line<'a, 'tcx: 'a>(
+    it: &'a clean::Item,
+    cx: &'a Context<'tcx>,
+) -> impl fmt::Display + Captures<'a> + Captures<'tcx> {
+    crate::html::format::display_fn(move |f| {
+        for a in it.attributes(cx.tcx(), cx.cache(), false) {
+            write!(f, "{a} ")?;
+        }
+        Ok(())
+    })
+}
+
 // When an attribute is rendered inside a  tag, it is formatted using
 // a div to produce a newline after it.
 fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) {
@@ -1175,6 +1191,17 @@ fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Con
     }
 }
 
+// Same as `render_attributes_in_code()`, but on the same line.
+fn render_attributes_in_code_same_line(
+    w: &mut impl fmt::Write,
+    it: &clean::Item,
+    cx: &Context<'_>,
+) {
+    for attr in it.attributes(cx.tcx(), cx.cache(), false) {
+        write!(w, "{attr} ").unwrap();
+    }
+}
+
 #[derive(Copy, Clone)]
 enum AssocItemLink<'a> {
     Anchor(Option<&'a str>),
@@ -1529,7 +1556,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
                             &[], // intentionally leaving out bounds
                             Some(&tydef.type_),
                             src_link,
-                            0,
+                            None,
                             cx,
                         );
                         out.push_str(";");
@@ -1733,7 +1760,7 @@ fn render_impl(
                     ty,
                     AssocConstValue::None,
                     link.anchor(if trait_.is_some() { &source_id } else { &id }),
-                    0,
+                    None,
                     cx,
                 );
                 w.write_str("");
@@ -1759,7 +1786,7 @@ fn render_impl(
                         _ => unreachable!(),
                     },
                     link.anchor(if trait_.is_some() { &source_id } else { &id }),
-                    0,
+                    None,
                     cx,
                 );
                 w.write_str("");
@@ -1781,7 +1808,7 @@ fn render_impl(
                     bounds,
                     None,
                     link.anchor(if trait_.is_some() { &source_id } else { &id }),
-                    0,
+                    None,
                     cx,
                 );
                 w.write_str("");
@@ -1803,7 +1830,7 @@ fn render_impl(
                     &[], // intentionally leaving out bounds
                     Some(tydef.item_type.as_ref().unwrap_or(&tydef.type_)),
                     link.anchor(if trait_.is_some() { &source_id } else { &id }),
-                    0,
+                    None,
                     cx,
                 );
                 w.write_str("");
@@ -2100,7 +2127,7 @@ pub(crate) fn render_impl_summary(
                         &[], // intentionally leaving out bounds
                         Some(&tydef.type_),
                         AssocItemLink::Anchor(None),
-                        0,
+                        None,
                         cx,
                     );
                     w.write_str(";");
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index e8230e63c0f60..1c381f9ada9b8 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -20,8 +20,9 @@ use super::{
     AssocItemLink, AssocItemRender, Context, ImplRenderingParameters, RenderMode,
     collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference,
     item_ty_to_section, notable_traits_button, notable_traits_json, render_all_impls,
-    render_assoc_item, render_assoc_items, render_attributes_in_code, render_attributes_in_pre,
-    render_impl, render_rightside, render_stability_since_raw,
+    render_assoc_item, render_assoc_items, render_attributes_in_code,
+    render_attributes_in_code_same_line, render_attributes_in_pre,
+    render_attributes_in_pre_same_line, render_impl, render_rightside, render_stability_since_raw,
     render_stability_since_raw_with_extra, write_section_heading,
 };
 use crate::clean;
@@ -438,6 +439,19 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
                     extra_info_tags(tcx, myitem, item, Some(import_def_id)).to_string()
                 });
 
+                let visibility_and_hidden = match myitem.visibility(tcx) {
+                    Some(ty::Visibility::Restricted(_)) => {
+                        if myitem.is_doc_hidden() {
+                            // Don't separate with a space when there are two of them
+                            " ๐Ÿ”’๐Ÿ‘ป "
+                        } else {
+                            " ๐Ÿ”’ "
+                        }
+                    }
+                    _ if myitem.is_doc_hidden() => " ๐Ÿ‘ป ",
+                    _ => "",
+                };
+
                 w.write_str(ITEM_TABLE_ROW_OPEN);
                 let id = match import.kind {
                     clean::ImportKind::Simple(s) => {
@@ -454,6 +468,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
                     w,
                     "
\ {vis}{imp}\ + {visibility_and_hidden}\
\ {stab_tags_before}{stab_tags}{stab_tags_after}", vis = visibility_print_with_space(myitem, cx), @@ -1445,8 +1460,9 @@ fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Uni fn print_tuple_struct_fields<'a, 'cx: 'a>( cx: &'a Context<'cx>, s: &'a [clean::Item], + writing_in_pre: bool, ) -> impl fmt::Display + 'a + Captures<'cx> { - display_fn(|f| { + display_fn(move |f| { if !s.is_empty() && s.iter().all(|field| { matches!(field.kind, clean::StrippedItem(box clean::StructFieldItem(..))) @@ -1455,13 +1471,22 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>( return f.write_str("/* private fields */"); } - for (i, ty) in s.iter().enumerate() { + for (i, it) in s.iter().enumerate() { if i > 0 { f.write_str(", ")?; } - match ty.kind { + match it.kind { clean::StrippedItem(box clean::StructFieldItem(_)) => f.write_str("_")?, - clean::StructFieldItem(ref ty) => write!(f, "{}", ty.print(cx))?, + clean::StructFieldItem(ref ty) => { + if it.is_doc_hidden() { + if writing_in_pre { + write!(f, "{}", render_attributes_in_pre_same_line(it, cx))?; + } else { + render_attributes_in_code_same_line(f, it, cx); + } + } + write!(f, "{}", ty.print(cx))? + } _ => unreachable!(), } } @@ -1472,13 +1497,13 @@ fn print_tuple_struct_fields<'a, 'cx: 'a>( fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) { let count_variants = e.variants().count(); wrap_item(w, |w| { - render_attributes_in_code(w, it, cx); write!( w, - "{}enum {}{}", - visibility_print_with_space(it, cx), - it.name.unwrap(), - e.generics.print(cx), + "{attrs}{vis}enum {name}{generics}", + attrs = render_attributes_in_pre(it, "", cx), + vis = visibility_print_with_space(it, cx), + name = it.name.unwrap(), + generics = e.generics.print(cx), ); render_enum_fields( @@ -1583,6 +1608,7 @@ fn render_enum_fields( if v.is_stripped() { continue; } + write!(w, "{}", render_attributes_in_pre(v, TAB, cx)); w.write_str(TAB); match v.kind { clean::VariantItem(ref var) => match var.kind { @@ -1596,7 +1622,12 @@ fn render_enum_fields( enum_def_id, ), clean::VariantKind::Tuple(ref s) => { - write!(w, "{}({})", v.name.unwrap(), print_tuple_struct_fields(cx, s)); + write!( + w, + "{}({})", + v.name.unwrap(), + print_tuple_struct_fields(cx, s, true) + ); } clean::VariantKind::Struct(ref s) => { render_struct(w, v, None, None, &s.fields, TAB, false, cx); @@ -1651,6 +1682,7 @@ fn item_variants( " rightside", ); w.write_str("

"); + render_attributes_in_code(w, variant, cx); if let clean::VariantItem(ref var) = variant.kind && let clean::VariantKind::CLike = var.kind { @@ -1670,7 +1702,7 @@ fn item_variants( let clean::VariantItem(variant_data) = &variant.kind else { unreachable!() }; if let clean::VariantKind::Tuple(ref s) = variant_data.kind { - write!(w, "({})", print_tuple_struct_fields(cx, s)); + write!(w, "({})", print_tuple_struct_fields(cx, s, false)); } w.write_str("

"); @@ -1679,6 +1711,7 @@ fn item_variants( let heading_and_fields = match &variant_data.kind { clean::VariantKind::Struct(s) => { // If there is no field to display, no need to add the heading. + // FIXME: this ignore the `--document-hidden-items` unstable flag if s.fields.iter().any(|f| !f.is_doc_hidden()) { Some(("Fields", &s.fields)) } else { @@ -1721,10 +1754,15 @@ fn item_variants( "
\ \ ยง\ - {f}: {t}\ + " + ); + render_attributes_in_code(w, it, cx); + write!( + w, + "{}: {}\ ", - f = field.name.unwrap(), - t = ty.print(cx), + field.name.unwrap(), + ty.print(cx), ); write!( w, @@ -1743,7 +1781,7 @@ fn item_variants( fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) { wrap_item(w, |w| { - // FIXME: Also print `#[doc(hidden)]` for `macro_rules!` if it `is_doc_hidden`. + write!(w, "{}", render_attributes_in_pre(it, "", cx)); if !t.macro_rules { write!(w, "{}", visibility_print_with_space(it, cx)); } @@ -2197,14 +2235,16 @@ fn render_union<'a, 'cx: 'a>( toggle_open(&mut f, format_args!("{count_fields} fields")); } + let prefix = " "; for field in fields { if let clean::StructFieldItem(ref ty) = field.kind { writeln!( f, - " {}{}: {},", - visibility_print_with_space(field, cx), - field.name.unwrap(), - ty.print(cx) + "{attrs}{prefix}{vis}{name}: {ty},", + attrs = render_attributes_in_pre(field, prefix, cx), + vis = visibility_print_with_space(field, cx), + name = field.name.unwrap(), + ty = ty.print(cx) )?; } } @@ -2280,11 +2320,13 @@ fn render_struct_fields( if toggle { toggle_open(&mut w, format_args!("{count_fields} fields")); } + let prefix = format!("{tab} "); for field in fields { if let clean::StructFieldItem(ref ty) = field.kind { write!( w, - "\n{tab} {vis}{name}: {ty},", + "\n{attrs}{prefix}{vis}{name}: {ty},", + attrs = render_attributes_in_pre(field, &prefix, cx), vis = visibility_print_with_space(field, cx), name = field.name.unwrap(), ty = ty.print(cx), @@ -2321,7 +2363,13 @@ fn render_struct_fields( match field.kind { clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"), clean::StructFieldItem(ref ty) => { - write!(w, "{}{}", visibility_print_with_space(field, cx), ty.print(cx),) + write!( + w, + "{attrs}{vis}{ty}", + attrs = render_attributes_in_pre_same_line(field, cx), + vis = visibility_print_with_space(field, cx), + ty = ty.print(cx), + ) } _ => unreachable!(), } diff --git a/tests/rustdoc/display-hidden-items.rs b/tests/rustdoc/display-hidden-items.rs index d9f53435e4635..240a902cf7e79 100644 --- a/tests/rustdoc/display-hidden-items.rs +++ b/tests/rustdoc/display-hidden-items.rs @@ -7,24 +7,28 @@ //@ has 'foo/index.html' //@ has - '//*[@class="item-name"]/span[@title="Hidden item"]' '๐Ÿ‘ป' -//@ has - '//*[@id="reexport.hidden_reexport"]/code' '#[doc(hidden)] pub use hidden::inside_hidden as hidden_reexport;' +//@ has - '//*[@id="reexport.hidden_reexport"]/code' 'pub use hidden::inside_hidden as hidden_reexport;' #[doc(hidden)] pub use hidden::inside_hidden as hidden_reexport; //@ has - '//*[@class="item-name"]/a[@class="trait"]' 'TraitHidden' -//@ has 'foo/trait.TraitHidden.html' -//@ has - '//code' '#[doc(hidden)] pub trait TraitHidden' +//@ has 'foo/trait.TraitHidden.html' '//*[@class="rust item-decl"]/code' '#[doc(hidden)]' #[doc(hidden)] pub trait TraitHidden {} //@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="trait"]' 'Trait' pub trait Trait { //@ has 'foo/trait.Trait.html' - //@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]' '#[doc(hidden)] const BAR: u32 = 0u32' + //@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]/*[@class="code-attribute"]' '#[doc(hidden)]' #[doc(hidden)] const BAR: u32 = 0; - //@ has - '//*[@id="method.foo"]/*[@class="code-header"]' 'fn foo()' + #[doc(hidden)] + //@ has 'foo/trait.Trait.html' + //@ has - '//*[@id="associatedtype.Baz"]/*[@class="code-header"]/*[@class="code-attribute"]' '#[doc(hidden)]' + type Baz; + + //@ has - '//*[@id="method.foo"]/*[@class="code-header"]/*[@class="code-attribute"]' '#[doc(hidden)]' #[doc(hidden)] fn foo() {} } @@ -32,27 +36,40 @@ pub trait Trait { //@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="struct"]' 'Struct' //@ has 'foo/struct.Struct.html' pub struct Struct { - //@ has - '//*[@id="structfield.a"]/code' 'a: u32' + // FIXME: display attrs on fields in the Fields section + //@ !has - '//*[@id="structfield.a"]/*[@class="code-header"]/*[@class="code-attribute"]' '#[doc(hidden)]' #[doc(hidden)] pub a: u32, } impl Struct { - //@ has - '//*[@id="method.new"]/*[@class="code-header"]' 'pub fn new() -> Self' + //@ has - '//*[@id="method.new"]/*[@class="code-header"]/*[@class="code-attribute"]' '#[doc(hidden)]' #[doc(hidden)] pub fn new() -> Self { Self { a: 0 } } } impl Trait for Struct { - //@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]' '#[doc(hidden)] const BAR: u32 = 0u32' - //@ has - '//*[@id="method.foo"]/*[@class="code-header"]' '#[doc(hidden)] fn foo()' + //@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]/*[@class="code-attribute"]' '#[doc(hidden)]' + //@ has - '//*[@id="method.foo"]/*[@class="code-header"]/*[@class="code-attribute"]' '#[doc(hidden)]' + // NOTE: feature(associated_type_defaults) is unstable so users have to set a type for trait associated items: + // we don't want to hide it if they don't ask for it explicitely in the impl. + //@ !has - '//*[@id="associatedtype.Baz"]/*[@class="code-header"]/*[@class="code-attribute"]' '#[doc(hidden)]' + + type Baz = (); } //@ has - '//*[@id="impl-TraitHidden-for-Struct"]/*[@class="code-header"]' 'impl TraitHidden for Struct' impl TraitHidden for Struct {} +//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="struct"]' 'TupleStruct' +pub struct TupleStruct( + // FIXME: display attrs on fields in the Fields section + //@ !has 'foo/struct.TupleStruct.html' '//*[@id="structfield.0"]/*[@class="code-header"]/*[@class="code-attribute"]' '#[doc(hidden)]' + #[doc(hidden)] + () +); + //@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="enum"]' 'HiddenEnum' -//@ has 'foo/enum.HiddenEnum.html' -//@ has - '//code' '#[doc(hidden)] pub enum HiddenEnum' +//@ has 'foo/enum.HiddenEnum.html' '//*[@class="rust item-decl"]/code' '#[doc(hidden)]' #[doc(hidden)] pub enum HiddenEnum { A, @@ -60,9 +77,11 @@ pub enum HiddenEnum { //@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="enum"]' 'Enum' pub enum Enum { - //@ has 'foo/enum.Enum.html' '//*[@id="variant.A"]/*[@class="code-header"]' 'A' + //@ has 'foo/enum.Enum.html' '//*[@id="variant.A"]/*[@class="code-header"]/*[@class="code-attribute"]' '#[doc(hidden)]' #[doc(hidden)] A, + //@ has 'foo/enum.Enum.html' '//*[@id="variant.B"]/*[@class="code-header"]/*[@class="code-attribute"]' '#[doc(hidden)]' + B(#[doc(hidden)] ()) } //@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="mod"]' 'hidden' @@ -73,3 +92,22 @@ pub mod hidden { //@ has 'foo/hidden/fn.inside_hidden.html' pub fn inside_hidden() {} } + +//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="union"]' 'Union' +//@ has 'foo/union.Union.html' '//*[@class="rust item-decl"]/code' '#[doc(hidden)]' +#[doc(hidden)] +pub union Union { + // FIXME: display attrs on fields in the Fields section + //@ !has 'foo/union.Union.html' '//*[@id="structfield.a"]/*[@class="code-header"]/*[@class="code-attribute"]' '#[doc(hidden)]' + #[doc(hidden)] + pub a: u8, +} + +//@ has 'foo/index.html' '//*[@class="item-name"]/a[@class="macro"]' 'macro_rule' +//@ has 'foo/macro.macro_rule.html' '//*[@class="rust item-decl"]/code' '#[doc(hidden)]' +#[doc(hidden)] +#[macro_export] +macro_rules! macro_rule { + () => {}; +} +