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

Show the actual value of constant values in the documentation #66221

Merged
merged 1 commit into from Dec 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock
Expand Up @@ -3869,6 +3869,7 @@ dependencies = [
name = "rustdoc"
version = "0.0.0"
dependencies = [
"itertools 0.8.0",
"minifier",
"pulldown-cmark 0.5.3",
"rustc-rayon",
Expand Down
2 changes: 1 addition & 1 deletion src/libcore/num/f32.rs
Expand Up @@ -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")]
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/Cargo.toml
Expand Up @@ -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"
11 changes: 10 additions & 1 deletion src/librustdoc/clean/inline.rs
Expand Up @@ -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 {
Expand Down
15 changes: 13 additions & 2 deletions src/librustdoc/clean/mod.rs
Expand Up @@ -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),
}
}
}
Expand Down Expand Up @@ -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,
}
}
}

Expand Down Expand Up @@ -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),
}),
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/librustdoc/clean/types.rs
Expand Up @@ -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)]
Expand Down
77 changes: 74 additions & 3 deletions src/librustdoc/clean/utils.rs
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down
26 changes: 24 additions & 2 deletions src/librustdoc/html/render.rs
Expand Up @@ -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)
}

Expand Down
4 changes: 2 additions & 2 deletions src/test/rustdoc/const-generics/const-impl.rs
Expand Up @@ -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() }
Expand Down
5 changes: 0 additions & 5 deletions src/test/rustdoc/dont-show-const-contents.rs

This file was deleted.

64 changes: 64 additions & 0 deletions 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";
ohadravid marked this conversation as resolved.
Show resolved Hide resolved

// @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: () = ();
ohadravid marked this conversation as resolved.
Show resolved Hide resolved

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);