Skip to content

Commit aabf2f6

Browse files
committed
debug-fmt-detail option
Allows disabling `fmt::Debug` derive and debug formatting.
1 parent 29b1207 commit aabf2f6

File tree

13 files changed

+162
-12
lines changed

13 files changed

+162
-12
lines changed

compiler/rustc_ast_lowering/src/format.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use rustc_ast::visit::Visitor;
55
use rustc_ast::*;
66
use rustc_data_structures::fx::FxIndexMap;
77
use rustc_hir as hir;
8+
use rustc_session::config::DebugFmtDetail;
89
use rustc_span::{
910
sym,
1011
symbol::{kw, Ident},
@@ -205,7 +206,10 @@ fn make_argument<'hir>(
205206
hir::LangItem::FormatArgument,
206207
match ty {
207208
Format(Display) => sym::new_display,
208-
Format(Debug) => sym::new_debug,
209+
Format(Debug) => match ctx.tcx.sess.opts.unstable_opts.debug_fmt_detail {
210+
DebugFmtDetail::Full | DebugFmtDetail::Shallow => sym::new_debug,
211+
DebugFmtDetail::None => sym::new_debug_noop,
212+
},
209213
Format(LowerExp) => sym::new_lower_exp,
210214
Format(UpperExp) => sym::new_upper_exp,
211215
Format(Octal) => sym::new_octal,

compiler/rustc_builtin_macros/src/deriving/debug.rs

+13
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::deriving::path_std;
44

55
use rustc_ast::{self as ast, EnumDef, MetaItem};
66
use rustc_expand::base::{Annotatable, ExtCtxt};
7+
use rustc_session::config::DebugFmtDetail;
78
use rustc_span::symbol::{sym, Ident, Symbol};
89
use rustc_span::Span;
910
use thin_vec::{thin_vec, ThinVec};
@@ -49,6 +50,11 @@ fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) ->
4950
// We want to make sure we have the ctxt set so that we can use unstable methods
5051
let span = cx.with_def_site_ctxt(span);
5152

53+
let fmt_detail = cx.sess.opts.unstable_opts.debug_fmt_detail;
54+
if fmt_detail == DebugFmtDetail::None {
55+
return BlockOrExpr::new_expr(cx.expr_ok(span, cx.expr_tuple(span, ThinVec::new())));
56+
}
57+
5258
let (ident, vdata, fields) = match substr.fields {
5359
Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
5460
EnumMatching(_, v, fields) => (v.ident, &v.data, fields),
@@ -61,6 +67,13 @@ fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) ->
6167
let name = cx.expr_str(span, ident.name);
6268
let fmt = substr.nonselflike_args[0].clone();
6369

70+
// Fieldless enums have been special-cased earlier
71+
if fmt_detail == DebugFmtDetail::Shallow {
72+
let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
73+
let expr = cx.expr_call_global(span, fn_path_write_str, thin_vec![fmt, name]);
74+
return BlockOrExpr::new_expr(expr);
75+
}
76+
6477
// Struct and tuples are similar enough that we use the same code for both,
6578
// with some extra pieces for structs due to the field names.
6679
let (is_struct, args_per_field) = match vdata {

compiler/rustc_feature/src/builtin_attrs.rs

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const GATED_CFGS: &[GatedCfg] = &[
3838
(sym::relocation_model, sym::cfg_relocation_model, cfg_fn!(cfg_relocation_model)),
3939
(sym::sanitizer_cfi_generalize_pointers, sym::cfg_sanitizer_cfi, cfg_fn!(cfg_sanitizer_cfi)),
4040
(sym::sanitizer_cfi_normalize_integers, sym::cfg_sanitizer_cfi, cfg_fn!(cfg_sanitizer_cfi)),
41+
(sym::debug_fmt_detail, sym::debug_fmt_detail, cfg_fn!(debug_fmt_detail)),
4142
];
4243

4344
/// Find a gated cfg determined by the `pred`icate which is given the cfg's name.

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,8 @@ declare_features! (
432432
(unstable, custom_inner_attributes, "1.30.0", Some(54726)),
433433
/// Allows custom test frameworks with `#![test_runner]` and `#[test_case]`.
434434
(unstable, custom_test_frameworks, "1.30.0", Some(50297)),
435+
/// Disabling fmt::Debug
436+
(unstable, debug_fmt_detail, "CURRENT_RUSTC_VERSION", Some(123940)),
435437
/// Allows declarative macros 2.0 (`macro`).
436438
(unstable, decl_macro, "1.17.0", Some(39412)),
437439
/// Allows default type parameters to influence type inference.

compiler/rustc_interface/src/tests.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ use rustc_data_structures::profiling::TimePassesFormat;
44
use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig};
55
use rustc_session::config::{
66
build_configuration, build_session_options, rustc_optgroups, BranchProtection, CFGuard, Cfg,
7-
CollapseMacroDebuginfo, CoverageOptions, DebugInfo, DumpMonoStatsFormat, ErrorOutputType,
8-
ExternEntry, ExternLocation, Externs, FunctionReturn, InliningThreshold, Input,
9-
InstrumentCoverage, InstrumentXRay, LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli,
10-
NextSolverConfig, OomStrategy, Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet,
11-
Passes, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion,
12-
WasiExecModel,
7+
CollapseMacroDebuginfo, CoverageOptions, DebugFmtDetail, DebugInfo, DumpMonoStatsFormat,
8+
ErrorOutputType, ExternEntry, ExternLocation, Externs, FunctionReturn, InliningThreshold,
9+
Input, InstrumentCoverage, InstrumentXRay, LinkSelfContained, LinkerPluginLto, LocationDetail,
10+
LtoCli, NextSolverConfig, OomStrategy, Options, OutFileName, OutputType, OutputTypes, PAuthKey,
11+
PacRet, Passes, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath,
12+
SymbolManglingVersion, WasiExecModel,
1313
};
1414
use rustc_session::lint::Level;
1515
use rustc_session::search_paths::SearchPath;
@@ -762,6 +762,7 @@ fn test_unstable_options_tracking_hash() {
762762
tracked!(coverage_options, CoverageOptions { branch: true });
763763
tracked!(crate_attr, vec!["abc".to_string()]);
764764
tracked!(cross_crate_inline_threshold, InliningThreshold::Always);
765+
tracked!(debug_fmt_detail, DebugFmtDetail::Full);
765766
tracked!(debug_info_for_profiling, true);
766767
tracked!(debug_macros, true);
767768
tracked!(default_hidden_visibility, Some(true));

compiler/rustc_session/src/config.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,17 @@ impl LocationDetail {
360360
}
361361
}
362362

363+
/// Values for the `-Z debug-fmt-detail` flag.
364+
#[derive(Copy, Clone, PartialEq, Hash, Debug)]
365+
pub enum DebugFmtDetail {
366+
/// Derive fully-featured implementation
367+
Full,
368+
/// Print only type name, without fields
369+
Shallow,
370+
/// `#[derive(Debug)]` and `{:?}` are no-ops
371+
None,
372+
}
373+
363374
#[derive(Clone, PartialEq, Hash, Debug)]
364375
pub enum SwitchWithOptPath {
365376
Enabled(Option<PathBuf>),
@@ -2863,10 +2874,10 @@ pub enum WasiExecModel {
28632874
pub(crate) mod dep_tracking {
28642875
use super::{
28652876
BranchProtection, CFGuard, CFProtection, CollapseMacroDebuginfo, CoverageOptions,
2866-
CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FunctionReturn,
2867-
InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail,
2868-
LtoCli, NextSolverConfig, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes,
2869-
Polonius, RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm,
2877+
CrateType, DebugFmtDetail, DebugInfo, DebugInfoCompression, ErrorOutputType,
2878+
FunctionReturn, InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto,
2879+
LocationDetail, LtoCli, NextSolverConfig, OomStrategy, OptLevel, OutFileName, OutputType,
2880+
OutputTypes, Polonius, RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm,
28702881
SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel,
28712882
};
28722883
use crate::lint;
@@ -2968,6 +2979,7 @@ pub(crate) mod dep_tracking {
29682979
OutputType,
29692980
RealFileName,
29702981
LocationDetail,
2982+
DebugFmtDetail,
29712983
BranchProtection,
29722984
OomStrategy,
29732985
LanguageIdentifier,

compiler/rustc_session/src/config/cfg.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use rustc_target::abi::Align;
2525
use rustc_target::spec::{PanicStrategy, RelocModel, SanitizerSet};
2626
use rustc_target::spec::{Target, TargetTriple, TARGETS};
2727

28-
use crate::config::CrateType;
28+
use crate::config::{CrateType, DebugFmtDetail};
2929
use crate::Session;
3030

3131
use std::hash::Hash;
@@ -115,6 +115,18 @@ pub(crate) fn default_configuration(sess: &Session) -> Cfg {
115115
ins_none!(sym::debug_assertions);
116116
}
117117

118+
match sess.opts.unstable_opts.debug_fmt_detail {
119+
DebugFmtDetail::Full => {
120+
ins_str!(sym::debug_fmt_detail, "full");
121+
}
122+
DebugFmtDetail::Shallow => {
123+
ins_str!(sym::debug_fmt_detail, "shallow");
124+
}
125+
DebugFmtDetail::None => {
126+
ins_str!(sym::debug_fmt_detail, "none");
127+
}
128+
}
129+
118130
if sess.overflow_checks() {
119131
ins_none!(sym::overflow_checks);
120132
}

compiler/rustc_session/src/options.rs

+14
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,7 @@ mod desc {
406406
pub const parse_linker_plugin_lto: &str =
407407
"either a boolean (`yes`, `no`, `on`, `off`, etc), or the path to the linker plugin";
408408
pub const parse_location_detail: &str = "either `none`, or a comma separated list of location details to track: `file`, `line`, or `column`";
409+
pub const parse_debug_fmt_detail: &str = "either `full`, `shallow`, or `none`";
409410
pub const parse_switch_with_opt_path: &str =
410411
"an optional path to the profiling data output directory";
411412
pub const parse_merge_functions: &str = "one of: `disabled`, `trampolines`, or `aliases`";
@@ -585,6 +586,16 @@ mod parse {
585586
}
586587
}
587588

589+
pub(crate) fn parse_debug_fmt_detail(opt: &mut DebugFmtDetail, v: Option<&str>) -> bool {
590+
*opt = match v {
591+
Some("full") => DebugFmtDetail::Full,
592+
Some("shallow") => DebugFmtDetail::Shallow,
593+
Some("none") => DebugFmtDetail::None,
594+
_ => return false,
595+
};
596+
true
597+
}
598+
588599
pub(crate) fn parse_location_detail(ld: &mut LocationDetail, v: Option<&str>) -> bool {
589600
if let Some(v) = v {
590601
ld.line = false;
@@ -1606,6 +1617,9 @@ options! {
16061617
"inject the given attribute in the crate"),
16071618
cross_crate_inline_threshold: InliningThreshold = (InliningThreshold::Sometimes(100), parse_inlining_threshold, [TRACKED],
16081619
"threshold to allow cross crate inlining of functions"),
1620+
debug_fmt_detail: DebugFmtDetail = (DebugFmtDetail::Full, parse_debug_fmt_detail, [TRACKED],
1621+
"How detailed `#[derive(Debug)]` should be. `full` prints types recursively, \
1622+
`shallow` prints only type names, `none` prints nothing and disables `{:?}`. (default: `full`)"),
16091623
debug_info_for_profiling: bool = (false, parse_bool, [TRACKED],
16101624
"emit discriminators and other data necessary for AutoFDO"),
16111625
debug_macros: bool = (false, parse_bool, [TRACKED],

compiler/rustc_span/src/symbol.rs

+2
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,7 @@ symbols! {
658658
debug_assert_macro,
659659
debug_assert_ne_macro,
660660
debug_assertions,
661+
debug_fmt_detail,
661662
debug_struct,
662663
debug_struct_fields_finish,
663664
debug_tuple,
@@ -1227,6 +1228,7 @@ symbols! {
12271228
new_binary,
12281229
new_const,
12291230
new_debug,
1231+
new_debug_noop,
12301232
new_display,
12311233
new_lower_exp,
12321234
new_lower_hex,

library/core/src/fmt/rt.rs

+4
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ impl<'a> Argument<'a> {
117117
Self::new(x, Debug::fmt)
118118
}
119119
#[inline(always)]
120+
pub fn new_debug_noop<'b, T: Debug>(x: &'b T) -> Argument<'_> {
121+
Self::new(x, |_, _| Ok(()))
122+
}
123+
#[inline(always)]
120124
pub fn new_octal<'b, T: Octal>(x: &'b T) -> Argument<'_> {
121125
Self::new(x, Octal::fmt)
122126
}

tests/ui/debug_fmt_detail/full.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ compile-flags: -Zdebug-fmt-detail=full
2+
//@ run-pass
3+
#![feature(debug_fmt_detail)]
4+
#![allow(dead_code)]
5+
#![allow(unused)]
6+
7+
#[derive(Debug)]
8+
struct Foo {
9+
bar: u32,
10+
}
11+
12+
fn main() {
13+
let s = format!("Still works: {:?} '{:?}'", cfg!(debug_fmt_detail = "full"), Foo { bar: 1 });
14+
assert_eq!("Still works: true 'Foo { bar: 1 }'", s);
15+
}

tests/ui/debug_fmt_detail/none.rs

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//@ compile-flags: -Zdebug-fmt-detail=none
2+
//@ run-pass
3+
#![feature(debug_fmt_detail)]
4+
#![allow(dead_code)]
5+
#![allow(unused)]
6+
7+
#[derive(Debug)]
8+
struct Foo {
9+
bar: u32,
10+
}
11+
12+
#[derive(Debug)]
13+
enum Baz {
14+
Quz,
15+
}
16+
17+
#[cfg(debug_fmt_detail = "full")]
18+
compile_error!("nope");
19+
20+
#[cfg(debug_fmt_detail = "none")]
21+
struct Custom;
22+
23+
impl std::fmt::Debug for Custom {
24+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25+
f.write_str("custom_fmt")
26+
}
27+
}
28+
29+
fn main() {
30+
let c = Custom;
31+
let s = format!("Debug is '{:?}', '{:#?}', and '{c:?}'", Foo { bar: 1 }, Baz::Quz);
32+
assert_eq!("Debug is '', '', and ''", s);
33+
34+
let f = 3.0;
35+
let s = format_args!("{:?}x{:#?}y{f:?}", 1234, "can't debug this").to_string();
36+
assert_eq!("xy", s);
37+
}

tests/ui/debug_fmt_detail/shallow.rs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//@ compile-flags: -Zdebug-fmt-detail=shallow
2+
//@ run-pass
3+
#![feature(debug_fmt_detail)]
4+
#![allow(dead_code)]
5+
#![allow(unused)]
6+
7+
#[derive(Debug)]
8+
struct Foo {
9+
bar: u32,
10+
bomb: Bomb,
11+
}
12+
13+
#[derive(Debug)]
14+
enum Baz {
15+
Quz,
16+
}
17+
18+
struct Bomb;
19+
20+
impl std::fmt::Debug for Bomb {
21+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22+
panic!()
23+
}
24+
}
25+
26+
fn main() {
27+
let s = format!("Debug is '{:?}' and '{:#?}'", Foo { bar: 1, bomb: Bomb }, Baz::Quz);
28+
assert_eq!("Debug is 'Foo' and 'Quz'", s);
29+
30+
let f = 3.0;
31+
let s = format_args!("{:?}{:#?}{f:?}", 1234, cfg!(debug_fmt_detail = "shallow")).to_string();
32+
assert_eq!("1234true3.0", s);
33+
}

0 commit comments

Comments
 (0)