diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index ebf6c55ee35df..be2227f47af61 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1786,7 +1786,10 @@ 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: def_id }), + ty::VariantDiscr::Relative(_) => None, + }), CtorKind::Fn => Variant::Tuple( variant.fields.iter().map(|field| clean_middle_field(field, cx)).collect(), ), @@ -1803,6 +1806,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, cx: &mut DocContext<'tcx>, ) -> Variant { match variant { @@ -1813,7 +1817,10 @@ 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(disr.body), + value: cx.tcx.hir().local_def_id(disr.hir_id).to_def_id(), + })), } } @@ -1967,7 +1974,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 diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 2808b400a0b53..d6bb7c6c4fc89 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -2098,7 +2098,7 @@ impl Enum { #[derive(Clone, Debug)] pub(crate) enum Variant { - CLike, + CLike(Option), Tuple(Vec), Struct(VariantStruct), } @@ -2107,11 +2107,31 @@ impl Variant { pub(crate) fn has_stripped_entries(&self) -> Option { 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(super) expr: Option, + pub(super) value: DefId, +} + +impl Discriminant { + /// Will be `None` in the case of cross-crate reexports, and may be + /// simplified + pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> Option { + self.expr.map(|body| print_const_expr(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() + } +} + /// Small wrapper around [`rustc_span::Span`] that adds helper methods /// and enforces calling [`rustc_span::Span::source_callsite()`]. #[derive(Copy, Clone, Debug)] @@ -2338,7 +2358,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) } } } diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index ac9ab33961676..a9d511ae11e8b 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -261,7 +261,11 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String { } } -pub(crate) fn print_evaluated_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option { +pub(crate) fn print_evaluated_const( + tcx: TyCtxt<'_>, + def_id: DefId, + underscores_and_type: bool, +) -> Option { tcx.const_eval_poly(def_id).ok().and_then(|val| { let ty = tcx.type_of(def_id); match (val, ty.kind()) { @@ -269,7 +273,7 @@ pub(crate) fn print_evaluated_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option 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, } @@ -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(), } diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs index 6b7e67e2ce342..ed702f5c4a9cc 100644 --- a/src/librustdoc/fold.rs +++ b/src/librustdoc/fold.rs @@ -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(_) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index d63d4c2d159ba..cfa4509428f10 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -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); diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs index 5c0c023c64e58..4d00931673006 100644 --- a/src/librustdoc/json/conversions.rs +++ b/src/librustdoc/json/conversions.rs @@ -662,7 +662,7 @@ impl FromWithTcx 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(|disr| disr.into_tcx(tcx))), Tuple(fields) => Variant::Tuple( fields .into_iter() @@ -678,6 +678,18 @@ impl FromWithTcx for Variant { } } +impl FromWithTcx for Discriminant { + fn from_tcx(disr: clean::Discriminant, tcx: TyCtxt<'_>) -> Self { + 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(tcx).unwrap(), + value: disr.value(tcx), + } + } +} + impl FromWithTcx for Import { fn from_tcx(import: clean::Import, tcx: TyCtxt<'_>) -> Self { use clean::ImportKind::*; diff --git a/src/librustdoc/visit.rs b/src/librustdoc/visit.rs index 0bb41977c97ca..c40274394f344 100644 --- a/src/librustdoc/visit.rs +++ b/src/librustdoc/visit.rs @@ -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(_) diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs index 7dcad66b1f992..d25f68b3743d6 100644 --- a/src/rustdoc-json-types/lib.rs +++ b/src/rustdoc-json-types/lib.rs @@ -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 @@ -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), Tuple(Vec), Struct(Vec), } +#[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 { diff --git a/src/test/rustdoc-json/enums/discriminant/basic.rs b/src/test/rustdoc-json/enums/discriminant/basic.rs new file mode 100644 index 0000000000000..8c221615aa753 --- /dev/null +++ b/src/test/rustdoc-json/enums/discriminant/basic.rs @@ -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, +} diff --git a/src/test/rustdoc-json/enums/discriminant/expr.rs b/src/test/rustdoc-json/enums/discriminant/expr.rs new file mode 100644 index 0000000000000..235b0b47381fc --- /dev/null +++ b/src/test/rustdoc-json/enums/discriminant/expr.rs @@ -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 +} diff --git a/src/test/rustdoc-json/enums/discriminant/limits.rs b/src/test/rustdoc-json/enums/discriminant/limits.rs new file mode 100644 index 0000000000000..8df73d78d237f --- /dev/null +++ b/src/test/rustdoc-json/enums/discriminant/limits.rs @@ -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, +} diff --git a/src/test/rustdoc-json/enums/discriminant/num_underscore_and_suffix.rs b/src/test/rustdoc-json/enums/discriminant/num_underscore_and_suffix.rs new file mode 100644 index 0000000000000..3417baa0760e1 --- /dev/null +++ b/src/test/rustdoc-json/enums/discriminant/num_underscore_and_suffix.rs @@ -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, +} diff --git a/src/test/rustdoc-json/enums/discriminant/only_some_have_discriminant.rs b/src/test/rustdoc-json/enums/discriminant/only_some_have_discriminant.rs new file mode 100644 index 0000000000000..6af944a2219ef --- /dev/null +++ b/src/test/rustdoc-json/enums/discriminant/only_some_have_discriminant.rs @@ -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, +}