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-Json: Add enum discriminant #101386

Merged
merged 2 commits into from
Sep 5, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1786,7 +1786,13 @@ pub(crate) fn clean_visibility(vis: ty::Visibility) -> Visibility {

pub(crate) fn clean_variant_def<'tcx>(variant: &ty::VariantDef, cx: &mut DocContext<'tcx>) -> Item {
let kind = match variant.ctor_kind {
CtorKind::Const => Variant::CLike,
CtorKind::Const => Variant::CLike(match variant.discr {
ty::VariantDiscr::Explicit(def_id) => Some(Discriminant {
expr: None,
value: print_evaluated_const(cx.tcx, def_id, false).unwrap(),
}),
ty::VariantDiscr::Relative(_) => None,
}),
CtorKind::Fn => Variant::Tuple(
variant.fields.iter().map(|field| clean_middle_field(field, cx)).collect(),
),
Expand All @@ -1803,6 +1809,7 @@ pub(crate) fn clean_variant_def<'tcx>(variant: &ty::VariantDef, cx: &mut DocCont

fn clean_variant_data<'tcx>(
variant: &hir::VariantData<'tcx>,
disr_expr: &Option<hir::AnonConst>,
cx: &mut DocContext<'tcx>,
) -> Variant {
match variant {
Expand All @@ -1813,7 +1820,17 @@ fn clean_variant_data<'tcx>(
hir::VariantData::Tuple(..) => {
Variant::Tuple(variant.fields().iter().map(|x| clean_field(x, cx)).collect())
}
hir::VariantData::Unit(..) => Variant::CLike,
hir::VariantData::Unit(..) => Variant::CLike(disr_expr.map(|disr| {
Discriminant {
expr: Some(print_const_expr(cx.tcx, disr.body)),
value: print_evaluated_const(
cx.tcx,
cx.tcx.hir().local_def_id(disr.hir_id).to_def_id(),
false,
)
.unwrap(),
}
})),
}
}

Expand Down Expand Up @@ -1967,7 +1984,7 @@ fn clean_maybe_renamed_item<'tcx>(
}

fn clean_variant<'tcx>(variant: &hir::Variant<'tcx>, cx: &mut DocContext<'tcx>) -> Item {
let kind = VariantItem(clean_variant_data(&variant.data, cx));
let kind = VariantItem(clean_variant_data(&variant.data, &variant.disr_expr, cx));
let what_rustc_thinks =
Item::from_hir_id_and_parts(variant.id, Some(variant.ident.name), kind, cx);
// don't show `pub` for variants, which are always public
Expand Down
14 changes: 11 additions & 3 deletions src/librustdoc/clean/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2098,7 +2098,7 @@ impl Enum {

#[derive(Clone, Debug)]
pub(crate) enum Variant {
CLike,
CLike(Option<Discriminant>),
Tuple(Vec<Item>),
Struct(VariantStruct),
}
Expand All @@ -2107,11 +2107,19 @@ impl Variant {
pub(crate) fn has_stripped_entries(&self) -> Option<bool> {
match *self {
Self::Struct(ref struct_) => Some(struct_.has_stripped_entries()),
Self::CLike | Self::Tuple(_) => None,
Self::CLike(..) | Self::Tuple(_) => None,
}
}
}

#[derive(Clone, Debug)]
pub(crate) struct Discriminant {
// In the case of cross crate re-exports, we don't have the nessesary information
// to reconstruct the expression of the discriminant, only the value.
pub(crate) expr: Option<String>,
pub(crate) value: String,
}

/// Small wrapper around [`rustc_span::Span`] that adds helper methods
/// and enforces calling [`rustc_span::Span::source_callsite()`].
#[derive(Copy, Clone, Debug)]
Expand Down Expand Up @@ -2338,7 +2346,7 @@ impl ConstantKind {
match *self {
ConstantKind::TyConst { .. } | ConstantKind::Anonymous { .. } => None,
ConstantKind::Extern { def_id } | ConstantKind::Local { def_id, .. } => {
print_evaluated_const(tcx, def_id)
print_evaluated_const(tcx, def_id, true)
}
}
}
Expand Down
34 changes: 25 additions & 9 deletions src/librustdoc/clean/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,15 +261,19 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String {
}
}

