From 344dd0e82805ce18d0569e9da8e14a7f0a9fdefe Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 28 Apr 2023 21:54:23 +0200 Subject: [PATCH 1/5] Make `repr` attribute local_only --- compiler/rustc_feature/src/builtin_attrs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 103e0f34407..c77292fdd16 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -344,7 +344,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ), ungated!(link_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(no_link, Normal, template!(Word), WarnFollowing), - ungated!(repr, Normal, template!(List: "C"), DuplicatesOk), + ungated!(repr, Normal, template!(List: "C"), DuplicatesOk, @only_local: true), ungated!(export_name, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(link_section, Normal, template!(NameValueStr: "name"), FutureWarnPreceding), ungated!(no_mangle, Normal, template!(Word), WarnFollowing, @only_local: true), From 61b6f6588402123ee20467f7df9863a9194d4fe2 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 28 Apr 2023 21:55:14 +0200 Subject: [PATCH 2/5] Get `repr` information through `AdtDef` for foreign items --- src/librustdoc/html/render/mod.rs | 69 ++++++++++++++++++++---- src/librustdoc/html/render/print_item.rs | 23 ++++---- src/librustdoc/lib.rs | 1 + 3 files changed, 72 insertions(+), 21 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index a5f08fdac11..55b249f8bbf 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -849,10 +849,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)); + write!(w, "{}", render_attributes_in_pre(meth, indent_str, tcx)); (4, indent_str, Ending::NoNewline) } else { - render_attributes_in_code(w, meth); + render_attributes_in_code(w, meth, tcx); (0, "", Ending::Newline) }; w.reserve(header_len + "{".len() + "".len()); @@ -1024,8 +1024,12 @@ fn render_assoc_item( const ALLOWED_ATTRIBUTES: &[Symbol] = &[sym::export_name, sym::link_section, sym::no_mangle, sym::repr, sym::non_exhaustive]; -fn attributes(it: &clean::Item) -> Vec { - it.attrs +fn attributes(it: &clean::Item, tcx: TyCtxt<'_>) -> Vec { + use rustc_abi::IntegerType; + use rustc_middle::ty::ReprFlags; + + let mut attrs: Vec = it + .attrs .other_attrs .iter() .filter_map(|attr| { @@ -1040,17 +1044,62 @@ fn attributes(it: &clean::Item) -> Vec { None } }) - .collect() + .collect(); + if let Some(def_id) = it.item_id.as_def_id() && + !def_id.is_local() && + // This check is needed because `adt_def` will panic if not a compatible type otherwise... + matches!(it.type_(), ItemType::Struct | ItemType::Enum | ItemType::Union) + { + let repr = tcx.adt_def(def_id).repr(); + let mut out = Vec::new(); + if repr.flags.contains(ReprFlags::IS_C) { + out.push("C"); + } + if repr.flags.contains(ReprFlags::IS_TRANSPARENT) { + out.push("transparent"); + } + if repr.flags.contains(ReprFlags::IS_SIMD) { + out.push("simd"); + } + let pack_s; + if let Some(pack) = repr.pack { + pack_s = format!("packed({})", pack.bytes()); + out.push(&pack_s); + } + let align_s; + if let Some(align) = repr.align { + align_s = format!("align({})", align.bytes()); + out.push(&align_s); + } + let int_s; + if let Some(int) = repr.int { + int_s = match int { + IntegerType::Pointer(is_signed) => { + format!("{}size", if is_signed { 'i' } else { 'u' }) + } + IntegerType::Fixed(size, is_signed) => { + format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) + } + }; + out.push(&int_s); + } + if out.is_empty() { + return Vec::new(); + } + attrs.push(format!("#[repr({})]", out.join(", "))); + } + attrs } // When an attribute is rendered inside a `
` tag, it is formatted using
 // a whitespace prefix and newline.
-fn render_attributes_in_pre<'a>(
+fn render_attributes_in_pre<'a, 'b: 'a>(
     it: &'a clean::Item,
     prefix: &'a str,
-) -> impl fmt::Display + Captures<'a> {
+    tcx: TyCtxt<'b>,
+) -> impl fmt::Display + Captures<'a> + Captures<'b> {
     crate::html::format::display_fn(move |f| {
-        for a in attributes(it) {
+        for a in attributes(it, tcx) {
             writeln!(f, "{}{}", prefix, a)?;
         }
         Ok(())
@@ -1059,8 +1108,8 @@ fn render_attributes_in_pre<'a>(
 
 // 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 Buffer, it: &clean::Item) {
-    for a in attributes(it) {
+fn render_attributes_in_code(w: &mut Buffer, it: &clean::Item, tcx: TyCtxt<'_>) {
+    for a in attributes(it, tcx) {
         write!(w, "
{}
", a); } } diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 3e71d41ec96..40f69189a8e 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -548,7 +548,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle w, "{attrs}{vis}{constness}{asyncness}{unsafety}{abi}fn \ {name}{generics}{decl}{notable_traits}{where_clause}", - attrs = render_attributes_in_pre(it, ""), + attrs = render_attributes_in_pre(it, "", tcx), vis = visibility, constness = constness, asyncness = asyncness, @@ -589,7 +589,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: it.name.unwrap(), t.generics.print(cx), bounds, - attrs = render_attributes_in_pre(it, ""), + attrs = render_attributes_in_pre(it, "", tcx), ); if !t.generics.where_predicates.is_empty() { @@ -1063,7 +1063,7 @@ fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: & t.generics.print(cx), print_where_clause(&t.generics, cx, 0, Ending::Newline), bounds(&t.bounds, true, cx), - attrs = render_attributes_in_pre(it, ""), + attrs = render_attributes_in_pre(it, "", cx.tcx()), ); }); @@ -1085,7 +1085,7 @@ fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &cl t.generics.print(cx), where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), bounds = bounds(&t.bounds, false, cx), - attrs = render_attributes_in_pre(it, ""), + attrs = render_attributes_in_pre(it, "", cx.tcx()), ); }); @@ -1109,7 +1109,7 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea t.generics.print(cx), where_clause = print_where_clause(&t.generics, cx, 0, Ending::Newline), type_ = t.type_.print(cx), - attrs = render_attributes_in_pre(it, ""), + attrs = render_attributes_in_pre(it, "", cx.tcx()), ); }); } @@ -1168,7 +1168,8 @@ fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean: &'b self, ) -> impl fmt::Display + Captures<'a> + 'b + Captures<'cx> { display_fn(move |f| { - let v = render_attributes_in_pre(self.it, ""); + let tcx = self.cx.borrow().tcx(); + let v = render_attributes_in_pre(self.it, "", tcx); write!(f, "{v}") }) } @@ -1250,7 +1251,7 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: visibility_print_with_space(it.visibility(tcx), it.item_id, cx), it.name.unwrap(), e.generics.print(cx), - attrs = render_attributes_in_pre(it, ""), + attrs = render_attributes_in_pre(it, "", tcx), ); if !print_where_clause_and_check(w, &e.generics, cx) { // If there wasn't a `where` clause, we add a whitespace. @@ -1445,7 +1446,7 @@ fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) { wrap_item(w, |w| { let tcx = cx.tcx(); - render_attributes_in_code(w, it); + render_attributes_in_code(w, it, tcx); write!( w, @@ -1492,7 +1493,7 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Struct) { wrap_item(w, |w| { - render_attributes_in_code(w, it); + render_attributes_in_code(w, it, cx.tcx()); render_struct(w, it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx); }); @@ -1542,7 +1543,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) { wrap_item(w, |w| { - render_attributes_in_code(w, it); + render_attributes_in_code(w, it, cx.tcx()); write!( w, "{vis}static {mutability}{name}: {typ}", @@ -1558,7 +1559,7 @@ fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { wrap_item(w, |w| { w.write_str("extern {\n"); - render_attributes_in_code(w, it); + render_attributes_in_code(w, it, cx.tcx()); write!( w, " {}type {};\n}}", diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index c15afca2261..eb589bb2414 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -33,6 +33,7 @@ extern crate tracing; // Dependencies listed in Cargo.toml do not need `extern crate`. extern crate pulldown_cmark; +extern crate rustc_abi; extern crate rustc_ast; extern crate rustc_ast_pretty; extern crate rustc_attr; From 89b0956a9a60f1c1b49e7b1df2c48f49f4af7314 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 29 Apr 2023 12:17:08 +0200 Subject: [PATCH 3/5] Fix display of attributes for enums --- src/librustdoc/html/render/print_item.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 40f69189a8e..4cc81e860f0 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1245,13 +1245,13 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: let tcx = cx.tcx(); let count_variants = e.variants().count(); wrap_item(w, |mut w| { + render_attributes_in_code(w, it, tcx); write!( w, - "{attrs}{}enum {}{}", + "{}enum {}{}", visibility_print_with_space(it.visibility(tcx), it.item_id, cx), it.name.unwrap(), e.generics.print(cx), - attrs = render_attributes_in_pre(it, "", tcx), ); if !print_where_clause_and_check(w, &e.generics, cx) { // If there wasn't a `where` clause, we add a whitespace. From 2693e20aa3a4d1a430fcacf48d85e984323e122f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 29 Apr 2023 12:17:12 +0200 Subject: [PATCH 4/5] Extend foreign inlined item with `#[repr()]` test --- tests/rustdoc/inline_cross/auxiliary/repr.rs | 22 ++++++++++++++++++-- tests/rustdoc/inline_cross/repr.rs | 22 +++++++++++++++++--- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/tests/rustdoc/inline_cross/auxiliary/repr.rs b/tests/rustdoc/inline_cross/auxiliary/repr.rs index 64a98f18146..4a6648a6439 100644 --- a/tests/rustdoc/inline_cross/auxiliary/repr.rs +++ b/tests/rustdoc/inline_cross/auxiliary/repr.rs @@ -1,4 +1,22 @@ -#[repr(C)] -pub struct Foo { +#![feature(repr_simd)] + +#[repr(C, align(8))] +pub struct ReprC { field: u8, } +#[repr(simd, packed(2))] +pub struct ReprSimd { + field: u8, +} +#[repr(transparent)] +pub struct ReprTransparent { + field: u8, +} +#[repr(isize)] +pub enum ReprIsize { + Bla, +} +#[repr(u8)] +pub enum ReprU8 { + Bla, +} diff --git a/tests/rustdoc/inline_cross/repr.rs b/tests/rustdoc/inline_cross/repr.rs index 7e1f2799af1..9e107cee9e9 100644 --- a/tests/rustdoc/inline_cross/repr.rs +++ b/tests/rustdoc/inline_cross/repr.rs @@ -7,7 +7,23 @@ extern crate repr; -// @has 'foo/struct.Foo.html' -// @has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C)]' +// @has 'foo/struct.ReprC.html' +// @has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(C, align(8))]' #[doc(inline)] -pub use repr::Foo; +pub use repr::ReprC; +// @has 'foo/struct.ReprSimd.html' +// @has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(simd, packed(2))]' +#[doc(inline)] +pub use repr::ReprSimd; +// @has 'foo/struct.ReprTransparent.html' +// @has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(transparent)]' +#[doc(inline)] +pub use repr::ReprTransparent; +// @has 'foo/enum.ReprIsize.html' +// @has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(isize)]' +#[doc(inline)] +pub use repr::ReprIsize; +// @has 'foo/enum.ReprU8.html' +// @has - '//*[@class="rust item-decl"]//*[@class="code-attribute"]' '#[repr(u8)]' +#[doc(inline)] +pub use repr::ReprU8; From b778688f913bf6d3735b0b603b41c6989f513399 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 29 Apr 2023 23:36:48 +0200 Subject: [PATCH 5/5] Unify attributes retrieval for JSON and HTML rendering --- src/librustdoc/clean/types.rs | 73 +++++++++++++++++++++++++++++ src/librustdoc/html/render/mod.rs | 75 +----------------------------- src/librustdoc/json/conversions.rs | 7 +-- 3 files changed, 76 insertions(+), 79 deletions(-) diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 74f330b7621..7371b44465b 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -11,6 +11,7 @@ use arrayvec::ArrayVec; use thin_vec::ThinVec; use rustc_ast as ast; +use rustc_ast_pretty::pprust; use rustc_attr::{ConstStability, Deprecation, Stability, StabilityLevel}; use rustc_const_eval::const_eval::is_unstable_const_fn; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -711,6 +712,78 @@ impl Item { }; Some(tcx.visibility(def_id)) } + + pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, keep_as_is: bool) -> Vec { + const ALLOWED_ATTRIBUTES: &[Symbol] = + &[sym::export_name, sym::link_section, sym::no_mangle, sym::repr, sym::non_exhaustive]; + + use rustc_abi::IntegerType; + use rustc_middle::ty::ReprFlags; + + let mut attrs: Vec = self + .attrs + .other_attrs + .iter() + .filter_map(|attr| { + if keep_as_is { + Some(pprust::attribute_to_string(attr)) + } else if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) { + Some( + pprust::attribute_to_string(attr) + .replace("\\\n", "") + .replace('\n', "") + .replace(" ", " "), + ) + } else { + None + } + }) + .collect(); + if let Some(def_id) = self.item_id.as_def_id() && + !def_id.is_local() && + // This check is needed because `adt_def` will panic if not a compatible type otherwise... + matches!(self.type_(), ItemType::Struct | ItemType::Enum | ItemType::Union) + { + let repr = tcx.adt_def(def_id).repr(); + let mut out = Vec::new(); + if repr.flags.contains(ReprFlags::IS_C) { + out.push("C"); + } + if repr.flags.contains(ReprFlags::IS_TRANSPARENT) { + out.push("transparent"); + } + if repr.flags.contains(ReprFlags::IS_SIMD) { + out.push("simd"); + } + let pack_s; + if let Some(pack) = repr.pack { + pack_s = format!("packed({})", pack.bytes()); + out.push(&pack_s); + } + let align_s; + if let Some(align) = repr.align { + align_s = format!("align({})", align.bytes()); + out.push(&align_s); + } + let int_s; + if let Some(int) = repr.int { + int_s = match int { + IntegerType::Pointer(is_signed) => { + format!("{}size", if is_signed { 'i' } else { 'u' }) + } + IntegerType::Fixed(size, is_signed) => { + format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) + } + }; + out.push(&int_s); + } + if out.is_empty() { + return Vec::new(); + } + attrs.push(format!("#[repr({})]", out.join(", "))); + } + attrs + } } #[derive(Clone, Debug)] diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 55b249f8bbf..91ca048050e 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -48,7 +48,6 @@ use std::str; use std::string::ToString; use askama::Template; -use rustc_ast_pretty::pprust; use rustc_attr::{ConstStability, Deprecation, StabilityLevel}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -1021,76 +1020,6 @@ fn render_assoc_item( } } -const ALLOWED_ATTRIBUTES: &[Symbol] = - &[sym::export_name, sym::link_section, sym::no_mangle, sym::repr, sym::non_exhaustive]; - -fn attributes(it: &clean::Item, tcx: TyCtxt<'_>) -> Vec { - use rustc_abi::IntegerType; - use rustc_middle::ty::ReprFlags; - - let mut attrs: Vec = it - .attrs - .other_attrs - .iter() - .filter_map(|attr| { - if ALLOWED_ATTRIBUTES.contains(&attr.name_or_empty()) { - Some( - pprust::attribute_to_string(attr) - .replace("\\\n", "") - .replace('\n', "") - .replace(" ", " "), - ) - } else { - None - } - }) - .collect(); - if let Some(def_id) = it.item_id.as_def_id() && - !def_id.is_local() && - // This check is needed because `adt_def` will panic if not a compatible type otherwise... - matches!(it.type_(), ItemType::Struct | ItemType::Enum | ItemType::Union) - { - let repr = tcx.adt_def(def_id).repr(); - let mut out = Vec::new(); - if repr.flags.contains(ReprFlags::IS_C) { - out.push("C"); - } - if repr.flags.contains(ReprFlags::IS_TRANSPARENT) { - out.push("transparent"); - } - if repr.flags.contains(ReprFlags::IS_SIMD) { - out.push("simd"); - } - let pack_s; - if let Some(pack) = repr.pack { - pack_s = format!("packed({})", pack.bytes()); - out.push(&pack_s); - } - let align_s; - if let Some(align) = repr.align { - align_s = format!("align({})", align.bytes()); - out.push(&align_s); - } - let int_s; - if let Some(int) = repr.int { - int_s = match int { - IntegerType::Pointer(is_signed) => { - format!("{}size", if is_signed { 'i' } else { 'u' }) - } - IntegerType::Fixed(size, is_signed) => { - format!("{}{}", if is_signed { 'i' } else { 'u' }, size.size().bytes() * 8) - } - }; - out.push(&int_s); - } - if out.is_empty() { - return Vec::new(); - } - attrs.push(format!("#[repr({})]", out.join(", "))); - } - attrs -} - // When an attribute is rendered inside a `
` tag, it is formatted using
 // a whitespace prefix and newline.
 fn render_attributes_in_pre<'a, 'b: 'a>(
@@ -1099,7 +1028,7 @@ fn render_attributes_in_pre<'a, 'b: 'a>(
     tcx: TyCtxt<'b>,
 ) -> impl fmt::Display + Captures<'a> + Captures<'b> {
     crate::html::format::display_fn(move |f| {
-        for a in attributes(it, tcx) {
+        for a in it.attributes(tcx, false) {
             writeln!(f, "{}{}", prefix, a)?;
         }
         Ok(())
@@ -1109,7 +1038,7 @@ fn render_attributes_in_pre<'a, 'b: 'a>(
 // 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 Buffer, it: &clean::Item, tcx: TyCtxt<'_>) {
-    for a in attributes(it, tcx) {
+    for a in it.attributes(tcx, false) {
         write!(w, "
{}
", a); } } diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index edd046ab772..62aab46fa7e 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -41,12 +41,7 @@ impl JsonRenderer<'_> { }) .collect(); let docs = item.attrs.collapsed_doc_value(); - let attrs = item - .attrs - .other_attrs - .iter() - .map(rustc_ast_pretty::pprust::attribute_to_string) - .collect(); + let attrs = item.attributes(self.tcx, true); let span = item.span(self.tcx); let visibility = item.visibility(self.tcx); let clean::Item { name, item_id, .. } = item;