Skip to content

Commit

Permalink
Auto merge of #116142 - GuillaumeGomez:enum-variant-display, r=fmease
Browse files Browse the repository at this point in the history
[rustdoc] Show enum discrimant if it is a C-like variant

Fixes #101337.

We currently display values for associated constant items in traits:

![image](https://github.com/rust-lang/rust/assets/3050060/03e566ec-c670-47b4-8ca2-b982baa7a0f4)

And we also display constant values like [here](file:///home/imperio/rust/rust/build/x86_64-unknown-linux-gnu/doc/std/f32/consts/constant.E.html).

I think that for coherency, we should display values of C-like enum variants.

With this change, it looks like this:

![image](https://github.com/rust-lang/rust/assets/3050060/b53fbbe0-bdb1-4289-8537-f2dd4988e9ac)

As for the display of the constant value itself, I used what we already have to keep coherency.

We display the C-like variants value in the following scenario:
 1. It is a C-like variant with a value set => all the time
 2. It is a C-like variant without a value set: All other variants are C-like variants and at least one them has its value set.

Here is the result in code:

```rust
// Ax and Bx value will be displayed.
enum A {
    Ax = 12,
    Bx,
}

// Ax and Bx value will not be displayed
enum B {
    Ax,
    Bx,
}

// Bx value will not be displayed
enum C {
    Ax(u32),
    Bx,
}

// Bx value will not be displayed, Cx value will be displayed.
#[repr(u32)]
enum D {
    Ax(u32),
    Bx,
    Cx = 12,
}
```

r? `@notriddle`
  • Loading branch information
bors committed Oct 9, 2023
2 parents 7ed044c + 1210aac commit be581d9
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 31 deletions.
7 changes: 3 additions & 4 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2084,9 +2084,8 @@ impl Discriminant {
pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> Option<String> {
self.expr.map(|body| rendered_const(tcx, body))
}
/// Will always be a machine readable number, without underscores or suffixes.
pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> String {
print_evaluated_const(tcx, self.value, false).unwrap()
pub(crate) fn value(&self, tcx: TyCtxt<'_>, with_underscores: bool) -> String {
print_evaluated_const(tcx, self.value, with_underscores, false).unwrap()
}
}

Expand Down Expand Up @@ -2331,7 +2330,7 @@ impl ConstantKind {
match *self {
ConstantKind::TyConst { .. } | ConstantKind::Anonymous { .. } => None,
ConstantKind::Extern { def_id } | ConstantKind::Local { def_id, .. } => {
print_evaluated_const(tcx, def_id, true)
print_evaluated_const(tcx, def_id, true, true)
}
}
}
Expand Down
28 changes: 17 additions & 11 deletions src/librustdoc/clean/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,8 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String {
pub(crate) fn print_evaluated_const(
tcx: TyCtxt<'_>,
def_id: DefId,
underscores_and_type: bool,
with_underscores: bool,
with_type: bool,
) -> Option<String> {
tcx.const_eval_poly(def_id).ok().and_then(|val| {
let ty = tcx.type_of(def_id).instantiate_identity();
Expand All @@ -284,7 +285,7 @@ pub(crate) fn print_evaluated_const(
(mir::ConstValue::Scalar(_), &ty::Adt(_, _)) => None,
(mir::ConstValue::Scalar(_), _) => {
let const_ = mir::Const::from_value(val, ty);
Some(print_const_with_custom_print_scalar(tcx, const_, underscores_and_type))
Some(print_const_with_custom_print_scalar(tcx, const_, with_underscores, with_type))
}
_ => None,
}
Expand Down Expand Up @@ -320,32 +321,37 @@ fn format_integer_with_underscore_sep(num: &str) -> String {
fn print_const_with_custom_print_scalar<'tcx>(
tcx: TyCtxt<'tcx>,
ct: mir::Const<'tcx>,
underscores_and_type: bool,
with_underscores: bool,
with_type: bool,
) -> String {
// Use a slightly different format for integer types which always shows the actual value.
// For all other types, fallback to the original `pretty_print_const`.
match (ct, ct.ty().kind()) {
(mir::Const::Val(mir::ConstValue::Scalar(int), _), ty::Uint(ui)) => {
if underscores_and_type {
format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str())
let mut output = if with_underscores {
format_integer_with_underscore_sep(&int.to_string())
} else {
int.to_string()
};
if with_type {
output += ui.name_str();
}
output
}
(mir::Const::Val(mir::ConstValue::Scalar(int), _), ty::Int(i)) => {
let ty = ct.ty();
let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size;
let data = int.assert_bits(size);
let sign_extended_data = size.sign_extend(data) as i128;
if underscores_and_type {
format!(
"{}{}",
format_integer_with_underscore_sep(&sign_extended_data.to_string()),
i.name_str()
)
let mut output = if with_underscores {
format_integer_with_underscore_sep(&sign_extended_data.to_string())
} else {
sign_extended_data.to_string()
};
if with_type {
output += i.name_str();
}
output
}
_ => ct.to_string(),
}
Expand Down
106 changes: 91 additions & 15 deletions src/librustdoc/html/render/print_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::CtorKind;
use rustc_hir::def_id::DefId;
use rustc_index::IndexVec;
use rustc_middle::middle::stability;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_target::abi::VariantIdx;
use std::cell::{RefCell, RefMut};
use std::cmp::Ordering;
use std::fmt;
Expand Down Expand Up @@ -1257,13 +1259,14 @@ fn item_type_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &c
w,
cx,
Some(&t.generics),
variants_iter(),
&variants,
variants_count,
has_stripped_entries,
*is_non_exhaustive,
it.def_id().unwrap(),
)
});
item_variants(w, cx, it, variants_iter());
item_variants(w, cx, it, &variants);
}
clean::TypeAliasInnerType::Union { fields } => {
wrap_item(w, |w| {
Expand Down Expand Up @@ -1416,36 +1419,81 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
it.name.unwrap(),
e.generics.print(cx),
);

render_enum_fields(
w,
cx,
Some(&e.generics),
e.variants(),
&e.variants,
count_variants,
e.has_stripped_entries(),
it.is_non_exhaustive(),
it.def_id().unwrap(),
);
});

write!(w, "{}", document(cx, it, None, HeadingOffset::H2));

if count_variants != 0 {
item_variants(w, cx, it, e.variants());
item_variants(w, cx, it, &e.variants);
}
let def_id = it.item_id.expect_def_id();
write!(w, "{}", render_assoc_items(cx, it, def_id, AssocItemRender::All));
write!(w, "{}", document_type_layout(cx, def_id));
}

fn render_enum_fields<'a>(
/// It'll return true if all variants are C-like variants and if at least one of them has a value
/// set.
fn should_show_enum_discriminant(variants: &IndexVec<VariantIdx, clean::Item>) -> bool {
let mut has_variants_with_value = false;
for variant in variants {
if let clean::VariantItem(ref var) = *variant.kind &&
matches!(var.kind, clean::VariantKind::CLike)
{
has_variants_with_value |= var.discriminant.is_some();
} else {
return false;
}
}
has_variants_with_value
}

fn display_c_like_variant(
w: &mut Buffer,
cx: &mut Context<'_>,
item: &clean::Item,
variant: &clean::Variant,
index: VariantIdx,
should_show_enum_discriminant: bool,
enum_def_id: DefId,
) {
let name = item.name.unwrap();
if let Some(ref value) = variant.discriminant {
write!(w, "{} = {}", name.as_str(), value.value(cx.tcx(), true));
} else if should_show_enum_discriminant {
let adt_def = cx.tcx().adt_def(enum_def_id);
let discr = adt_def.discriminant_for_variant(cx.tcx(), index);
if discr.ty.is_signed() {
write!(w, "{} = {}", name.as_str(), discr.val as i128);
} else {
write!(w, "{} = {}", name.as_str(), discr.val);
}
} else {
w.write_str(name.as_str());
}
}

fn render_enum_fields(
mut w: &mut Buffer,
cx: &mut Context<'_>,
g: Option<&clean::Generics>,
variants: impl Iterator<Item = &'a clean::Item>,
variants: &IndexVec<VariantIdx, clean::Item>,
count_variants: usize,
has_stripped_entries: bool,
is_non_exhaustive: bool,
enum_def_id: DefId,
) {
let should_show_enum_discriminant = should_show_enum_discriminant(variants);
if !g.is_some_and(|g| print_where_clause_and_check(w, g, cx)) {
// If there wasn't a `where` clause, we add a whitespace.
w.write_str(" ");
Expand All @@ -1461,15 +1509,24 @@ fn render_enum_fields<'a>(
toggle_open(&mut w, format_args!("{count_variants} variants"));
}
const TAB: &str = " ";
for v in variants {
for (index, v) in variants.iter_enumerated() {
if v.is_stripped() {
continue;
}
w.write_str(TAB);
let name = v.name.unwrap();
match *v.kind {
// FIXME(#101337): Show discriminant
clean::VariantItem(ref var) => match var.kind {
clean::VariantKind::CLike => w.write_str(name.as_str()),
clean::VariantKind::CLike => display_c_like_variant(
w,
cx,
v,
var,
index,
should_show_enum_discriminant,
enum_def_id,
),
clean::VariantKind::Tuple(ref s) => {
write!(w, "{name}({})", print_tuple_struct_fields(cx, s),);
write!(w, "{}({})", v.name.unwrap(), print_tuple_struct_fields(cx, s));
}
clean::VariantKind::Struct(ref s) => {
render_struct(w, v, None, None, &s.fields, TAB, false, cx);
Expand All @@ -1490,11 +1547,11 @@ fn render_enum_fields<'a>(
}
}

fn item_variants<'a>(
fn item_variants(
w: &mut Buffer,
cx: &mut Context<'_>,
it: &clean::Item,
variants: impl Iterator<Item = &'a clean::Item>,
variants: &IndexVec<VariantIdx, clean::Item>,
) {
let tcx = cx.tcx();
write!(
Expand All @@ -1507,7 +1564,11 @@ fn item_variants<'a>(
document_non_exhaustive_header(it),
document_non_exhaustive(it)
);
for variant in variants {
let should_show_enum_discriminant = should_show_enum_discriminant(variants);
for (index, variant) in variants.iter_enumerated() {
if variant.is_stripped() {
continue;
}
let id = cx.derive_id(format!("{}.{}", ItemType::Variant, variant.name.unwrap()));
write!(
w,
Expand All @@ -1522,7 +1583,22 @@ fn item_variants<'a>(
it.const_stable_since(tcx),
" rightside",
);
write!(w, "<h3 class=\"code-header\">{name}", name = variant.name.unwrap());
w.write_str("<h3 class=\"code-header\">");
if let clean::VariantItem(ref var) = *variant.kind &&
let clean::VariantKind::CLike = var.kind
{
display_c_like_variant(
w,
cx,
variant,
var,
index,
should_show_enum_discriminant,
it.def_id().unwrap(),
);
} else {
w.write_str(variant.name.unwrap().as_str());
}

let clean::VariantItem(variant_data) = &*variant.kind else { unreachable!() };

Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/json/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -744,7 +744,7 @@ impl FromWithTcx<clean::Discriminant> for Discriminant {
// `rustc_middle` types, not `rustc_hir`, but because JSON never inlines
// the expr is always some.
expr: disr.expr(tcx).unwrap(),
value: disr.value(tcx),
value: disr.value(tcx, false),
}
}
}
Expand Down
24 changes: 24 additions & 0 deletions tests/rustdoc/auxiliary/enum-variant.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#![crate_name = "bar"]

pub enum E {
A = 12,
B,
C = 1245,
}

pub enum F {
A,
B,
}

#[repr(u32)]
pub enum G {
A = 12,
B,
C(u32),
}

pub enum H {
A,
C(u32),
}
Loading

0 comments on commit be581d9

Please sign in to comment.