Skip to content

Commit 159ad5f

Browse files
committed
Reuse const rendering from rustdoc in rmeta encoding
1 parent 1bd0430 commit 159ad5f

File tree

7 files changed

+107
-113
lines changed

7 files changed

+107
-113
lines changed

compiler/rustc_metadata/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,6 @@ pub mod locator;
4242

4343
pub use fs::{emit_wrapper_file, METADATA_FILENAME};
4444
pub use native_libs::find_native_static_library;
45-
pub use rmeta::{encode_metadata, EncodedMetadata, METADATA_HEADER};
45+
pub use rmeta::{encode_metadata, rendered_const, EncodedMetadata, METADATA_HEADER};
4646

4747
fluent_messages! { "../messages.ftl" }

compiler/rustc_metadata/src/rmeta/encoder.rs

+96-10
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ use rustc_hir::def_id::{
1717
CrateNum, DefId, DefIndex, LocalDefId, CRATE_DEF_ID, CRATE_DEF_INDEX, LOCAL_CRATE,
1818
};
1919
use rustc_hir::definitions::DefPathData;
20-
use rustc_hir::intravisit;
2120
use rustc_hir::lang_items::LangItem;
21+
use rustc_hir_pretty::id_to_string;
2222
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
2323
use rustc_middle::middle::dependency_format::Linkage;
2424
use rustc_middle::middle::exported_symbols::{
@@ -1614,7 +1614,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
16141614
record!(self.tables.mir_const_qualif[def_id.to_def_id()] <- qualifs);
16151615
let body_id = tcx.hir().maybe_body_owned_by(def_id);
16161616
if let Some(body_id) = body_id {
1617-
let const_data = self.encode_rendered_const_for_body(body_id);
1617+
let const_data = rendered_const(self.tcx, body_id);
16181618
record!(self.tables.rendered_const[def_id.to_def_id()] <- const_data);
16191619
}
16201620
}
@@ -1682,14 +1682,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
16821682
}
16831683
}
16841684