pub(crate) fn print_evaluated_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<String> {
pub(crate) fn print_evaluated_const(
tcx: TyCtxt<'_>,
def_id: DefId,
underscores_and_type: bool,
) -> Option<String> {
tcx.const_eval_poly(def_id).ok().and_then(|val| {
let ty = tcx.type_of(def_id);
match (val, ty.kind()) {
(_, &ty::Ref(..)) => None,
(ConstValue::Scalar(_), &ty::Adt(_, _)) => None,
(ConstValue::Scalar(_), _) => {
let const_ = mir::ConstantKind::from_value(val, ty);
Some(print_const_with_custom_print_scalar(tcx, const_))
Some(print_const_with_custom_print_scalar(tcx, const_, underscores_and_type))
}
_ => None,
}
Expand Down Expand Up @@ -302,23 +306,35 @@ fn format_integer_with_underscore_sep(num: &str) -> String {
.collect()
}

fn print_const_with_custom_print_scalar(tcx: TyCtxt<'_>, ct: mir::ConstantKind<'_>) -> String {
fn print_const_with_custom_print_scalar(
tcx: TyCtxt<'_>,
ct: mir::ConstantKind<'_>,
underscores_and_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::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Uint(ui)) => {
format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str())
if underscores_and_type {
format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str())
} else {
int.to_string()
}
}
(mir::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Int(i)) => {
let ty = tcx.lift(ct.ty()).unwrap();
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;
format!(
"{}{}",
format_integer_with_underscore_sep(&sign_extended_data.to_string()),
i.name_str()
)
if underscores_and_type {
format!(
"{}{}",
format_integer_with_underscore_sep(&sign_extended_data.to_string()),
i.name_str()
)
} else {
sign_extended_data.to_string()
}
}
_ => ct.to_string(),
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub(crate) trait DocFolder: Sized {
let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
VariantItem(Variant::Tuple(fields))
}
Variant::CLike => VariantItem(Variant::CLike),
Variant::CLike(disr) => VariantItem(Variant::CLike(disr)),
},
ExternCrateItem { src: _ }
| ImportItem(_)
Expand Down
3 changes: 2 additions & 1 deletion src/librustdoc/html/render/print_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1203,7 +1203,8 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::
let name = v.name.unwrap();
match *v.kind {
clean::VariantItem(ref var) => match var {
clean::Variant::CLike => write!(w, "{}", name),
// FIXME(#101337): Show discriminant
clean::Variant::CLike(..) => write!(w, "{}", name),
clean::Variant::Tuple(ref s) => {
write!(w, "{}(", name);
print_tuple_struct_fields(w, cx, s);
Expand Down
12 changes: 11 additions & 1 deletion src/librustdoc/json/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ impl FromWithTcx<clean::Variant> for Variant {
fn from_tcx(variant: clean::Variant, tcx: TyCtxt<'_>) -> Self {
use clean::Variant::*;
match variant {
CLike => Variant::Plain,
CLike(disr) => Variant::Plain(disr.map(convert_discriminant)),
Tuple(fields) => Variant::Tuple(
fields
.into_iter()
Expand All @@ -678,6 +678,16 @@ impl FromWithTcx<clean::Variant> for Variant {
}
}

fn convert_discriminant(disr: clean::Discriminant) -> Discriminant {
Discriminant {
// expr is only none if going throught the inlineing path, which gets
// `rustc_middle` types, not `rustc_hir`, but because JSON never inlines
// the expr is always some.
expr: disr.expr.unwrap(),
value: disr.value,
}
}

impl FromWithTcx<clean::Import> for Import {
fn from_tcx(import: clean::Import, tcx: TyCtxt<'_>) -> Self {
use clean::ImportKind::*;
Expand Down
2 changes: 1 addition & 1 deletion src/librustdoc/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub(crate) trait DocVisitor: Sized {
VariantItem(i) => match i {
Variant::Struct(j) => j.fields.iter().for_each(|x| self.visit_item(x)),
Variant::Tuple(fields) => fields.iter().for_each(|x| self.visit_item(x)),
Variant::CLike => {}
Variant::CLike(_) => {}
},
ExternCrateItem { src: _ }
| ImportItem(_)
Expand Down
21 changes: 19 additions & 2 deletions src/rustdoc-json-types/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::path::PathBuf;
use serde::{Deserialize, Serialize};

/// rustdoc format-version.
pub const FORMAT_VERSION: u32 = 18;
pub const FORMAT_VERSION: u32 = 19;

/// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information
/// about the language items in the local crate, as well as info about external items to allow
Expand Down Expand Up @@ -308,11 +308,28 @@ pub struct Enum {
#[serde(rename_all = "snake_case")]
#[serde(tag = "variant_kind", content = "variant_inner")]
pub enum Variant {
Plain,
Plain(Option<Discriminant>),
Tuple(Vec<Type>),
Struct(Vec<Id>),
}

#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Discriminant {
/// The expression that produced the discriminant.
///
/// Unlike `value`, this preserves the original formatting (eg suffixes,
/// hexadecimal, and underscores), making it unsuitable to be machine
/// interpreted.
///
/// In some cases, when the value is to complex, this may be `"{ _ }"`.
/// When this occurs is unstable, and may change without notice.
pub expr: String,
/// The numerical value of the discriminant. Stored as a string due to
/// JSON's poor support for large integers, and the fact that it would need
/// to store from [`i128::MIN`] to [`u128::MAX`].
pub value: String,
}

#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum StructType {
Expand Down
12 changes: 12 additions & 0 deletions src/test/rustdoc-json/enums/discriminant/basic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#[repr(i8)]
pub enum Ordering {
// @is "$.index[*][?(@.name=='Less')].inner.variant_inner.expr" '"-1"'
// @is "$.index[*][?(@.name=='Less')].inner.variant_inner.value" '"-1"'
Less = -1,
// @is "$.index[*][?(@.name=='Equal')].inner.variant_inner.expr" '"0"'
// @is "$.index[*][?(@.name=='Equal')].inner.variant_inner.value" '"0"'
Equal = 0,
// @is "$.index[*][?(@.name=='Greater')].inner.variant_inner.expr" '"1"'
// @is "$.index[*][?(@.name=='Greater')].inner.variant_inner.value" '"1"'
Greater = 1,
}
39 changes: 39 additions & 0 deletions src/test/rustdoc-json/enums/discriminant/expr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
pub enum Foo {
// @is "$.index[*][?(@.name=='Addition')].inner.variant_inner.value" '"0"'
// @is "$.index[*][?(@.name=='Addition')].inner.variant_inner.expr" '"{ _ }"'
Addition = 0 + 0,
// @is "$.index[*][?(@.name=='Bin')].inner.variant_inner.value" '"1"'
// @is "$.index[*][?(@.name=='Bin')].inner.variant_inner.expr" '"0b1"'
Bin = 0b1,
// @is "$.index[*][?(@.name=='Oct')].inner.variant_inner.value" '"2"'
// @is "$.index[*][?(@.name=='Oct')].inner.variant_inner.expr" '"0o2"'
Oct = 0o2,
// @is "$.index[*][?(@.name=='PubConst')].inner.variant_inner.value" '"3"'
// @is "$.index[*][?(@.name=='PubConst')].inner.variant_inner.expr" '"THREE"'
PubConst = THREE,
// @is "$.index[*][?(@.name=='Hex')].inner.variant_inner.value" '"4"'
// @is "$.index[*][?(@.name=='Hex')].inner.variant_inner.expr" '"0x4"'
Hex = 0x4,
// @is "$.index[*][?(@.name=='Cast')].inner.variant_inner.value" '"5"'
// @is "$.index[*][?(@.name=='Cast')].inner.variant_inner.expr" '"{ _ }"'
Cast = 5 as isize,
// @is "$.index[*][?(@.name=='PubCall')].inner.variant_inner.value" '"6"'
// @is "$.index[*][?(@.name=='PubCall')].inner.variant_inner.expr" '"{ _ }"'
PubCall = six(),
// @is "$.index[*][?(@.name=='PrivCall')].inner.variant_inner.value" '"7"'
// @is "$.index[*][?(@.name=='PrivCall')].inner.variant_inner.expr" '"{ _ }"'
PrivCall = seven(),
// @is "$.index[*][?(@.name=='PrivConst')].inner.variant_inner.value" '"8"'
// @is "$.index[*][?(@.name=='PrivConst')].inner.variant_inner.expr" '"EIGHT"'
PrivConst = EIGHT,
}

pub const THREE: isize = 3;
const EIGHT: isize = 8;

pub const fn six() -> isize {
6
}
const fn seven() -> isize {
7
}
43 changes: 43 additions & 0 deletions src/test/rustdoc-json/enums/discriminant/limits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// ignore-tidy-linelength
#![feature(repr128)]
#![allow(incomplete_features)]

#[repr(u64)]
pub enum U64 {
// @is "$.index[*][?(@.name=='U64Min')].inner.variant_inner.value" '"0"'
// @is "$.index[*][?(@.name=='U64Min')].inner.variant_inner.expr" '"u64::MIN"'
U64Min = u64::MIN,
// @is "$.index[*][?(@.name=='U64Max')].inner.variant_inner.value" '"18446744073709551615"'
// @is "$.index[*][?(@.name=='U64Max')].inner.variant_inner.expr" '"u64::MAX"'
U64Max = u64::MAX,
}

#[repr(i64)]
pub enum I64 {
// @is "$.index[*][?(@.name=='I64Min')].inner.variant_inner.value" '"-9223372036854775808"'
// @is "$.index[*][?(@.name=='I64Min')].inner.variant_inner.expr" '"i64::MIN"'
I64Min = i64::MIN,
// @is "$.index[*][?(@.name=='I64Max')].inner.variant_inner.value" '"9223372036854775807"'
// @is "$.index[*][?(@.name=='I64Max')].inner.variant_inner.expr" '"i64::MAX"'
I64Max = i64::MAX,
}

#[repr(u128)]
pub enum U128 {
// @is "$.index[*][?(@.name=='U128Min')].inner.variant_inner.value" '"0"'
// @is "$.index[*][?(@.name=='U128Min')].inner.variant_inner.expr" '"u128::MIN"'
U128Min = u128::MIN,
// @is "$.index[*][?(@.name=='U128Max')].inner.variant_inner.value" '"340282366920938463463374607431768211455"'
// @is "$.index[*][?(@.name=='U128Max')].inner.variant_inner.expr" '"u128::MAX"'
U128Max = u128::MAX,
}

#[repr(i128)]
pub enum I128 {
// @is "$.index[*][?(@.name=='I128Min')].inner.variant_inner.value" '"-170141183460469231731687303715884105728"'
// @is "$.index[*][?(@.name=='I128Min')].inner.variant_inner.expr" '"i128::MIN"'
I128Min = i128::MIN,
// @is "$.index[*][?(@.name=='I128Max')].inner.variant_inner.value" '"170141183460469231731687303715884105727"'
// @is "$.index[*][?(@.name=='I128Max')].inner.variant_inner.expr" '"i128::MAX"'
I128Max = i128::MAX,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#[repr(u32)]
pub enum Foo {
// @is "$.index[*][?(@.name=='Basic')].inner.variant_inner.value" '"0"'
// @is "$.index[*][?(@.name=='Basic')].inner.variant_inner.expr" '"0"'
Basic = 0,
// @is "$.index[*][?(@.name=='Suffix')].inner.variant_inner.value" '"10"'
// @is "$.index[*][?(@.name=='Suffix')].inner.variant_inner.expr" '"10u32"'
Suffix = 10u32,
// @is "$.index[*][?(@.name=='Underscore')].inner.variant_inner.value" '"100"'
// @is "$.index[*][?(@.name=='Underscore')].inner.variant_inner.expr" '"1_0_0"'
Underscore = 1_0_0,
// @is "$.index[*][?(@.name=='SuffixUnderscore')].inner.variant_inner.value" '"1000"'
// @is "$.index[*][?(@.name=='SuffixUnderscore')].inner.variant_inner.expr" '"1_0_0_0u32"'
SuffixUnderscore = 1_0_0_0u32,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pub enum Foo {
// @is "$.index[*][?(@.name=='Has')].inner.variant_inner" '{"expr":"0", "value":"0"}'
Has = 0,
// @is "$.index[*][?(@.name=='Doesnt')].inner.variant_inner" null
Doesnt,
// @is "$.index[*][?(@.name=='AlsoDoesnt')].inner.variant_inner" null
AlsoDoesnt,
// @is "$.index[*][?(@.name=='AlsoHas')].inner.variant_inner" '{"expr":"44", "value":"44"}'
AlsoHas = 44,
}