rustc_builtin_macros/deriving/
debug.rs1use rustc_ast::{self as ast, EnumDef, MetaItem};
2use rustc_expand::base::{Annotatable, ExtCtxt};
3use rustc_session::config::FmtDebug;
4use rustc_span::{Ident, Span, Symbol, sym};
5use thin_vec::{ThinVec, thin_vec};
6
7use crate::deriving::generic::ty::*;
8use crate::deriving::generic::*;
9use crate::deriving::path_std;
10
11pub(crate) fn expand_deriving_debug(
12 cx: &ExtCtxt<'_>,
13 span: Span,
14 mitem: &MetaItem,
15 item: &Annotatable,
16 push: &mut dyn FnMut(Annotatable),
17 is_const: bool,
18) {
19 let fmtr = Ref(Box::new(Path(path_std!(fmt::Formatter))), ast::Mutability::Mut);
21
22 let trait_def = TraitDef {
23 span,
24 path: path_std!(fmt::Debug),
25 skip_path_as_bound: false,
26 needs_copy_as_bound_if_packed: true,
27 additional_bounds: Vec::new(),
28 supports_unions: false,
29 methods: vec![MethodDef {
30 name: sym::fmt,
31 generics: Bounds::empty(),
32 explicit_self: true,
33 nonself_args: vec![(fmtr, sym::f)],
34 ret_ty: Path(path_std!(fmt::Result)),
35 attributes: thin_vec![cx.attr_word(sym::inline, span)],
36 fieldless_variants_strategy:
37 FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless,
38 combine_substructure: combine_substructure(Box::new(|a, b, c| {
39 show_substructure(a, b, c)
40 })),
41 }],
42 associated_types: Vec::new(),
43 is_const,
44 is_staged_api_crate: cx.ecfg.features.staged_api(),
45 };
46 trait_def.expand(cx, mitem, item, push)
47}
48
49fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
50 let span = cx.with_def_site_ctxt(span);
52
53 let fmt_detail = cx.sess.opts.unstable_opts.fmt_debug;
54 if fmt_detail == FmtDebug::None {
55 return BlockOrExpr::new_expr(cx.expr_ok(span, cx.expr_tuple(span, ThinVec::new())));
56 }
57
58 let (ident, vdata, fields) = match substr.fields {
59 Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
60 EnumMatching(v, fields) => (v.ident, &v.data, fields),
61 AllFieldlessEnum(enum_def) => return show_fieldless_enum(cx, span, enum_def, substr),
62 EnumDiscr(..) | StaticStruct(..) | StaticEnum(..) => {
63 cx.dcx().span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
64 }
65 };
66
67 let name = cx.expr_str(span, ident.name);
68 let fmt = substr.nonselflike_args[0].clone();
69
70 if fmt_detail == FmtDebug::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
77 let (is_struct, args_per_field) = match vdata {
80 ast::VariantData::Unit(..) => {
81 assert!(fields.is_empty());
83 (false, 0)
84 }
85 ast::VariantData::Tuple(..) => (false, 1),
86 ast::VariantData::Struct { .. } => (true, 2),
87 };
88
89 const CUTOFF: usize = 5;
91
92 fn expr_for_field(
93 cx: &ExtCtxt<'_>,
94 field: &FieldInfo,
95 index: usize,
96 len: usize,
97 ) -> Box<ast::Expr> {
98 if index < len - 1 {
99 field.self_expr.clone()
100 } else {
101 cx.expr_addr_of(field.span, field.self_expr.clone())
104 }
105 }
106
107 if fields.is_empty() {
108 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
110 let expr = cx.expr_call_global(span, fn_path_write_str, thin_vec![fmt, name]);
111 BlockOrExpr::new_expr(expr)
112 } else if fields.len() <= CUTOFF {
113 let debug = if is_struct {
115 format!("debug_struct_field{}_finish", fields.len())
116 } else {
117 format!("debug_tuple_field{}_finish", fields.len())
118 };
119 let fn_path_debug = cx.std_path(&[sym::fmt, sym::Formatter, Symbol::intern(&debug)]);
120
121 let mut args = ThinVec::with_capacity(2 + fields.len() * args_per_field);
122 args.extend([fmt, name]);
123 for i in 0..fields.len() {
124 let field = &fields[i];
125 if is_struct {
126 let name = cx.expr_str(field.span, field.name.unwrap().name);
127 args.push(name);
128 }
129
130 let field = expr_for_field(cx, field, i, fields.len());
131 args.push(field);
132 }
133 let expr = cx.expr_call_global(span, fn_path_debug, args);
134 BlockOrExpr::new_expr(expr)
135 } else {
136 let mut name_exprs = ThinVec::with_capacity(fields.len());
138 let mut value_exprs = ThinVec::with_capacity(fields.len());
139
140 for i in 0..fields.len() {
141 let field = &fields[i];
142 if is_struct {
143 name_exprs.push(cx.expr_str(field.span, field.name.unwrap().name));
144 }
145
146 let field = expr_for_field(cx, field, i, fields.len());
147 value_exprs.push(field);
148 }
149
150 let names_let = is_struct.then(|| {
152 let lt_static = Some(cx.lifetime_static(span));
153 let ty_static_ref = cx.ty_ref(span, cx.ty_infer(span), lt_static, ast::Mutability::Not);
154 cx.stmt_let_ty(
155 span,
156 false,
157 Ident::new(sym::names, span),
158 Some(ty_static_ref),
159 cx.expr_array_ref(span, name_exprs),
160 )
161 });
162
163 let path_debug = cx.path_global(span, cx.std_path(&[sym::fmt, sym::Debug]));
165 let ty_dyn_debug = cx.ty(
166 span,
167 ast::TyKind::TraitObject(
168 vec![cx.trait_bound(path_debug, false)],
169 ast::TraitObjectSyntax::Dyn,
170 ),
171 );
172 let ty_slice = cx.ty(
173 span,
174 ast::TyKind::Slice(cx.ty_ref(span, ty_dyn_debug, None, ast::Mutability::Not)),
175 );
176 let values_let = cx.stmt_let_ty(
177 span,
178 false,
179 Ident::new(sym::values, span),
180 Some(cx.ty_ref(span, ty_slice, None, ast::Mutability::Not)),
181 cx.expr_array_ref(span, value_exprs),
182 );
183
184 let sym_debug = if is_struct {
187 sym::debug_struct_fields_finish
188 } else {
189 sym::debug_tuple_fields_finish
190 };
191 let fn_path_debug_internal = cx.std_path(&[sym::fmt, sym::Formatter, sym_debug]);
192
193 let mut args = ThinVec::with_capacity(4);
194 args.push(fmt);
195 args.push(name);
196 if is_struct {
197 args.push(cx.expr_ident(span, Ident::new(sym::names, span)));
198 }
199 args.push(cx.expr_ident(span, Ident::new(sym::values, span)));
200 let expr = cx.expr_call_global(span, fn_path_debug_internal, args);
201
202 let mut stmts = ThinVec::with_capacity(2);
203 if is_struct {
204 stmts.push(names_let.unwrap());
205 }
206 stmts.push(values_let);
207 BlockOrExpr::new_mixed(stmts, Some(expr))
208 }
209}
210
211fn show_fieldless_enum(
225 cx: &ExtCtxt<'_>,
226 span: Span,
227 def: &EnumDef,
228 substr: &Substructure<'_>,
229) -> BlockOrExpr {
230 let fmt = substr.nonselflike_args[0].clone();
231 let arms = def
232 .variants
233 .iter()
234 .map(|v| {
235 let variant_path = cx.path(span, vec![substr.type_ident, v.ident]);
236 let pat = match &v.data {
237 ast::VariantData::Tuple(fields, _) => {
238 debug_assert!(fields.is_empty());
239 cx.pat_tuple_struct(span, variant_path, ThinVec::new())
240 }
241 ast::VariantData::Struct { fields, .. } => {
242 debug_assert!(fields.is_empty());
243 cx.pat_struct(span, variant_path, ThinVec::new())
244 }
245 ast::VariantData::Unit(_) => cx.pat_path(span, variant_path),
246 };
247 cx.arm(span, pat, cx.expr_str(span, v.ident.name))
248 })
249 .collect::<ThinVec<_>>();
250 let name = cx.expr_match(span, cx.expr_self(span), arms);
251 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
252 BlockOrExpr::new_expr(cx.expr_call_global(span, fn_path_write_str, thin_vec![fmt, name]))
253}