1685-
fn encode_rendered_const_for_body(&mut self, body_id: hir::BodyId) -> String {
1686-
let hir = self.tcx.hir();
1687-
let body = hir.body(body_id);
1688-
rustc_hir_pretty::to_string(&(&hir as &dyn intravisit::Map<'_>), |s| {
1689-
s.print_expr(&body.value)
1690-
})
1691-
}
1692-
16931685
#[instrument(level = "debug", skip(self))]
16941686
fn encode_info_for_macro(&mut self, def_id: LocalDefId) {
16951687
let tcx = self.tcx;
@@ -2291,3 +2283,97 @@ pub fn provide(providers: &mut Providers) {
22912283
..*providers
22922284
}
22932285
}
2286+
2287+
/// Build a textual representation of an unevaluated constant expression.
2288+
///
2289+
/// If the const expression is too complex, an underscore `_` is returned.
2290+
/// For const arguments, it's `{ _ }` to be precise.
2291+
/// This means that the output is not necessarily valid Rust code.
2292+
///
2293+
/// Currently, only
2294+
///
2295+
/// * literals (optionally with a leading `-`)
2296+
/// * unit `()`
2297+
/// * blocks (`{ … }`) around simple expressions and
2298+
/// * paths without arguments
2299+
///
2300+
/// are considered simple enough. Simple blocks are included since they are
2301+
/// necessary to disambiguate unit from the unit type.
2302+
/// This list might get extended in the future.
2303+
///
2304+
/// Without this censoring, in a lot of cases the output would get too large
2305+
/// and verbose. Consider `match` expressions, blocks and deeply nested ADTs.
2306+
/// Further, private and `doc(hidden)` fields of structs would get leaked
2307+
/// since HIR datatypes like the `body` parameter do not contain enough
2308+
/// semantic information for this function to be able to hide them –
2309+
/// at least not without significant performance overhead.
2310+
///
2311+
/// Whenever possible, prefer to evaluate the constant first and try to
2312+
/// use a different method for pretty-printing. Ideally this function
2313+
/// should only ever be used as a fallback.
2314+
pub fn rendered_const<'tcx>(tcx: TyCtxt<'tcx>, body: hir::BodyId) -> String {
2315+
let hir = tcx.hir();
2316+
let value = &hir.body(body).value;
2317+
2318+
#[derive(PartialEq, Eq)]
2319+
enum Classification {
2320+
Literal,
2321+
Simple,
2322+
Complex,
2323+
}
2324+
2325+
use Classification::*;
2326+
2327+
fn classify(expr: &hir::Expr<'_>) -> Classification {
2328+
match &expr.kind {
2329+
hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
2330+
if matches!(expr.kind, hir::ExprKind::Lit(_)) { Literal } else { Complex }
2331+
}
2332+
hir::ExprKind::Lit(_) => Literal,
2333+
hir::ExprKind::Tup([]) => Simple,
2334+
hir::ExprKind::Block(hir::Block { stmts: [], expr: Some(expr), .. }, _) => {
2335+
if classify(expr) == Complex { Complex } else { Simple }
2336+
}
2337+
// Paths with a self-type or arguments are too “complex” following our measure since
2338+
// they may leak private fields of structs (with feature `adt_const_params`).
2339+
// Consider: `<Self as Trait<{ Struct { private: () } }>>::CONSTANT`.
2340+
// Paths without arguments are definitely harmless though.
2341+
hir::ExprKind::Path(hir::QPath::Resolved(_, hir::Path { segments, .. })) => {
2342+
if segments.iter().all(|segment| segment.args.is_none()) { Simple } else { Complex }
2343+
}
2344+
// FIXME: Claiming that those kinds of QPaths are simple is probably not true if the Ty
2345+
// contains const arguments. Is there a *concise* way to check for this?
2346+
hir::ExprKind::Path(hir::QPath::TypeRelative(..)) => Simple,
2347+
// FIXME: Can they contain const arguments and thus leak private struct fields?
2348+
hir::ExprKind::Path(hir::QPath::LangItem(..)) => Simple,
2349+
_ => Complex,
2350+
}
2351+
}
2352+
2353+
let classification = classify(value);
2354+
2355+
if classification == Literal
2356+
&& !value.span.from_expansion()
2357+
&& let Ok(snippet) = tcx.sess.source_map().span_to_snippet(value.span) {
2358+
// For literals, we avoid invoking the pretty-printer and use the source snippet instead to
2359+
// preserve certain stylistic choices the user likely made for the sake legibility like
2360+
//
2361+
// * hexadecimal notation
2362+
// * underscores
2363+
// * character escapes
2364+
//
2365+
// FIXME: This passes through `-/*spacer*/0` verbatim.
2366+
snippet
2367+
} else if classification == Simple {
2368+
// Otherwise we prefer pretty-printing to get rid of extraneous whitespace, comments and
2369+
// other formatting artifacts.
2370+
id_to_string(&hir, body.hir_id)
2371+
} else if tcx.def_kind(hir.body_owner_def_id(body).to_def_id()) == DefKind::AnonConst {
2372+
// FIXME: Omit the curly braces if the enclosing expression is an array literal
2373+
// with a repeated element (an `ExprKind::Repeat`) as in such case it
2374+
// would not actually need any disambiguation.
2375+
"{ _ }".to_owned()
2376+
} else {
2377+
"_".to_owned()
2378+
}
2379+
}

compiler/rustc_metadata/src/rmeta/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub use decoder::provide_extern;
4242
use decoder::DecodeContext;
4343
pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob};
4444
use encoder::EncodeContext;
45-
pub use encoder::{encode_metadata, EncodedMetadata};
45+
pub use encoder::{encode_metadata, rendered_const, EncodedMetadata};
4646
use rustc_span::hygiene::SyntaxContextData;
4747

4848
mod decoder;

