about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-12-24 14:40:33 +0000
committerbors <bors@rust-lang.org>2019-12-24 14:40:33 +0000
commitf6dca76be55dd6bb8d0d7d3b6bc39ae8373db506 (patch)
tree7a11fc8bb67b4bdcdc6c49860d1f41381411f64f
parent84d8f9ddd3819501735d30b3c584c6935ef6c9da (diff)
parent811bdeee002827fbc950ac52a6175e933567823c (diff)
downloadrust-f6dca76be55dd6bb8d0d7d3b6bc39ae8373db506.tar.gz
rust-f6dca76be55dd6bb8d0d7d3b6bc39ae8373db506.zip
Auto merge of #66221 - ohadravid:doc-constants, r=Dylan-DPC
Show the actual value of constant values in the documentation

Fixes #66099, making rustdoc show evaluated constant scalar values.

![image](https://user-images.githubusercontent.com/2358365/68474827-c7a95e80-0226-11ea-818a-ded7bbff861f.png)

where `main.rs` is
```
pub const VAL3: i32 = i32::max_value();
pub const VAL4: i32 = i32::max_value() - 1;
```

As a fallback, when a constant value is not evaluated (either because of an error or because it isn't a scalar), the original expression is used for consistency.

I mimicked the way min/max values of integers are [`pretty_print`ed](https://github.com/rust-lang/rust/blob/master/src/librustc/ty/print/pretty.rs#L900), to show both the value a the "hint". While a little goofy for `std`, in user crates I think it's actually rather helpful.
-rw-r--r--Cargo.lock1
-rw-r--r--src/libcore/num/f32.rs2
-rw-r--r--src/librustdoc/Cargo.toml1
-rw-r--r--src/librustdoc/clean/inline.rs11
-rw-r--r--src/librustdoc/clean/mod.rs15
-rw-r--r--src/librustdoc/clean/types.rs2
-rw-r--r--src/librustdoc/clean/utils.rs77
-rw-r--r--src/librustdoc/html/render.rs26
-rw-r--r--src/test/rustdoc/const-generics/const-impl.rs4
-rw-r--r--src/test/rustdoc/dont-show-const-contents.rs5
-rw-r--r--src/test/rustdoc/show-const-contents.rs64
11 files changed, 192 insertions, 16 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 4c819e88809..5132f77e578 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 e1e6c57a0fb..fd7b7cf0b34 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 f2822916d3c..74e46b1eae3 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 1c519d6fe1b..35e26819b10 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 5f69c47bcdb..e5c0a6aadce 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -435,6 +435,8 @@ impl Clean<Constant> 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<Type> for Ty<'tcx> {
 
 impl<'tcx> Clean<Constant> 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<Item> for doctree::Static<'_> {
 
 impl Clean<Item> 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 1d9a9981053..e7b9964d465 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<String>,
+    pub is_literal: bool,
 }
 
 #[derive(Clone, PartialEq, Debug)]
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index df2f27aca47..e52afe501c3 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<String> {
+    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 506f84baaa5..f763255a932 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<String> {
 fn item_constant(w: &mut Buffer, cx: &Context, it: &clean::Item, c: &clean::Constant) {
     write!(w, "<pre class='rust const'>");
     render_attributes(w, it, false);
+
     write!(
         w,
         "{vis}const \
-               {name}: {typ}</pre>",
+               {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, "</pre>");
     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 2d506787b3b..819adfeb9c7 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<T, const ORDER: Order> {
     inner: Vec<T>,
 }
 
-// @has foo/struct.VSet.html '//h3[@id="impl"]/code' 'impl<T> VSet<T, { Order::Sorted }>'
+// @has foo/struct.VSet.html '//h3[@id="impl"]/code' 'impl<T> VSet<T, {Order::Sorted}>'
 impl <T> VSet<T, {Order::Sorted}> {
     pub fn new() -> Self {
         Self { inner: Vec::new() }
     }
 }
 
-// @has foo/struct.VSet.html '//h3[@id="impl-1"]/code' 'impl<T> VSet<T, { Order::Unsorted }>'
+// @has foo/struct.VSet.html '//h3[@id="impl-1"]/code' 'impl<T> VSet<T, {Order::Unsorted}>'
 impl <T> VSet<T, {Order::Unsorted}> {
     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 656d579e4f3..00000000000
--- 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 00000000000..6d95f7827a1
--- /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);