Skip to content

Commit 1fb6947

Browse files
committed
Auto merge of #115286 - saethlin:detangler, r=petrochenkov
Skip rendering metadata strings from include_str!/include_bytes! The const rendering code in rustdoc completely ignores consts from expansions, but the compiler was rendering all consts. So some consts (namely those from `include_bytes!`) were rendered then ignored. Most of the diff here is from moving `print_const_expr` from rustdoc into `rustc_hir_pretty` so that it can be used in rustdoc and when building rmeta files.
2 parents 9dc11a1 + 159ad5f commit 1fb6947

File tree

7 files changed

+107
-113
lines changed

7 files changed

+107
-113
lines changed

Diff for: 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" }

Diff for: 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::{
@@ -1615,7 +1615,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
16151615
record!(self.tables.mir_const_qualif[def_id.to_def_id()] <- qualifs);
16161616
let body_id = tcx.hir().maybe_body_owned_by(def_id);
16171617
if let Some(body_id) = body_id {
1618-
let const_data = self.encode_rendered_const_for_body(body_id);
1618+
let const_data = rendered_const(self.tcx, body_id);
16191619
record!(self.tables.rendered_const[def_id.to_def_id()] <- const_data);
16201620
}
16211621
}
@@ -1683,14 +1683,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
16831683
}
16841684
}
16851685

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

Diff for: 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;

Diff for: 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
}

Diff for: 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};
@@ -255,7 +256,7 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String {
255256
match n.kind() {
256257
ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args: _ }) => {
257258
let s = if let Some(def) = def.as_local() {
258-
print_const_expr(cx.tcx, cx.tcx.hir().body_owned_by(def))
259+
rendered_const(cx.tcx, cx.tcx.hir().body_owned_by(def))
259260
} else {
260261
inline::print_inlined_const(cx.tcx, def)
261262
};
@@ -367,100 +368,6 @@ pub(crate) fn is_literal_expr(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
367368
false
368369
}
369370

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

Diff for: 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
}

Diff for: 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)