Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[rustdoc] Show enum discriminant if it is a C-like variant #116142

Merged
merged 9 commits into from
Oct 9, 2023
7 changes: 3 additions & 4 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2091,9 +2091,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 @@ -2348,7 +2347,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() {
GuillaumeGomez marked this conversation as resolved.
Show resolved Hide resolved
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 @@ -745,7 +745,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
Loading