From 811bdeee002827fbc950ac52a6175e933567823c Mon Sep 17 00:00:00 2001 From: Ohad Ravid Date: Wed, 11 Dec 2019 14:50:19 +0100 Subject: [PATCH] Show value for consts in the documentation --- Cargo.lock | 1 + src/libcore/num/f32.rs | 2 +- src/librustdoc/Cargo.toml | 1 + src/librustdoc/clean/inline.rs | 11 ++- src/librustdoc/clean/mod.rs | 15 +++- src/librustdoc/clean/types.rs | 2 + src/librustdoc/clean/utils.rs | 77 ++++++++++++++++++- src/librustdoc/html/render.rs | 26 ++++++- src/test/rustdoc/const-generics/const-impl.rs | 4 +- src/test/rustdoc/dont-show-const-contents.rs | 5 -- src/test/rustdoc/show-const-contents.rs | 64 +++++++++++++++ 11 files changed, 192 insertions(+), 16 deletions(-) delete mode 100644 src/test/rustdoc/dont-show-const-contents.rs create mode 100644 src/test/rustdoc/show-const-contents.rs diff --git a/Cargo.lock b/Cargo.lock index 4c819e88809b2..5132f77e578a7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3869,6 +3869,7 @@ dependencies = [ name = "rustdoc" version = "0.0.0" dependencies = [ + "itertools 0.8.0", "minifier", "pulldown-cmark 0.5.3", "rustc-rayon", diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index e1e6c57a0fb7d..fd7b7cf0b3490 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -30,7 +30,7 @@ pub const DIGITS: u32 = 6; /// /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon #[stable(feature = "rust1", since = "1.0.0")] -pub const EPSILON: f32 = 1.19209290e-07_f32; +pub const EPSILON: f32 = 1.1920929e-7_f32; /// Smallest finite `f32` value. #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index f2822916d3cdf..74e46b1eae362 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -15,3 +15,4 @@ rayon = { version = "0.3.0", package = "rustc-rayon" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tempfile = "3" +itertools = "0.8" diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 1c519d6fe1beb..35e26819b10b4 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -450,7 +450,16 @@ pub fn print_inlined_const(cx: &DocContext<'_>, did: DefId) -> String { } fn build_const(cx: &DocContext<'_>, did: DefId) -> clean::Constant { - clean::Constant { type_: cx.tcx.type_of(did).clean(cx), expr: print_inlined_const(cx, did) } + clean::Constant { + type_: cx.tcx.type_of(did).clean(cx), + expr: print_inlined_const(cx, did), + value: clean::utils::print_evaluated_const(cx, did), + is_literal: cx + .tcx + .hir() + .as_local_hir_id(did) + .map_or(false, |hir_id| clean::utils::is_literal_expr(cx, hir_id)), + } } fn build_static(cx: &DocContext<'_>, did: DefId, mutable: bool) -> clean::Static { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 5f69c47bcdb15..e5c0a6aadce43 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -435,6 +435,8 @@ impl Clean for hir::ConstArg { Constant { type_: cx.tcx.type_of(cx.tcx.hir().body_owner_def_id(self.value.body)).clean(cx), expr: print_const_expr(cx, self.value.body), + value: None, + is_literal: is_literal_expr(cx, self.value.body.hir_id), } } } @@ -1717,7 +1719,12 @@ impl<'tcx> Clean for Ty<'tcx> { impl<'tcx> Clean for ty::Const<'tcx> { fn clean(&self, cx: &DocContext<'_>) -> Constant { - Constant { type_: self.ty.clean(cx), expr: format!("{}", self) } + Constant { + type_: self.ty.clean(cx), + expr: format!("{}", self), + value: None, + is_literal: false, + } } } @@ -2062,17 +2069,21 @@ impl Clean for doctree::Static<'_> { impl Clean for doctree::Constant<'_> { fn clean(&self, cx: &DocContext<'_>) -> Item { + let def_id = cx.tcx.hir().local_def_id(self.id); + Item { name: Some(self.name.clean(cx)), attrs: self.attrs.clean(cx), source: self.whence.clean(cx), - def_id: cx.tcx.hir().local_def_id(self.id), + def_id, visibility: self.vis.clean(cx), stability: cx.stability(self.id).clean(cx), deprecation: cx.deprecation(self.id).clean(cx), inner: ConstantItem(Constant { type_: self.type_.clean(cx), expr: print_const_expr(cx, self.expr), + value: print_evaluated_const(cx, def_id), + is_literal: is_literal_expr(cx, self.expr.hir_id), }), } } diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 1d9a9981053cb..e7b9964d4652a 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -1460,6 +1460,8 @@ pub struct Static { pub struct Constant { pub type_: Type, pub expr: String, + pub value: Option, + pub is_literal: bool, } #[derive(Clone, PartialEq, Debug)] diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index df2f27aca47fe..e52afe501c34f 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -8,17 +8,18 @@ use crate::clean::{ }; use crate::core::DocContext; +use itertools::Itertools; use rustc::hir; use rustc::hir::def::{DefKind, Res}; use rustc::hir::def_id::{DefId, LOCAL_CRATE}; +use rustc::mir::interpret::{sign_extend, ConstValue, Scalar}; use rustc::ty::subst::{GenericArgKind, SubstsRef}; use rustc::ty::{self, DefIdTree, Ty}; use rustc::util::nodemap::FxHashSet; +use std::mem; use syntax_pos; use syntax_pos::symbol::{kw, sym, Symbol}; -use std::mem; - pub fn krate(mut cx: &mut DocContext<'_>) -> Crate { use crate::visit_lib::LibEmbargoVisitor; @@ -482,8 +483,78 @@ pub fn print_const(cx: &DocContext<'_>, n: &ty::Const<'_>) -> String { } } +pub fn print_evaluated_const(cx: &DocContext<'_>, def_id: DefId) -> Option { + let value = + cx.tcx.const_eval_poly(def_id).ok().and_then(|value| match (value.val, &value.ty.kind) { + (_, ty::Ref(..)) => None, + (ty::ConstKind::Value(ConstValue::Scalar(_)), ty::Adt(_, _)) => None, + (ty::ConstKind::Value(ConstValue::Scalar(_)), _) => { + Some(print_const_with_custom_print_scalar(cx, value)) + } + _ => None, + }); + + value +} + +fn format_integer_with_underscore_sep(num: &str) -> String { + let num_chars: Vec<_> = num.chars().collect(); + let num_start_index = if num_chars.get(0) == Some(&'-') { 1 } else { 0 }; + + num_chars[..num_start_index] + .iter() + .chain(num_chars[num_start_index..].rchunks(3).rev().intersperse(&['_']).flatten()) + .collect() +} + +fn print_const_with_custom_print_scalar(cx: &DocContext<'_>, ct: &'tcx ty::Const<'tcx>) -> 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.val, &ct.ty.kind) { + (ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data, .. })), ty::Uint(ui)) => { + format!("{}{}", format_integer_with_underscore_sep(&data.to_string()), ui.name_str()) + } + (ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data, .. })), ty::Int(i)) => { + let ty = cx.tcx.lift(&ct.ty).unwrap(); + let size = cx.tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size; + let sign_extended_data = sign_extend(data, size) as i128; + + format!( + "{}{}", + format_integer_with_underscore_sep(&sign_extended_data.to_string()), + i.name_str() + ) + } + _ => ct.to_string(), + } +} + +pub fn is_literal_expr(cx: &DocContext<'_>, hir_id: hir::HirId) -> bool { + if let hir::Node::Expr(expr) = cx.tcx.hir().get(hir_id) { + if let hir::ExprKind::Lit(_) = &expr.kind { + return true; + } + + if let hir::ExprKind::Unary(hir::UnOp::UnNeg, expr) = &expr.kind { + if let hir::ExprKind::Lit(_) = &expr.kind { + return true; + } + } + } + + false +} + pub fn print_const_expr(cx: &DocContext<'_>, body: hir::BodyId) -> String { - cx.tcx.hir().hir_to_pretty_string(body.hir_id) + let value = &cx.tcx.hir().body(body).value; + + let snippet = if !value.span.from_expansion() { + cx.sess().source_map().span_to_snippet(value.span).ok() + } else { + None + }; + + snippet.unwrap_or_else(|| cx.tcx.hir().hir_to_pretty_string(body.hir_id)) } /// Given a type Path, resolve it to a Type using the TyCtxt diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 506f84baaa58f..f763255a93290 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -2272,14 +2272,36 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec { fn item_constant(w: &mut Buffer, cx: &Context, it: &clean::Item, c: &clean::Constant) { write!(w, "
");
     render_attributes(w, it, false);
+
     write!(
         w,
         "{vis}const \
-               {name}: {typ}
", + {name}: {typ}", vis = it.visibility.print_with_space(), name = it.name.as_ref().unwrap(), - typ = c.type_.print() + typ = c.type_.print(), ); + + if c.value.is_some() || c.is_literal { + write!(w, " = {expr};", expr = c.expr); + } else { + write!(w, ";"); + } + + if let Some(value) = &c.value { + if !c.is_literal { + let value_lowercase = value.to_lowercase(); + let expr_lowercase = c.expr.to_lowercase(); + + if value_lowercase != expr_lowercase + && value_lowercase.trim_end_matches("i32") != expr_lowercase + { + write!(w, " // {value}", value = value); + } + } + } + + write!(w, ""); document(w, cx, it) } diff --git a/src/test/rustdoc/const-generics/const-impl.rs b/src/test/rustdoc/const-generics/const-impl.rs index 2d506787b3b80..819adfeb9c775 100644 --- a/src/test/rustdoc/const-generics/const-impl.rs +++ b/src/test/rustdoc/const-generics/const-impl.rs @@ -17,14 +17,14 @@ pub struct VSet { inner: Vec, } -// @has foo/struct.VSet.html '//h3[@id="impl"]/code' 'impl VSet' +// @has foo/struct.VSet.html '//h3[@id="impl"]/code' 'impl VSet' impl VSet { pub fn new() -> Self { Self { inner: Vec::new() } } } -// @has foo/struct.VSet.html '//h3[@id="impl-1"]/code' 'impl VSet' +// @has foo/struct.VSet.html '//h3[@id="impl-1"]/code' 'impl VSet' impl VSet { pub fn new() -> Self { Self { inner: Vec::new() } diff --git a/src/test/rustdoc/dont-show-const-contents.rs b/src/test/rustdoc/dont-show-const-contents.rs deleted file mode 100644 index 656d579e4f3bc..0000000000000 --- a/src/test/rustdoc/dont-show-const-contents.rs +++ /dev/null @@ -1,5 +0,0 @@ -// Test that the contents of constants are not displayed as part of the -// documentation. - -// @!has dont_show_const_contents/constant.CONST_S.html 'dont show this' -pub const CONST_S: &'static str = "dont show this"; diff --git a/src/test/rustdoc/show-const-contents.rs b/src/test/rustdoc/show-const-contents.rs new file mode 100644 index 0000000000000..6d95f7827a1d7 --- /dev/null +++ b/src/test/rustdoc/show-const-contents.rs @@ -0,0 +1,64 @@ +// Test that the contents of constants are displayed as part of the +// documentation. + +// @has show_const_contents/constant.CONST_S.html 'show this' +// @!has show_const_contents/constant.CONST_S.html '; //' +pub const CONST_S: &'static str = "show this"; + +// @has show_const_contents/constant.CONST_I32.html '= 42;' +// @!has show_const_contents/constant.CONST_I32.html '; //' +pub const CONST_I32: i32 = 42; + +// @has show_const_contents/constant.CONST_I32_HEX.html '= 0x42;' +// @!has show_const_contents/constant.CONST_I32_HEX.html '; //' +pub const CONST_I32_HEX: i32 = 0x42; + +// @has show_const_contents/constant.CONST_NEG_I32.html '= -42;' +// @!has show_const_contents/constant.CONST_NEG_I32.html '; //' +pub const CONST_NEG_I32: i32 = -42; + +// @has show_const_contents/constant.CONST_EQ_TO_VALUE_I32.html '= 42i32;' +// @!has show_const_contents/constant.CONST_EQ_TO_VALUE_I32.html '// 42i32' +pub const CONST_EQ_TO_VALUE_I32: i32 = 42i32; + +// @has show_const_contents/constant.CONST_CALC_I32.html '= 42 + 1; // 43i32' +pub const CONST_CALC_I32: i32 = 42 + 1; + +// @!has show_const_contents/constant.CONST_REF_I32.html '= &42;' +// @!has show_const_contents/constant.CONST_REF_I32.html '; //' +pub const CONST_REF_I32: &'static i32 = &42; + +// @has show_const_contents/constant.CONST_I32_MAX.html '= i32::max_value(); // 2_147_483_647i32' +pub const CONST_I32_MAX: i32 = i32::max_value(); + +// @!has show_const_contents/constant.UNIT.html '= ();' +// @!has show_const_contents/constant.UNIT.html '; //' +pub const UNIT: () = (); + +pub struct MyType(i32); + +// @!has show_const_contents/constant.MY_TYPE.html '= MyType(42);' +// @!has show_const_contents/constant.MY_TYPE.html '; //' +pub const MY_TYPE: MyType = MyType(42); + +pub struct MyTypeWithStr(&'static str); + +// @!has show_const_contents/constant.MY_TYPE_WITH_STR.html '= MyTypeWithStr("show this");' +// @!has show_const_contents/constant.MY_TYPE_WITH_STR.html '; //' +pub const MY_TYPE_WITH_STR: MyTypeWithStr = MyTypeWithStr("show this"); + +// @has show_const_contents/constant.EPSILON.html '1.1920929e-7f32;' +// @!has show_const_contents/constant.EPSILON.html '; //' +pub use std::f32::EPSILON; + +// @has show_const_contents/constant.MAX.html '= i32::max_value(); // 2_147_483_647i32' +pub use std::i32::MAX; + +macro_rules! int_module { + ($T:ident) => ( + pub const MIN: $T = $T::min_value(); + ) +} + +// @has show_const_contents/constant.MIN.html '= i16::min_value(); // -32_768i16' +int_module!(i16);