src/librustdoc/clean/types.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use rustc_hir::lang_items::LangItem;
2222
use rustc_hir::{BodyId, Mutability};
2323
use rustc_hir_analysis::check::intrinsic::intrinsic_operation_unsafety;
2424
use rustc_index::IndexVec;
25+
use rustc_metadata::rendered_const;
2526
use rustc_middle::ty::fast_reject::SimplifiedType;
2627
use rustc_middle::ty::{self, TyCtxt, Visibility};
2728
use rustc_resolve::rustdoc::{add_doc_fragment, attrs_to_doc_fragments, inner_docs, DocFragment};
@@ -35,7 +36,7 @@ use rustc_target::spec::abi::Abi;
3536
use crate::clean::cfg::Cfg;
3637
use crate::clean::external_path;
3738
use crate::clean::inline::{self, print_inlined_const};
38-
use crate::clean::utils::{is_literal_expr, print_const_expr, print_evaluated_const};
39+
use crate::clean::utils::{is_literal_expr, print_evaluated_const};
3940
use crate::core::DocContext;
4041
use crate::formats::cache::Cache;
4142
use crate::formats::item_type::ItemType;
@@ -2086,7 +2087,7 @@ impl Discriminant {
20862087
/// Will be `None` in the case of cross-crate reexports, and may be
20872088
/// simplified
20882089
pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> Option<String> {
2089-
self.expr.map(|body| print_const_expr(tcx, body))
2090+
self.expr.map(|body| rendered_const(tcx, body))
20902091
}
20912092
/// Will always be a machine readable number, without underscores or suffixes.
20922093
pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> String {
@@ -2326,7 +2327,7 @@ impl ConstantKind {
23262327
ConstantKind::TyConst { ref expr } => expr.to_string(),
23272328
ConstantKind::Extern { def_id } => print_inlined_const(tcx, def_id),
23282329
ConstantKind::Local { body, .. } | ConstantKind::Anonymous { body } => {
2329-
print_const_expr(tcx, body)
2330+
rendered_const(tcx, body)
23302331
}
23312332
}
23322333
}

src/librustdoc/clean/utils.rs

+2-95
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use rustc_ast::tokenstream::TokenTree;
1414
use rustc_hir as hir;
1515
use rustc_hir::def::{DefKind, Res};
1616
use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
17+
use rustc_metadata::rendered_const;
1718
use rustc_middle::mir;
1819
use rustc_middle::mir::interpret::ConstValue;
1920
use rustc_middle::ty::{self, GenericArgKind, GenericArgsRef, TyCtxt};
@@ -253,7 +254,7 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String {
253254
match n.kind() {
254255
ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args: _ }) => {
255256
let s = if let Some(def) = def.as_local() {
256-
print_const_expr(cx.tcx, cx.tcx.hir().body_owned_by(def))
257+
rendered_const(cx.tcx, cx.tcx.hir().body_owned_by(def))
257258
} else {
258259
inline::print_inlined_const(cx.tcx, def)
259260
};
@@ -365,100 +366,6 @@ pub(crate) fn is_literal_expr(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
365366
false
366367
}
367368

368-
/// Build a textual representation of an unevaluated constant expression.
369-
///
370-
/// If the const expression is too complex, an underscore `_` is returned.
371-
/// For const arguments, it's `{ _ }` to be precise.
372-
/// This means that the output is not necessarily valid Rust code.
373-
///
374-
/// Currently, only
375-
///
376-
/// * literals (optionally with a leading `-`)
377-
/// * unit `()`
378-
/// * blocks (`{ … }`) around simple expressions and
379-
/// * paths without arguments
380-
///
381-
/// are considered simple enough. Simple blocks are included since they are
382-
/// necessary to disambiguate unit from the unit type.
383-
/// This list might get extended in the future.
384-
///
385-
/// Without this censoring, in a lot of cases the output would get too large
386-
/// and verbose. Consider `match` expressions, blocks and deeply nested ADTs.
387-
/// Further, private and `doc(hidden)` fields of structs would get leaked
388-
/// since HIR datatypes like the `body` parameter do not contain enough
389-
/// semantic information for this function to be able to hide them –
390-
/// at least not without significant performance overhead.
391-
///
392-
/// Whenever possible, prefer to evaluate the constant first and try to
393-
/// use a different method for pretty-printing. Ideally this function
394-
/// should only ever be used as a fallback.
395-
pub(crate) fn print_const_expr(tcx: TyCtxt<'_>, body: hir::BodyId) -> String {
396-
let hir = tcx.hir();
397-
let value = &hir.body(body).value;
398-
399-
#[derive(PartialEq, Eq)]
400-
enum Classification {
401-
Literal,
402-
Simple,
403-
Complex,
404-
}
405-
406-
use Classification::*;
407-
408-
fn classify(expr: &hir::Expr<'_>) -> Classification {
409-
match &expr.kind {
410-
hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
411-
if matches!(expr.kind, hir::ExprKind::Lit(_)) { Literal } else { Complex }
412-
}
413-
hir::ExprKind::Lit(_) => Literal,
414-
hir::ExprKind::Tup([]) => Simple,
415-
hir::ExprKind::Block(hir::Block { stmts: [], expr: Some(expr), .. }, _) => {
416-
if classify(expr) == Complex { Complex } else { Simple }
417-
}
418-
// Paths with a self-type or arguments are too “complex” following our measure since
419-
// they may leak private fields of structs (with feature `adt_const_params`).
420-
// Consider: `<Self as Trait<{ Struct { private: () } }>>::CONSTANT`.
421-
// Paths without arguments are definitely harmless though.
422-
hir::ExprKind::Path(hir::QPath::Resolved(_, hir::Path { segments, .. })) => {
423-
if segments.iter().all(|segment| segment.args.is_none()) { Simple } else { Complex }
424-
}
425-
// FIXME: Claiming that those kinds of QPaths are simple is probably not true if the Ty
426-
// contains const arguments. Is there a *concise* way to check for this?
427-
hir::ExprKind::Path(hir::QPath::TypeRelative(..)) => Simple,
428-
// FIXME: Can they contain const arguments and thus leak private struct fields?
429-
hir::ExprKind::Path(hir::QPath::LangItem(..)) => Simple,
430-
_ => Complex,
431-
}
432-
}
433-
434-
let classification = classify(value);
435-
436-
if classification == Literal
437-
&& !value.span.from_expansion()
438-
&& let Ok(snippet) = tcx.sess.source_map().span_to_snippet(value.span) {
439-
// For literals, we avoid invoking the pretty-printer and use the source snippet instead to
440-
// preserve certain stylistic choices the user likely made for the sake legibility like
441-
//
442-
// * hexadecimal notation
443-
// * underscores
444-
// * character escapes
445-
//
446-
// FIXME: This passes through `-/*spacer*/0` verbatim.
447-
snippet
448-
} else if classification == Simple {
449-
// Otherwise we prefer pretty-printing to get rid of extraneous whitespace, comments and
450-
// other formatting artifacts.
451-
rustc_hir_pretty::id_to_string(&hir, body.hir_id)
452-
} else if tcx.def_kind(hir.body_owner_def_id(body).to_def_id()) == DefKind::AnonConst {
453-
// FIXME: Omit the curly braces if the enclosing expression is an array literal
454-
// with a repeated element (an `ExprKind::Repeat`) as in such case it
455-
// would not actually need any disambiguation.
456-
"{ _ }".to_owned()
457-
} else {
458-
"_".to_owned()
459-
}
460-
}
461-
462369
/// Given a type Path, resolve it to a Type using the TyCtxt
463370
pub(crate) fn resolve_type(cx: &mut DocContext<'_>, path: Path) -> Type {
464371
debug!("resolve_type({path:?})");

src/librustdoc/json/conversions.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ use std::fmt;
88

99
use rustc_ast::ast;
1010
use rustc_hir::{def::CtorKind, def::DefKind, def_id::DefId};
11+
use rustc_metadata::rendered_const;
1112
use rustc_middle::ty::{self, TyCtxt};
1213
use rustc_span::symbol::sym;
1314
use rustc_span::{Pos, Symbol};
1415
use rustc_target::spec::abi::Abi as RustcAbi;
1516

1617
use rustdoc_json_types::*;
1718

18-
use crate::clean::utils::print_const_expr;
1919
use crate::clean::{self, ItemId};
2020
use crate::formats::item_type::ItemType;
2121
use crate::json::JsonRenderer;
@@ -805,7 +805,7 @@ impl FromWithTcx<clean::Static> for Static {
805805
Static {
806806
type_: stat.type_.into_tcx(tcx),
807807
mutable: stat.mutability == ast::Mutability::Mut,
808-
expr: stat.expr.map(|e| print_const_expr(tcx, e)).unwrap_or_default(),
808+
expr: stat.expr.map(|e| rendered_const(tcx, e)).unwrap_or_default(),
809809
}
810810
}
811811
}

tests/rustdoc/show-const-contents.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub struct MyTypeWithStr(&'static str);
4747
// @!hasraw show_const_contents/constant.MY_TYPE_WITH_STR.html '; //'
4848
pub const MY_TYPE_WITH_STR: MyTypeWithStr = MyTypeWithStr("show this");
4949

50-
// @hasraw show_const_contents/constant.PI.html '= 3.14159265358979323846264338327950288f32;'
50+
// @hasraw show_const_contents/constant.PI.html '= 3.14159265358979323846264338327950288_f32;'
5151
// @hasraw show_const_contents/constant.PI.html '; // 3.14159274f32'
5252
pub use std::f32::consts::PI;
5353

0 commit comments

Comments
 (0)