Rollup merge of #140863 - GuillaumeGomez:cleanup-tyalias-render, r=lolbinarycat
[rustdoc] Unify type aliases rendering with other ADT Fixes #140739. Better reviewed one commit at a time. Just one thing I'm wondering: should we also render non-`repr` attributes? If so, I wonder if we shouldn't simply change `clean::TypeAlias` to contain the other ADT directly (`Struct`, `Enum` and `Union`) and remove the `TypeAlias::generics` field. Can be done in a follow-up too. cc ``@camelid`` r? ``@notriddle``
This commit is contained in:
commit
6257d2fb1c
8 changed files with 381 additions and 219 deletions
|
|
@ -610,6 +610,9 @@ impl Item {
|
|||
UnionItem(ref union_) => Some(union_.has_stripped_entries()),
|
||||
EnumItem(ref enum_) => Some(enum_.has_stripped_entries()),
|
||||
VariantItem(ref v) => v.has_stripped_entries(),
|
||||
TypeAliasItem(ref type_alias) => {
|
||||
type_alias.inner_type.as_ref().and_then(|t| t.has_stripped_entries())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -761,14 +764,11 @@ impl Item {
|
|||
Some(tcx.visibility(def_id))
|
||||
}
|
||||
|
||||
pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Vec<String> {
|
||||
pub(crate) fn attributes_without_repr(&self, tcx: TyCtxt<'_>, is_json: bool) -> Vec<String> {
|
||||
const ALLOWED_ATTRIBUTES: &[Symbol] =
|
||||
&[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive];
|
||||
|
||||
use rustc_abi::IntegerType;
|
||||
|
||||
let mut attrs: Vec<String> = self
|
||||
.attrs
|
||||
self.attrs
|
||||
.other_attrs
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
|
|
@ -796,74 +796,28 @@ impl Item {
|
|||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
.collect()
|
||||
}
|
||||
|
||||
// Add #[repr(...)]
|
||||
if let Some(def_id) = self.def_id()
|
||||
&& let ItemType::Struct | ItemType::Enum | ItemType::Union = self.type_()
|
||||
{
|
||||
let adt = tcx.adt_def(def_id);
|
||||
let repr = adt.repr();
|
||||
let mut out = Vec::new();
|
||||
if repr.c() {
|
||||
out.push("C");
|
||||
}
|
||||
if repr.transparent() {
|
||||
// Render `repr(transparent)` iff the non-1-ZST field is public or at least one
|
||||
// field is public in case all fields are 1-ZST fields.
|
||||
let render_transparent = is_json
|
||||
|| cache.document_private
|
||||
|| adt
|
||||
.all_fields()
|
||||
.find(|field| {
|
||||
let ty =
|
||||
field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did));
|
||||
tcx.layout_of(
|
||||
ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty),
|
||||
)
|
||||
.is_ok_and(|layout| !layout.is_1zst())
|
||||
})
|
||||
.map_or_else(
|
||||
|| adt.all_fields().any(|field| field.vis.is_public()),
|
||||
|field| field.vis.is_public(),
|
||||
);
|
||||
pub(crate) fn attributes_and_repr(
|
||||
&self,
|
||||
tcx: TyCtxt<'_>,
|
||||
cache: &Cache,
|
||||
is_json: bool,
|
||||
) -> Vec<String> {
|
||||
let mut attrs = self.attributes_without_repr(tcx, is_json);
|
||||
|
||||
if render_transparent {
|
||||
out.push("transparent");
|
||||
}
|
||||
}
|
||||
if repr.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() {
|
||||
attrs.push(format!("#[repr({})]", out.join(", ")));
|
||||
}
|
||||
if let Some(repr_attr) = self.repr(tcx, cache, is_json) {
|
||||
attrs.push(repr_attr);
|
||||
}
|
||||
attrs
|
||||
}
|
||||
|
||||
/// Returns a stringified `#[repr(...)]` attribute.
|
||||
pub(crate) fn repr(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Option<String> {
|
||||
repr_attributes(tcx, cache, self.def_id()?, self.type_(), is_json)
|
||||
}
|
||||
|
||||
pub fn is_doc_hidden(&self) -> bool {
|
||||
self.attrs.is_doc_hidden()
|
||||
}
|
||||
|
|
@ -873,6 +827,73 @@ impl Item {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn repr_attributes(
|
||||
tcx: TyCtxt<'_>,
|
||||
cache: &Cache,
|
||||
def_id: DefId,
|
||||
item_type: ItemType,
|
||||
is_json: bool,
|
||||
) -> Option<String> {
|
||||
use rustc_abi::IntegerType;
|
||||
|
||||
if !matches!(item_type, ItemType::Struct | ItemType::Enum | ItemType::Union) {
|
||||
return None;
|
||||
}
|
||||
let adt = tcx.adt_def(def_id);
|
||||
let repr = adt.repr();
|
||||
let mut out = Vec::new();
|
||||
if repr.c() {
|
||||
out.push("C");
|
||||
}
|
||||
if repr.transparent() {
|
||||
// Render `repr(transparent)` iff the non-1-ZST field is public or at least one
|
||||
// field is public in case all fields are 1-ZST fields.
|
||||
let render_transparent = cache.document_private
|
||||
|| is_json
|
||||
|| adt
|
||||
.all_fields()
|
||||
.find(|field| {
|
||||
let ty = field.ty(tcx, ty::GenericArgs::identity_for_item(tcx, field.did));
|
||||
tcx.layout_of(ty::TypingEnv::post_analysis(tcx, field.did).as_query_input(ty))
|
||||
.is_ok_and(|layout| !layout.is_1zst())
|
||||
})
|
||||
.map_or_else(
|
||||
|| adt.all_fields().any(|field| field.vis.is_public()),
|
||||
|field| field.vis.is_public(),
|
||||
);
|
||||
|
||||
if render_transparent {
|
||||
out.push("transparent");
|
||||
}
|
||||
}
|
||||
if repr.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() { Some(format!("#[repr({})]", out.join(", "))) } else { None }
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum ItemKind {
|
||||
ExternCrateItem {
|
||||
|
|
@ -2107,7 +2128,7 @@ impl Enum {
|
|||
self.variants.iter().any(|f| f.is_stripped())
|
||||
}
|
||||
|
||||
pub(crate) fn variants(&self) -> impl Iterator<Item = &Item> {
|
||||
pub(crate) fn non_stripped_variants(&self) -> impl Iterator<Item = &Item> {
|
||||
self.variants.iter().filter(|v| !v.is_stripped())
|
||||
}
|
||||
}
|
||||
|
|
@ -2345,6 +2366,17 @@ pub(crate) enum TypeAliasInnerType {
|
|||
Struct { ctor_kind: Option<CtorKind>, fields: Vec<Item> },
|
||||
}
|
||||
|
||||
impl TypeAliasInnerType {
|
||||
fn has_stripped_entries(&self) -> Option<bool> {
|
||||
Some(match self {
|
||||
Self::Enum { variants, .. } => variants.iter().any(|v| v.is_stripped()),
|
||||
Self::Union { fields } | Self::Struct { fields, .. } => {
|
||||
fields.iter().any(|f| f.is_stripped())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct TypeAlias {
|
||||
pub(crate) type_: Type,
|
||||
|
|
|
|||
|
|
@ -1194,18 +1194,36 @@ fn render_assoc_item(
|
|||
// 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(), false) {
|
||||
for a in it.attributes_and_repr(cx.tcx(), cx.cache(), false) {
|
||||
writeln!(f, "{prefix}{a}")?;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
struct CodeAttribute(String);
|
||||
|
||||
fn render_code_attribute(code_attr: CodeAttribute, w: &mut impl fmt::Write) {
|
||||
write!(w, "<div class=\"code-attribute\">{}</div>", code_attr.0).unwrap();
|
||||
}
|
||||
|
||||
// When an attribute is rendered inside a <code> 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<'_>) {
|
||||
for attr in it.attributes(cx.tcx(), cx.cache(), false) {
|
||||
write!(w, "<div class=\"code-attribute\">{attr}</div>").unwrap();
|
||||
for attr in it.attributes_and_repr(cx.tcx(), cx.cache(), false) {
|
||||
render_code_attribute(CodeAttribute(attr), w);
|
||||
}
|
||||
}
|
||||
|
||||
/// used for type aliases to only render their `repr` attribute.
|
||||
fn render_repr_attributes_in_code(
|
||||
w: &mut impl fmt::Write,
|
||||
cx: &Context<'_>,
|
||||
def_id: DefId,
|
||||
item_type: ItemType,
|
||||
) {
|
||||
if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type, false) {
|
||||
render_code_attribute(CodeAttribute(repr), w);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use super::{
|
|||
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_impl, render_repr_attributes_in_code, render_rightside, render_stability_since_raw,
|
||||
render_stability_since_raw_with_extra, write_section_heading,
|
||||
};
|
||||
use crate::clean;
|
||||
|
|
@ -1278,94 +1278,58 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) ->
|
|||
|
||||
match inner_type {
|
||||
clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive } => {
|
||||
let variants_iter = || variants.iter().filter(|i| !i.is_stripped());
|
||||
let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
|
||||
let enum_def_id = ty.ty_adt_def().unwrap().did();
|
||||
|
||||
wrap_item(w, |w| {
|
||||
let variants_len = variants.len();
|
||||
let variants_count = variants_iter().count();
|
||||
let has_stripped_entries = variants_len != variants_count;
|
||||
|
||||
write!(
|
||||
w,
|
||||
"enum {}{}{}",
|
||||
it.name.unwrap(),
|
||||
t.generics.print(cx),
|
||||
render_enum_fields(
|
||||
cx,
|
||||
Some(&t.generics),
|
||||
variants,
|
||||
variants_count,
|
||||
has_stripped_entries,
|
||||
*is_non_exhaustive,
|
||||
enum_def_id,
|
||||
)
|
||||
)
|
||||
})?;
|
||||
write!(w, "{}", item_variants(cx, it, variants, enum_def_id))?;
|
||||
DisplayEnum {
|
||||
variants,
|
||||
generics: &t.generics,
|
||||
is_non_exhaustive: *is_non_exhaustive,
|
||||
def_id: enum_def_id,
|
||||
}
|
||||
.render_into(cx, it, true, w)?;
|
||||
}
|
||||
clean::TypeAliasInnerType::Union { fields } => {
|
||||
wrap_item(w, |w| {
|
||||
let fields_count = fields.iter().filter(|i| !i.is_stripped()).count();
|
||||
let has_stripped_fields = fields.len() != fields_count;
|
||||
let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
|
||||
let union_def_id = ty.ty_adt_def().unwrap().did();
|
||||
|
||||
write!(
|
||||
w,
|
||||
"union {}{}{}",
|
||||
it.name.unwrap(),
|
||||
t.generics.print(cx),
|
||||
render_struct_fields(
|
||||
Some(&t.generics),
|
||||
None,
|
||||
fields,
|
||||
"",
|
||||
true,
|
||||
has_stripped_fields,
|
||||
cx,
|
||||
),
|
||||
)
|
||||
})?;
|
||||
write!(w, "{}", item_fields(cx, it, fields, None))?;
|
||||
ItemUnion {
|
||||
cx,
|
||||
it,
|
||||
fields,
|
||||
generics: &t.generics,
|
||||
is_type_alias: true,
|
||||
def_id: union_def_id,
|
||||
}
|
||||
.render_into(w)?;
|
||||
}
|
||||
clean::TypeAliasInnerType::Struct { ctor_kind, fields } => {
|
||||
wrap_item(w, |w| {
|
||||
let fields_count = fields.iter().filter(|i| !i.is_stripped()).count();
|
||||
let has_stripped_fields = fields.len() != fields_count;
|
||||
let ty = cx.tcx().type_of(it.def_id().unwrap()).instantiate_identity();
|
||||
let struct_def_id = ty.ty_adt_def().unwrap().did();
|
||||
|
||||
write!(
|
||||
w,
|
||||
"struct {}{}{}",
|
||||
it.name.unwrap(),
|
||||
t.generics.print(cx),
|
||||
render_struct_fields(
|
||||
Some(&t.generics),
|
||||
*ctor_kind,
|
||||
fields,
|
||||
"",
|
||||
true,
|
||||
has_stripped_fields,
|
||||
cx,
|
||||
),
|
||||
)
|
||||
})?;
|
||||
write!(w, "{}", item_fields(cx, it, fields, None))?;
|
||||
DisplayStruct {
|
||||
ctor_kind: *ctor_kind,
|
||||
generics: &t.generics,
|
||||
fields,
|
||||
def_id: struct_def_id,
|
||||
}
|
||||
.render_into(cx, it, true, w)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let def_id = it.item_id.expect_def_id();
|
||||
// Render any items associated directly to this alias, as otherwise they
|
||||
// won't be visible anywhere in the docs. It would be nice to also show
|
||||
// associated items from the aliased type (see discussion in #32077), but
|
||||
// we need #14072 to make sense of the generics.
|
||||
write!(
|
||||
w,
|
||||
"{}{}",
|
||||
render_assoc_items(cx, it, def_id, AssocItemRender::All),
|
||||
document_type_layout(cx, def_id)
|
||||
)?;
|
||||
}
|
||||
|
||||
let def_id = it.item_id.expect_def_id();
|
||||
// Render any items associated directly to this alias, as otherwise they
|
||||
// won't be visible anywhere in the docs. It would be nice to also show
|
||||
// associated items from the aliased type (see discussion in #32077), but
|
||||
// we need #14072 to make sense of the generics.
|
||||
write!(
|
||||
w,
|
||||
"{}{}",
|
||||
render_assoc_items(cx, it, def_id, AssocItemRender::All),
|
||||
document_type_layout(cx, def_id)
|
||||
)?;
|
||||
|
||||
// [RUSTDOCIMPL] type.impl
|
||||
//
|
||||
// Include type definitions from the alias target type.
|
||||
|
|
@ -1463,50 +1427,83 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) ->
|
|||
})
|
||||
}
|
||||
|
||||
fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display {
|
||||
item_template!(
|
||||
#[template(path = "item_union.html")]
|
||||
struct ItemUnion<'a, 'cx> {
|
||||
cx: &'a Context<'cx>,
|
||||
it: &'a clean::Item,
|
||||
s: &'a clean::Union,
|
||||
},
|
||||
methods = [document, document_type_layout, render_attributes_in_pre, render_assoc_items]
|
||||
);
|
||||
item_template!(
|
||||
#[template(path = "item_union.html")]
|
||||
struct ItemUnion<'a, 'cx> {
|
||||
cx: &'a Context<'cx>,
|
||||
it: &'a clean::Item,
|
||||
fields: &'a [clean::Item],
|
||||
generics: &'a clean::Generics,
|
||||
is_type_alias: bool,
|
||||
def_id: DefId,
|
||||
},
|
||||
methods = [document, document_type_layout, render_assoc_items]
|
||||
);
|
||||
|
||||
impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
|
||||
fn render_union(&self) -> impl Display {
|
||||
render_union(self.it, Some(&self.s.generics), &self.s.fields, self.cx)
|
||||
}
|
||||
|
||||
fn document_field(&self, field: &'a clean::Item) -> impl Display {
|
||||
document(self.cx, field, Some(self.it), HeadingOffset::H3)
|
||||
}
|
||||
|
||||
fn stability_field(&self, field: &clean::Item) -> Option<String> {
|
||||
field.stability_class(self.cx.tcx())
|
||||
}
|
||||
|
||||
fn print_ty(&self, ty: &'a clean::Type) -> impl Display {
|
||||
ty.print(self.cx)
|
||||
}
|
||||
|
||||
fn fields_iter(
|
||||
&self,
|
||||
) -> iter::Peekable<impl Iterator<Item = (&'a clean::Item, &'a clean::Type)>> {
|
||||
self.s
|
||||
.fields
|
||||
.iter()
|
||||
.filter_map(|f| match f.kind {
|
||||
clean::StructFieldItem(ref ty) => Some((f, ty)),
|
||||
_ => None,
|
||||
})
|
||||
.peekable()
|
||||
}
|
||||
impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
|
||||
fn render_union(&self) -> impl Display {
|
||||
render_union(self.it, Some(&self.generics), &self.fields, self.cx)
|
||||
}
|
||||
|
||||
fn document_field(&self, field: &'a clean::Item) -> impl Display {
|
||||
document(self.cx, field, Some(self.it), HeadingOffset::H3)
|
||||
}
|
||||
|
||||
fn stability_field(&self, field: &clean::Item) -> Option<String> {
|
||||
field.stability_class(self.cx.tcx())
|
||||
}
|
||||
|
||||
fn print_ty(&self, ty: &'a clean::Type) -> impl Display {
|
||||
ty.print(self.cx)
|
||||
}
|
||||
|
||||
// FIXME (GuillaumeGomez): When <https://github.com/askama-rs/askama/issues/452> is implemented,
|
||||
// we can replace the returned value with:
|
||||
//
|
||||
// `iter::Peekable<impl Iterator<Item = (&'a clean::Item, &'a clean::Type)>>`
|
||||
//
|
||||
// And update `item_union.html`.
|
||||
fn fields_iter(&self) -> impl Iterator<Item = (&'a clean::Item, &'a clean::Type)> {
|
||||
self.fields.iter().filter_map(|f| match f.kind {
|
||||
clean::StructFieldItem(ref ty) => Some((f, ty)),
|
||||
_ => 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,
|
||||
false,
|
||||
) {
|
||||
writeln!(f, "{repr}")?;
|
||||
};
|
||||
} else {
|
||||
for a in self.it.attributes_and_repr(self.cx.tcx(), self.cx.cache(), false) {
|
||||
writeln!(f, "{a}")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn item_union(cx: &Context<'_>, it: &clean::Item, s: &clean::Union) -> impl fmt::Display {
|
||||
fmt::from_fn(|w| {
|
||||
ItemUnion { cx, it, s }.render_into(w).unwrap();
|
||||
ItemUnion {
|
||||
cx,
|
||||
it,
|
||||
fields: &s.fields,
|
||||
generics: &s.generics,
|
||||
is_type_alias: false,
|
||||
def_id: it.def_id().unwrap(),
|
||||
}
|
||||
.render_into(w)?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
|
@ -1533,41 +1530,81 @@ fn print_tuple_struct_fields(cx: &Context<'_>, s: &[clean::Item]) -> impl Displa
|
|||
})
|
||||
}
|
||||
|
||||
fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display {
|
||||
fmt::from_fn(|w| {
|
||||
let count_variants = e.variants().count();
|
||||
struct DisplayEnum<'clean> {
|
||||
variants: &'clean IndexVec<VariantIdx, clean::Item>,
|
||||
generics: &'clean clean::Generics,
|
||||
is_non_exhaustive: bool,
|
||||
def_id: DefId,
|
||||
}
|
||||
|
||||
impl<'clean> DisplayEnum<'clean> {
|
||||
fn render_into<W: fmt::Write>(
|
||||
self,
|
||||
cx: &Context<'_>,
|
||||
it: &clean::Item,
|
||||
is_type_alias: bool,
|
||||
w: &mut W,
|
||||
) -> fmt::Result {
|
||||
let non_stripped_variant_count = self.variants.iter().filter(|i| !i.is_stripped()).count();
|
||||
let variants_len = self.variants.len();
|
||||
let has_stripped_entries = variants_len != non_stripped_variant_count;
|
||||
|
||||
wrap_item(w, |w| {
|
||||
render_attributes_in_code(w, it, cx);
|
||||
if is_type_alias {
|
||||
// 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);
|
||||
}
|
||||
write!(
|
||||
w,
|
||||
"{}enum {}{}{}",
|
||||
visibility_print_with_space(it, cx),
|
||||
it.name.unwrap(),
|
||||
e.generics.print(cx),
|
||||
self.generics.print(cx),
|
||||
render_enum_fields(
|
||||
cx,
|
||||
Some(&e.generics),
|
||||
&e.variants,
|
||||
count_variants,
|
||||
e.has_stripped_entries(),
|
||||
it.is_non_exhaustive(),
|
||||
it.def_id().unwrap(),
|
||||
Some(self.generics),
|
||||
self.variants,
|
||||
non_stripped_variant_count,
|
||||
has_stripped_entries,
|
||||
self.is_non_exhaustive,
|
||||
self.def_id,
|
||||
),
|
||||
)
|
||||
})?;
|
||||
|
||||
write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
|
||||
|
||||
if count_variants != 0 {
|
||||
write!(w, "{}", item_variants(cx, it, &e.variants, it.def_id().unwrap()))?;
|
||||
}
|
||||
let def_id = it.item_id.expect_def_id();
|
||||
let layout_def_id = if is_type_alias {
|
||||
self.def_id
|
||||
} else {
|
||||
write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
|
||||
// We don't return the same `DefId` since the layout size of the type alias might be
|
||||
// different since we might have more information on the generics.
|
||||
def_id
|
||||
};
|
||||
|
||||
if non_stripped_variant_count != 0 {
|
||||
write!(w, "{}", item_variants(cx, it, self.variants, self.def_id))?;
|
||||
}
|
||||
write!(
|
||||
w,
|
||||
"{}{}",
|
||||
render_assoc_items(cx, it, def_id, AssocItemRender::All),
|
||||
document_type_layout(cx, def_id)
|
||||
document_type_layout(cx, layout_def_id)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn item_enum(cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) -> impl fmt::Display {
|
||||
fmt::from_fn(|w| {
|
||||
DisplayEnum {
|
||||
variants: &e.variants,
|
||||
generics: &e.generics,
|
||||
is_non_exhaustive: it.is_non_exhaustive(),
|
||||
def_id: it.def_id().unwrap(),
|
||||
}
|
||||
.render_into(cx, it, false, w)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -1955,27 +1992,59 @@ fn item_constant(
|
|||
})
|
||||
}
|
||||
|
||||
fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display {
|
||||
fmt::from_fn(|w| {
|
||||
struct DisplayStruct<'a> {
|
||||
ctor_kind: Option<CtorKind>,
|
||||
generics: &'a clean::Generics,
|
||||
fields: &'a [clean::Item],
|
||||
def_id: DefId,
|
||||
}
|
||||
|
||||
impl<'a> DisplayStruct<'a> {
|
||||
fn render_into<W: fmt::Write>(
|
||||
self,
|
||||
cx: &Context<'_>,
|
||||
it: &clean::Item,
|
||||
is_type_alias: bool,
|
||||
w: &mut W,
|
||||
) -> fmt::Result {
|
||||
wrap_item(w, |w| {
|
||||
render_attributes_in_code(w, it, cx);
|
||||
if is_type_alias {
|
||||
// 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);
|
||||
}
|
||||
write!(
|
||||
w,
|
||||
"{}",
|
||||
render_struct(it, Some(&s.generics), s.ctor_kind, &s.fields, "", true, cx)
|
||||
render_struct(it, Some(self.generics), self.ctor_kind, self.fields, "", true, cx)
|
||||
)
|
||||
})?;
|
||||
|
||||
let def_id = it.item_id.expect_def_id();
|
||||
if !is_type_alias {
|
||||
write!(w, "{}", document(cx, it, None, HeadingOffset::H2))?;
|
||||
}
|
||||
|
||||
let def_id = it.item_id.expect_def_id();
|
||||
write!(
|
||||
w,
|
||||
"{}{}{}{}",
|
||||
document(cx, it, None, HeadingOffset::H2),
|
||||
item_fields(cx, it, &s.fields, s.ctor_kind),
|
||||
"{}{}{}",
|
||||
item_fields(cx, it, self.fields, self.ctor_kind),
|
||||
render_assoc_items(cx, it, def_id, AssocItemRender::All),
|
||||
document_type_layout(cx, def_id),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn item_struct(cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) -> impl fmt::Display {
|
||||
fmt::from_fn(|w| {
|
||||
DisplayStruct {
|
||||
ctor_kind: s.ctor_kind,
|
||||
generics: &s.generics,
|
||||
fields: s.fields.as_slice(),
|
||||
def_id: it.def_id().unwrap(),
|
||||
}
|
||||
.render_into(cx, it, false, w)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -599,7 +599,7 @@ fn sidebar_enum<'a>(
|
|||
deref_id_map: &'a DefIdMap<String>,
|
||||
) {
|
||||
let mut variants = e
|
||||
.variants()
|
||||
.non_stripped_variants()
|
||||
.filter_map(|v| v.name)
|
||||
.map(|name| Link::new(format!("variant.{name}"), name.to_string()))
|
||||
.collect::<Vec<_>>();
|
||||
|
|
|
|||
|
|
@ -2,15 +2,16 @@
|
|||
{{ self.render_attributes_in_pre()|safe }}
|
||||
{{ self.render_union()|safe }}
|
||||
</code></pre>
|
||||
{{ self.document()|safe }}
|
||||
{% if self.fields_iter().peek().is_some() %}
|
||||
{% if !self.is_type_alias %}
|
||||
{{ self.document()|safe }}
|
||||
{% endif %}
|
||||
{% if self.fields_iter().next().is_some() %}
|
||||
<h2 id="fields" class="fields section-header"> {# #}
|
||||
Fields<a href="#fields" class="anchor">§</a> {# #}
|
||||
</h2>
|
||||
{% for (field, ty) in self.fields_iter() %}
|
||||
{% let name = field.name.expect("union field name") %}
|
||||
<span id="structfield.{{ name }}" {#+ #}
|
||||
class="{{ ItemType::StructField +}} section-header"> {# #}
|
||||
<span id="structfield.{{ name }}" class="{{ ItemType::StructField +}} section-header"> {# #}
|
||||
<a href="#structfield.{{ name }}" class="anchor field">§</a> {# #}
|
||||
<code>{{ name }}: {{+ self.print_ty(ty)|safe }}</code> {# #}
|
||||
</span>
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ impl JsonRenderer<'_> {
|
|||
})
|
||||
.collect();
|
||||
let docs = item.opt_doc_value();
|
||||
let attrs = item.attributes(self.tcx, self.cache(), true);
|
||||
let attrs = item.attributes_and_repr(self.tcx, self.cache(), true);
|
||||
let span = item.span(self.tcx);
|
||||
let visibility = item.visibility(self.tcx);
|
||||
let clean::ItemInner { name, item_id, .. } = *item.inner;
|
||||
|
|
|
|||
42
tests/rustdoc/type-alias/repr.rs
Normal file
42
tests/rustdoc/type-alias/repr.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// This test ensures that the `repr` attribute is displayed in type aliases.
|
||||
//
|
||||
// Regression test for <https://github.com/rust-lang/rust/issues/140739>.
|
||||
|
||||
#![crate_name = "foo"]
|
||||
|
||||
/// bla
|
||||
#[repr(C)]
|
||||
pub struct Foo1;
|
||||
|
||||
//@ has 'foo/type.Bar1.html'
|
||||
//@ has - '//*[@class="rust item-decl"]/code' '#[repr(C)]pub struct Bar1;'
|
||||
// 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
|
||||
pub type Bar1 = Foo1;
|
||||
|
||||
/// bla
|
||||
#[repr(C)]
|
||||
pub union Foo2 {
|
||||
pub a: u8,
|
||||
}
|
||||
|
||||
//@ has 'foo/type.Bar2.html'
|
||||
//@ matches - '//*[@class="rust item-decl"]' '#\[repr\(C\)\]\npub 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
|
||||
pub type Bar2 = Foo2;
|
||||
|
||||
/// bla
|
||||
#[repr(C)]
|
||||
pub enum Foo3 {
|
||||
A,
|
||||
}
|
||||
|
||||
//@ has 'foo/type.Bar3.html'
|
||||
//@ matches - '//*[@class="rust item-decl"]' '#\[repr\(C\)\]pub enum Bar3 \{*'
|
||||
// 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
|
||||
pub type Bar3 = Foo3;
|
||||
|
|
@ -61,7 +61,7 @@ pub type TypeAlias = X;
|
|||
pub type GenericTypeAlias = (Generic<(u32, ())>, Generic<u32>);
|
||||
|
||||
// Regression test for the rustdoc equivalent of #85103.
|
||||
//@ hasraw type_layout/type.Edges.html 'Encountered an error during type layout; the type failed to be normalized.'
|
||||
//@ hasraw type_layout/type.Edges.html 'Unable to compute type layout, possibly due to this type having generic parameters. Layout can only be computed for concrete, fully-instantiated types.'
|
||||
pub type Edges<'a, E> = std::borrow::Cow<'a, [E]>;
|
||||
|
||||
//@ !hasraw type_layout/trait.MyTrait.html 'Size: '
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue