diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 8d7f05775064f..673947ad30832 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1029,6 +1029,7 @@ fn assoc_const( ) -> impl fmt::Display { let tcx = cx.tcx(); fmt::from_fn(move |w| { + render_attributes_in_code(w, it, &" ".repeat(indent), cx); write!( w, "{indent}{vis}const {name}{generics}: {ty}", @@ -1136,10 +1137,10 @@ fn assoc_method( let (indent, indent_str, end_newline) = if parent == ItemType::Trait { header_len += 4; let indent_str = " "; - write!(w, "{}", render_attributes_in_pre(meth, indent_str, cx))?; + render_attributes_in_code(w, meth, indent_str, cx); (4, indent_str, Ending::NoNewline) } else { - render_attributes_in_code(w, meth, cx); + render_attributes_in_code(w, meth, "", cx); (0, "", Ending::Newline) }; write!( @@ -1309,28 +1310,28 @@ fn render_assoc_item( }) } -// When an attribute is rendered inside a `
` tag, it is formatted using
-// a whitespace prefix and newline.
-fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> impl fmt::Display {
- fmt::from_fn(move |f| {
- for a in it.attributes(cx.tcx(), cx.cache()) {
- writeln!(f, "{prefix}{a}")?;
- }
- Ok(())
- })
-}
-
struct CodeAttribute(String);
-fn render_code_attribute(code_attr: CodeAttribute, w: &mut impl fmt::Write) {
- write!(w, "{}", code_attr.0).unwrap();
+fn render_code_attribute(prefix: &str, code_attr: CodeAttribute, w: &mut impl fmt::Write) {
+ write!(
+ w,
+ "{prefix}{attr}",
+ prefix = prefix,
+ attr = code_attr.0
+ )
+ .unwrap();
}
// 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<'_>) {
+fn render_attributes_in_code(
+ w: &mut impl fmt::Write,
+ it: &clean::Item,
+ prefix: &str,
+ cx: &Context<'_>,
+) {
for attr in it.attributes(cx.tcx(), cx.cache()) {
- render_code_attribute(CodeAttribute(attr), w);
+ render_code_attribute(prefix, CodeAttribute(attr), w);
}
}
@@ -1342,7 +1343,7 @@ fn render_repr_attributes_in_code(
item_type: ItemType,
) {
if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type) {
- render_code_attribute(CodeAttribute(repr), w);
+ render_code_attribute("", CodeAttribute(repr), w);
}
}
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 407238d66b8cd..2618ec272ca5a 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -20,8 +20,8 @@ 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_repr_attributes_in_code, render_rightside, render_stability_since_raw,
+ render_assoc_item, render_assoc_items, render_attributes_in_code, render_impl,
+ render_repr_attributes_in_code, render_rightside, render_stability_since_raw,
render_stability_since_raw_with_extra, write_section_heading,
};
use crate::clean;
@@ -107,13 +107,6 @@ macro_rules! item_template_methods {
}
item_template_methods!($($rest)*);
};
- (render_attributes_in_pre $($rest:tt)*) => {
- fn render_attributes_in_pre(&self) -> impl fmt::Display {
- let (item, cx) = self.item_and_cx();
- render_attributes_in_pre(item, "", cx)
- }
- item_template_methods!($($rest)*);
- };
(render_assoc_items $($rest:tt)*) => {
fn render_assoc_items(&self) -> impl fmt::Display {
let (item, cx) = self.item_and_cx();
@@ -457,7 +450,12 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
write!(
w,
"\
- {vis}{imp}{stab_tags}\
+ "
+ )?;
+ render_attributes_in_code(w, myitem, "", cx);
+ write!(
+ w,
+ "{vis}{imp}{stab_tags}\
",
vis = visibility_print_with_space(myitem, cx),
imp = import.print(cx)
@@ -625,11 +623,11 @@ fn item_function(cx: &Context<'_>, it: &clean::Item, f: &clean::Function) -> imp
let notable_traits = notable_traits_button(&f.decl.output, cx).maybe_display();
wrap_item(w, |w| {
+ render_attributes_in_code(w, it, "", cx);
write!(
w,
- "{attrs}{vis}{constness}{asyncness}{safety}{abi}fn \
+ "{vis}{constness}{asyncness}{safety}{abi}fn \
{name}{generics}{decl}{notable_traits}{where_clause}",
- attrs = render_attributes_in_pre(it, "", cx),
vis = visibility,
constness = constness,
asyncness = asyncness,
@@ -666,10 +664,10 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt:
// Output the trait definition
wrap_item(w, |mut w| {
+ render_attributes_in_code(&mut w, it, "", cx);
write!(
w,
- "{attrs}{vis}{safety}{is_auto}trait {name}{generics}{bounds}",
- attrs = render_attributes_in_pre(it, "", cx),
+ "{vis}{safety}{is_auto}trait {name}{generics}{bounds}",
vis = visibility_print_with_space(it, cx),
safety = t.safety(tcx).print_with_space(),
is_auto = if t.is_auto(tcx) { "auto " } else { "" },
@@ -1240,10 +1238,10 @@ fn item_trait_alias(
) -> impl fmt::Display {
fmt::from_fn(|w| {
wrap_item(w, |w| {
+ render_attributes_in_code(w, it, "", cx);
write!(
w,
- "{attrs}trait {name}{generics} = {bounds}{where_clause};",
- attrs = render_attributes_in_pre(it, "", cx),
+ "trait {name}{generics} = {bounds}{where_clause};",
name = it.name.unwrap(),
generics = t.generics.print(cx),
bounds = print_bounds(&t.bounds, true, cx),
@@ -1268,10 +1266,10 @@ fn item_trait_alias(
fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) -> impl fmt::Display {
fmt::from_fn(|w| {
wrap_item(w, |w| {
+ render_attributes_in_code(w, it, "", cx);
write!(
w,
- "{attrs}{vis}type {name}{generics}{where_clause} = {type_};",
- attrs = render_attributes_in_pre(it, "", cx),
+ "{vis}type {name}{generics}{where_clause} = {type_};",
vis = visibility_print_with_space(it, cx),
name = it.name.unwrap(),
generics = t.generics.print(cx),
@@ -1452,7 +1450,14 @@ item_template!(
impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
fn render_union(&self) -> impl Display {
- render_union(self.it, Some(self.generics), self.fields, self.cx)
+ render_union(
+ self.it,
+ Some(self.generics),
+ self.fields,
+ self.def_id,
+ self.is_type_alias,
+ self.cx,
+ )
}
fn document_field(&self, field: &'a clean::Item) -> impl Display {
@@ -1479,27 +1484,6 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
_ => None,
})
}
-
- fn render_attributes_in_pre(&self) -> impl fmt::Display {
- fmt::from_fn(move |f| {
- if self.is_type_alias {
- // For now the only attributes we render for type aliases are `repr` attributes.
- if let Some(repr) = clean::repr_attributes(
- self.cx.tcx(),
- self.cx.cache(),
- self.def_id,
- ItemType::Union,
- ) {
- writeln!(f, "{repr}")?;
- };
- } else {
- for a in self.it.attributes(self.cx.tcx(), self.cx.cache()) {
- writeln!(f, "{a}")?;
- }
- }
- Ok(())
- })
- }
}
fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display {
@@ -1563,7 +1547,7 @@ impl<'clean> DisplayEnum<'clean> {
// For now the only attributes we render for type aliases are `repr` attributes.
render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Enum);
} else {
- render_attributes_in_code(w, it, cx);
+ render_attributes_in_code(w, it, "", cx);
}
write!(
w,
@@ -1702,7 +1686,7 @@ fn render_enum_fields(
if v.is_stripped() {
continue;
}
- write!(w, "{}", render_attributes_in_pre(v, TAB, cx))?;
+ render_attributes_in_code(w, v, TAB, cx);
w.write_str(TAB)?;
match v.kind {
clean::VariantItem(ref var) => match var.kind {
@@ -1882,6 +1866,7 @@ fn item_macro(cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) -> impl fmt:
fmt::from_fn(|w| {
wrap_item(w, |w| {
// FIXME: Also print `#[doc(hidden)]` for `macro_rules!` if it `is_doc_hidden`.
+ render_attributes_in_code(w, it, "", cx);
if !t.macro_rules {
write!(w, "{}", visibility_print_with_space(it, cx))?;
}
@@ -1950,7 +1935,7 @@ fn item_constant(
fmt::from_fn(|w| {
wrap_item(w, |w| {
let tcx = cx.tcx();
- render_attributes_in_code(w, it, cx);
+ render_attributes_in_code(w, it, "", cx);
write!(
w,
@@ -2018,7 +2003,7 @@ impl<'a> DisplayStruct<'a> {
// For now the only attributes we render for type aliases are `repr` attributes.
render_repr_attributes_in_code(w, cx, self.def_id, ItemType::Struct);
} else {
- render_attributes_in_code(w, it, cx);
+ render_attributes_in_code(w, it, "", cx);
}
write!(
w,
@@ -2115,7 +2100,7 @@ fn item_static(
) -> impl fmt::Display {
fmt::from_fn(move |w| {
wrap_item(w, |w| {
- render_attributes_in_code(w, it, cx);
+ render_attributes_in_code(w, it, "", cx);
write!(
w,
"{vis}{safe}static {mutability}{name}: {typ}",
@@ -2135,7 +2120,7 @@ fn item_foreign_type(cx: &Context<'_>, it: &clean::Item) -> impl fmt::Display {
fmt::from_fn(|w| {
wrap_item(w, |w| {
w.write_str("extern {\n")?;
- render_attributes_in_code(w, it, cx);
+ render_attributes_in_code(w, it, "", cx);
write!(w, " {}type {};\n}}", visibility_print_with_space(it, cx), it.name.unwrap(),)
})?;
@@ -2358,9 +2343,17 @@ fn render_union(
it: &clean::Item,
g: Option<&clean::Generics>,
fields: &[clean::Item],
+ def_id: DefId,
+ is_type_alias: bool,
cx: &Context<'_>,
) -> impl Display {
fmt::from_fn(move |mut f| {
+ if is_type_alias {
+ // For now the only attributes we render for type aliases are `repr` attributes.
+ render_repr_attributes_in_code(f, cx, def_id, ItemType::Union);
+ } else {
+ render_attributes_in_code(f, it, "", cx);
+ }
write!(f, "{}union {}", visibility_print_with_space(it, cx), it.name.unwrap(),)?;
let where_displayed = if let Some(generics) = g {
@@ -2390,6 +2383,7 @@ fn render_union(
for field in fields {
if let clean::StructFieldItem(ref ty) = field.kind {
+ render_attributes_in_code(&mut f, field, " ", cx);
writeln!(
f,
" {}{}: {},",
@@ -2481,11 +2475,15 @@ fn render_struct_fields(
if toggle {
toggle_open(&mut *w, format_args!("{count_fields} fields"));
}
+ if has_visible_fields {
+ writeln!(w)?;
+ }
for field in fields {
if let clean::StructFieldItem(ref ty) = field.kind {
- write!(
+ render_attributes_in_code(w, field, &format!("{tab} "), cx);
+ writeln!(
w,
- "\n{tab} {vis}{name}: {ty},",
+ "{tab} {vis}{name}: {ty},",
vis = visibility_print_with_space(field, cx),
name = field.name.unwrap(),
ty = ty.print(cx)
@@ -2495,12 +2493,12 @@ fn render_struct_fields(
if has_visible_fields {
if has_stripped_entries {
- write!(
+ writeln!(
w,
- "\n{tab} /* private fields */"
+ "{tab} /* private fields */"
)?;
}
- write!(w, "\n{tab}")?;
+ write!(w, "{tab}")?;
} else if has_stripped_entries {
write!(w, " /* private fields */ ")?;
}
diff --git a/src/librustdoc/html/templates/item_union.html b/src/librustdoc/html/templates/item_union.html
index b5d3367a6a10b..5dba43ca255c7 100644
--- a/src/librustdoc/html/templates/item_union.html
+++ b/src/librustdoc/html/templates/item_union.html
@@ -1,5 +1,4 @@
- {{ self.render_attributes_in_pre()|safe }}
{{ self.render_union()|safe }}
{% if !self.is_type_alias %}
diff --git a/tests/rustdoc/attribute-rendering.rs b/tests/rustdoc/attribute-rendering.rs
index bf9b81077f356..fb40d0a9887cf 100644
--- a/tests/rustdoc/attribute-rendering.rs
+++ b/tests/rustdoc/attribute-rendering.rs
@@ -1,7 +1,8 @@
#![crate_name = "foo"]
//@ has 'foo/fn.f.html'
-//@ has - //*[@'class="rust item-decl"]' '#[unsafe(export_name = "f")] pub fn f()'
+//@ has - //*[@'class="code-attribute"]' '#[unsafe(export_name = "f")]'
+//@ has - //*[@'class="rust item-decl"]' 'pub fn f()'
#[unsafe(export_name = "\
f")]
pub fn f() {}
diff --git a/tests/rustdoc/attributes.rs b/tests/rustdoc/attributes.rs
index 34487a8912778..db5f89ed46e4a 100644
--- a/tests/rustdoc/attributes.rs
+++ b/tests/rustdoc/attributes.rs
@@ -1,18 +1,77 @@
//@ edition: 2024
#![crate_name = "foo"]
-//@ has foo/fn.f.html '//pre[@class="rust item-decl"]' '#[unsafe(no_mangle)]'
+//@ has foo/fn.f.html '//*[@class="code-attribute"]' '#[unsafe(no_mangle)]'
#[unsafe(no_mangle)]
pub extern "C" fn f() {}
-//@ has foo/fn.g.html '//pre[@class="rust item-decl"]' '#[unsafe(export_name = "bar")]'
+//@ has foo/fn.g.html '//*[@class="code-attribute"]' '#[unsafe(export_name = "bar")]'
#[unsafe(export_name = "bar")]
pub extern "C" fn g() {}
-//@ has foo/fn.example.html '//pre[@class="rust item-decl"]' '#[unsafe(link_section = ".text")]'
+//@ has foo/fn.example.html '//*[@class="code-attribute"]' '#[unsafe(link_section = ".text")]'
#[unsafe(link_section = ".text")]
pub extern "C" fn example() {}
-//@ has foo/struct.Repr.html '//pre[@class="rust item-decl"]' '#[repr(C, align(8))]'
+//@ has foo/struct.Repr.html '//*[@class="code-attribute"]' '#[repr(C, align(8))]'
#[repr(C, align(8))]
pub struct Repr;
+
+//@ has foo/macro.macro_rule.html '//*[@class="code-attribute"]' '#[unsafe(link_section = ".text")]'
+#[unsafe(link_section = ".text")]
+#[macro_export]
+macro_rules! macro_rule {
+ () => {};
+}
+
+//@ has 'foo/enum.Enum.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "enum")]'
+#[unsafe(link_section = "enum")]
+pub enum Enum {
+ //@ has 'foo/enum.Enum.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "a")]'
+ #[unsafe(link_section = "a")]
+ A,
+ //@ has 'foo/enum.Enum.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "quz")]'
+ #[unsafe(link_section = "quz")]
+ Quz {
+ //@ has 'foo/enum.Enum.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "b")]'
+ #[unsafe(link_section = "b")]
+ b: (),
+ },
+}
+
+//@ has 'foo/trait.Trait.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "trait")]'
+#[unsafe(link_section = "trait")]
+pub trait Trait {
+ //@ has 'foo/trait.Trait.html'
+ //@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]/*[@class="code-attribute"]' '#[unsafe(link_section = "bar")]'
+ //@ has - '//*[@id="associatedconstant.BAR"]/*[@class="code-header"]' 'const BAR: u32 = 0u32'
+ #[unsafe(link_section = "bar")]
+ const BAR: u32 = 0;
+
+ //@ has - '//*[@class="code-attribute"]' '#[unsafe(link_section = "foo")]'
+ #[unsafe(link_section = "foo")]
+ fn foo() {}
+}
+
+//@ has 'foo/union.Union.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "union")]'
+#[unsafe(link_section = "union")]
+pub union Union {
+ //@ has 'foo/union.Union.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "x")]'
+ #[unsafe(link_section = "x")]
+ pub x: u32,
+ y: f32,
+}
+
+//@ has 'foo/struct.Struct.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "struct")]'
+#[unsafe(link_section = "struct")]
+pub struct Struct {
+ //@ has 'foo/struct.Struct.html' '//*[@class="code-attribute"]' '#[unsafe(link_section = "x")]'
+ #[unsafe(link_section = "x")]
+ pub x: u32,
+ y: f32,
+}
+
+// Check that the attributes from the trait items show up consistently in the impl.
+//@ has 'foo/struct.Struct.html' '//*[@id="trait-implementations-list"]//*[@class="code-attribute"]' '#[unsafe(link_section = "bar")]'
+//@ has 'foo/struct.Struct.html' '//*[@id="trait-implementations-list"]//*[@class="code-attribute"]' '#[unsafe(link_section = "foo")]'
+impl Trait for Struct {}
diff --git a/tests/rustdoc/enum/enum-variant-non_exhaustive.type-alias-code.html b/tests/rustdoc/enum/enum-variant-non_exhaustive.type-alias-code.html
index 04eea709079d7..e8b8e93beb45e 100644
--- a/tests/rustdoc/enum/enum-variant-non_exhaustive.type-alias-code.html
+++ b/tests/rustdoc/enum/enum-variant-non_exhaustive.type-alias-code.html
@@ -1,4 +1,4 @@
pub enum TypeAlias {
- #[non_exhaustive]
+ #[non_exhaustive]
Variant,
-}
\ No newline at end of file
+}
diff --git a/tests/rustdoc/enum/enum-variant-non_exhaustive.type-code.html b/tests/rustdoc/enum/enum-variant-non_exhaustive.type-code.html
index 6c8851ea5df47..51763c824ebd2 100644
--- a/tests/rustdoc/enum/enum-variant-non_exhaustive.type-code.html
+++ b/tests/rustdoc/enum/enum-variant-non_exhaustive.type-code.html
@@ -1,4 +1,4 @@
pub enum Type {
- #[non_exhaustive]
+ #[non_exhaustive]
Variant,
-}
\ No newline at end of file
+}
diff --git a/tests/rustdoc/type-alias/repr.rs b/tests/rustdoc/type-alias/repr.rs
index cf90798036099..884ed74264a58 100644
--- a/tests/rustdoc/type-alias/repr.rs
+++ b/tests/rustdoc/type-alias/repr.rs
@@ -22,7 +22,8 @@ pub union Foo2 {
}
//@ has 'foo/type.Bar2.html'
-//@ matches - '//*[@class="rust item-decl"]' '#\[repr\(C\)\]\npub union Bar2 \{*'
+//@ matches - '//*[@class="code-attribute"]' '#\[repr\(C\)\]'
+//@ matches - '//*[@class="rust item-decl"]' 'pub union Bar2 \{*'
// Ensures that we see the doc comment of the type alias and not of the aliased type.
//@ has - '//*[@class="toggle top-doc"]/*[@class="docblock"]' 'bar'
/// bar