Skip to content

Commit f6dca76

Browse files
committed
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.
2 parents 84d8f9d + 811bdee commit f6dca76

File tree

11 files changed

+192
-16
lines changed

11 files changed

+192
-16
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3869,6 +3869,7 @@ dependencies = [
38693869
name = "rustdoc"
38703870
version = "0.0.0"
38713871
dependencies = [
3872+
"itertools 0.8.0",
38723873
"minifier",
38733874
"pulldown-cmark 0.5.3",
38743875
"rustc-rayon",

src/libcore/num/f32.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub const DIGITS: u32 = 6;
3030
///
3131
/// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon
3232
#[stable(feature = "rust1", since = "1.0.0")]
33-
pub const EPSILON: f32 = 1.19209290e-07_f32;
33+
pub const EPSILON: f32 = 1.1920929e-7_f32;
3434

3535
/// Smallest finite `f32` value.
3636
#[stable(feature = "rust1", since = "1.0.0")]

src/librustdoc/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ rayon = { version = "0.3.0", package = "rustc-rayon" }
1515
serde = { version = "1.0", features = ["derive"] }
1616
serde_json = "1.0"
1717
tempfile = "3"
18+
itertools = "0.8"

src/librustdoc/clean/inline.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,16 @@ pub fn print_inlined_const(cx: &DocContext<'_>, did: DefId) -> String {
450450
}
451451

452452
fn build_const(cx: &DocContext<'_>, did: DefId) -> clean::Constant {
453-
clean::Constant { type_: cx.tcx.type_of(did).clean(cx), expr: print_inlined_const(cx, did) }
453+
clean::Constant {
454+
type_: cx.tcx.type_of(did).clean(cx),
455+
expr: print_inlined_const(cx, did),
456+
value: clean::utils::print_evaluated_const(cx, did),
457+
is_literal: cx
458+
.tcx
459+
.hir()
460+
.as_local_hir_id(did)
461+
.map_or(false, |hir_id| clean::utils::is_literal_expr(cx, hir_id)),
462+
}
454463
}
455464

456465
fn build_static(cx: &DocContext<'_>, did: DefId, mutable: bool) -> clean::Static {

src/librustdoc/clean/mod.rs

+13-2
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,8 @@ impl Clean<Constant> for hir::ConstArg {
435435
Constant {
436436
type_: cx.tcx.type_of(cx.tcx.hir().body_owner_def_id(self.value.body)).clean(cx),
437437
expr: print_const_expr(cx, self.value.body),
438+
value: None,
439+
is_literal: is_literal_expr(cx, self.value.body.hir_id),
438440
}
439441
}
440442
}
@@ -1717,7 +1719,12 @@ impl<'tcx> Clean<Type> for Ty<'tcx> {
17171719

17181720
impl<'tcx> Clean<Constant> for ty::Const<'tcx> {
17191721
fn clean(&self, cx: &DocContext<'_>) -> Constant {
1720-
Constant { type_: self.ty.clean(cx), expr: format!("{}", self) }
1722+
Constant {
1723+
type_: self.ty.clean(cx),
1724+
expr: format!("{}", self),
1725+
value: None,
1726+
is_literal: false,
1727+
}
17211728
}
17221729
}
17231730

@@ -2062,17 +2069,21 @@ impl Clean<Item> for doctree::Static<'_> {
20622069

20632070
impl Clean<Item> for doctree::Constant<'_> {
20642071
fn clean(&self, cx: &DocContext<'_>) -> Item {
2072+
let def_id = cx.tcx.hir().local_def_id(self.id);
2073+
20652074
Item {
20662075
name: Some(self.name.clean(cx)),
20672076
attrs: self.attrs.clean(cx),
20682077
source: self.whence.clean(cx),
2069-
def_id: cx.tcx.hir().local_def_id(self.id),
2078+
def_id,
20702079
visibility: self.vis.clean(cx),
20712080
stability: cx.stability(self.id).clean(cx),
20722081
deprecation: cx.deprecation(self.id).clean(cx),
20732082
inner: ConstantItem(Constant {
20742083
type_: self.type_.clean(cx),
20752084
expr: print_const_expr(cx, self.expr),
2085+
value: print_evaluated_const(cx, def_id),
2086+
is_literal: is_literal_expr(cx, self.expr.hir_id),
20762087
}),
20772088
}
20782089
}

src/librustdoc/clean/types.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,8 @@ pub struct Static {
14601460
pub struct Constant {
14611461
pub type_: Type,
14621462
pub expr: String,
1463+
pub value: Option<String>,
1464+
pub is_literal: bool,
14631465
}
14641466

14651467
#[derive(Clone, PartialEq, Debug)]

src/librustdoc/clean/utils.rs

+74-3
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,18 @@ use crate::clean::{
88
};
99
use crate::core::DocContext;
1010

11+
use itertools::Itertools;
1112
use rustc::hir;
1213
use rustc::hir::def::{DefKind, Res};
1314
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
15+
use rustc::mir::interpret::{sign_extend, ConstValue, Scalar};
1416
use rustc::ty::subst::{GenericArgKind, SubstsRef};
1517
use rustc::ty::{self, DefIdTree, Ty};
1618
use rustc::util::nodemap::FxHashSet;
19+
use std::mem;
1720
use syntax_pos;
1821
use syntax_pos::symbol::{kw, sym, Symbol};
1922

20-
use std::mem;
21-
2223
pub fn krate(mut cx: &mut DocContext<'_>) -> Crate {
2324
use crate::visit_lib::LibEmbargoVisitor;
2425

@@ -482,8 +483,78 @@ pub fn print_const(cx: &DocContext<'_>, n: &ty::Const<'_>) -> String {
482483
}
483484
}
484485

486+
pub fn print_evaluated_const(cx: &DocContext<'_>, def_id: DefId) -> Option<String> {
487+
let value =
488+
cx.tcx.const_eval_poly(def_id).ok().and_then(|value| match (value.val, &value.ty.kind) {
489+
(_, ty::Ref(..)) => None,
490+
(ty::ConstKind::Value(ConstValue::Scalar(_)), ty::Adt(_, _)) => None,
491+
(ty::ConstKind::Value(ConstValue::Scalar(_)), _) => {
492+
Some(print_const_with_custom_print_scalar(cx, value))
493+
}
494+
_ => None,
495+
});
496+
497+
value
498+
}
499+
500+
fn format_integer_with_underscore_sep(num: &str) -> String {
501+
let num_chars: Vec<_> = num.chars().collect();
502+
let num_start_index = if num_chars.get(0) == Some(&'-') { 1 } else { 0 };
503+
504+
num_chars[..num_start_index]
505+
.iter()
506+
.chain(num_chars[num_start_index..].rchunks(3).rev().intersperse(&['_']).flatten())
507+
.collect()
508+
}
509+
510+
fn print_const_with_custom_print_scalar(cx: &DocContext<'_>, ct: &'tcx ty::Const<'tcx>) -> String {
511+
// Use a slightly different format for integer types which always shows the actual value.
512+
// For all other types, fallback to the original `pretty_print_const`.
513+
match (ct.val, &ct.ty.kind) {
514+
(ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data, .. })), ty::Uint(ui)) => {
515+
format!("{}{}", format_integer_with_underscore_sep(&data.to_string()), ui.name_str())
516+
}
517+
(ty::ConstKind::Value(ConstValue::Scalar(Scalar::Raw { data, .. })), ty::Int(i)) => {
518+
let ty = cx.tcx.lift(&ct.ty).unwrap();
519+
let size = cx.tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size;
520+
let sign_extended_data = sign_extend(data, size) as i128;
521+
522+
format!(
523+
"{}{}",
524+
format_integer_with_underscore_sep(&sign_extended_data.to_string()),
525+
i.name_str()
526+
)
527+
}
528+
_ => ct.to_string(),
529+
}
530+
}
531+
532+
pub fn is_literal_expr(cx: &DocContext<'_>, hir_id: hir::HirId) -> bool {
533+
if let hir::Node::Expr(expr) = cx.tcx.hir().get(hir_id) {
534+
if let hir::ExprKind::Lit(_) = &expr.kind {
535+
return true;
536+
}
537+
538+
if let hir::ExprKind::Unary(hir::UnOp::UnNeg, expr) = &expr.kind {
539+
if let hir::ExprKind::Lit(_) = &expr.kind {
540+
return true;
541+
}
542+
}
543+
}
544+
545+
false
546+
}
547+
485548
pub fn print_const_expr(cx: &DocContext<'_>, body: hir::BodyId) -> String {
486-
cx.tcx.hir().hir_to_pretty_string(body.hir_id)
549+
let value = &cx.tcx.hir().body(body).value;
550+
551+
let snippet = if !value.span.from_expansion() {
552+
cx.sess().source_map().span_to_snippet(value.span).ok()
553+
} else {
554+
None
555+
};
556+
557+
snippet.unwrap_or_else(|| cx.tcx.hir().hir_to_pretty_string(body.hir_id))
487558
}
488559

489560
/// Given a type Path, resolve it to a Type using the TyCtxt

src/librustdoc/html/render.rs

+24-2
Original file line numberDiff line numberDiff line change
@@ -2272,14 +2272,36 @@ fn short_stability(item: &clean::Item, cx: &Context) -> Vec<String> {
22722272
fn item_constant(w: &mut Buffer, cx: &Context, it: &clean::Item, c: &clean::Constant) {
22732273
write!(w, "<pre class='rust const'>");
22742274
render_attributes(w, it, false);
2275+
22752276
write!(
22762277
w,
22772278
"{vis}const \
2278-
{name}: {typ}</pre>",
2279+
{name}: {typ}",
22792280
vis = it.visibility.print_with_space(),
22802281
name = it.name.as_ref().unwrap(),
2281-
typ = c.type_.print()
2282+
typ = c.type_.print(),
22822283
);
2284+
2285+
if c.value.is_some() || c.is_literal {
2286+
write!(w, " = {expr};", expr = c.expr);
2287+
} else {
2288+
write!(w, ";");
2289+
}
2290+
2291+
if let Some(value) = &c.value {
2292+
if !c.is_literal {
2293+
let value_lowercase = value.to_lowercase();
2294+
let expr_lowercase = c.expr.to_lowercase();
2295+
2296+
if value_lowercase != expr_lowercase
2297+
&& value_lowercase.trim_end_matches("i32") != expr_lowercase
2298+
{
2299+
write!(w, " // {value}", value = value);
2300+
}
2301+
}
2302+
}
2303+
2304+
write!(w, "</pre>");
22832305
document(w, cx, it)
22842306
}
22852307

src/test/rustdoc/const-generics/const-impl.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ pub struct VSet<T, const ORDER: Order> {
1717
inner: Vec<T>,
1818
}
1919

20-
// @has foo/struct.VSet.html '//h3[@id="impl"]/code' 'impl<T> VSet<T, { Order::Sorted }>'
20+
// @has foo/struct.VSet.html '//h3[@id="impl"]/code' 'impl<T> VSet<T, {Order::Sorted}>'
2121
impl <T> VSet<T, {Order::Sorted}> {
2222
pub fn new() -> Self {
2323
Self { inner: Vec::new() }
2424
}
2525
}
2626

27-
// @has foo/struct.VSet.html '//h3[@id="impl-1"]/code' 'impl<T> VSet<T, { Order::Unsorted }>'
27+
// @has foo/struct.VSet.html '//h3[@id="impl-1"]/code' 'impl<T> VSet<T, {Order::Unsorted}>'
2828
impl <T> VSet<T, {Order::Unsorted}> {
2929
pub fn new() -> Self {
3030
Self { inner: Vec::new() }

src/test/rustdoc/dont-show-const-contents.rs

-5
This file was deleted.
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Test that the contents of constants are displayed as part of the
2+
// documentation.
3+
4+
// @has show_const_contents/constant.CONST_S.html 'show this'
5+
// @!has show_const_contents/constant.CONST_S.html '; //'
6+
pub const CONST_S: &'static str = "show this";
7+
8+
// @has show_const_contents/constant.CONST_I32.html '= 42;'
9+
// @!has show_const_contents/constant.CONST_I32.html '; //'
10+
pub const CONST_I32: i32 = 42;
11+
12+
// @has show_const_contents/constant.CONST_I32_HEX.html '= 0x42;'
13+
// @!has show_const_contents/constant.CONST_I32_HEX.html '; //'
14+
pub const CONST_I32_HEX: i32 = 0x42;
15+
16+
// @has show_const_contents/constant.CONST_NEG_I32.html '= -42;'
17+
// @!has show_const_contents/constant.CONST_NEG_I32.html '; //'
18+
pub const CONST_NEG_I32: i32 = -42;
19+
20+
// @has show_const_contents/constant.CONST_EQ_TO_VALUE_I32.html '= 42i32;'
21+
// @!has show_const_contents/constant.CONST_EQ_TO_VALUE_I32.html '// 42i32'
22+
pub const CONST_EQ_TO_VALUE_I32: i32 = 42i32;
23+
24+
// @has show_const_contents/constant.CONST_CALC_I32.html '= 42 + 1; // 43i32'
25+
pub const CONST_CALC_I32: i32 = 42 + 1;
26+
27+
// @!has show_const_contents/constant.CONST_REF_I32.html '= &42;'
28+
// @!has show_const_contents/constant.CONST_REF_I32.html '; //'
29+
pub const CONST_REF_I32: &'static i32 = &42;
30+
31+
// @has show_const_contents/constant.CONST_I32_MAX.html '= i32::max_value(); // 2_147_483_647i32'
32+
pub const CONST_I32_MAX: i32 = i32::max_value();
33+
34+
// @!has show_const_contents/constant.UNIT.html '= ();'
35+
// @!has show_const_contents/constant.UNIT.html '; //'
36+
pub const UNIT: () = ();
37+
38+
pub struct MyType(i32);
39+
40+
// @!has show_const_contents/constant.MY_TYPE.html '= MyType(42);'
41+
// @!has show_const_contents/constant.MY_TYPE.html '; //'
42+
pub const MY_TYPE: MyType = MyType(42);
43+
44+
pub struct MyTypeWithStr(&'static str);
45+
46+
// @!has show_const_contents/constant.MY_TYPE_WITH_STR.html '= MyTypeWithStr("show this");'
47+
// @!has show_const_contents/constant.MY_TYPE_WITH_STR.html '; //'
48+
pub const MY_TYPE_WITH_STR: MyTypeWithStr = MyTypeWithStr("show this");
49+
50+
// @has show_const_contents/constant.EPSILON.html '1.1920929e-7f32;'
51+
// @!has show_const_contents/constant.EPSILON.html '; //'
52+
pub use std::f32::EPSILON;
53+
54+
// @has show_const_contents/constant.MAX.html '= i32::max_value(); // 2_147_483_647i32'
55+
pub use std::i32::MAX;
56+
57+
macro_rules! int_module {
58+
($T:ident) => (
59+
pub const MIN: $T = $T::min_value();
60+
)
61+
}
62+
63+
// @has show_const_contents/constant.MIN.html '= i16::min_value(); // -32_768i16'
64+
int_module!(i16);

0 commit comments

Comments
 (0)