rustc_builtin_macros/deriving/
debug.rs
1use 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 };
45 trait_def.expand(cx, mitem, item, push)
46}
47
48fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
49 let span = cx.with_def_site_ctxt(span);
51
52 let fmt_detail = cx.sess.opts.unstable_opts.fmt_debug;
53 if fmt_detail == FmtDebug::None {
54 return BlockOrExpr::new_expr(cx.expr_ok(span, cx.expr_tuple(span, ThinVec::new())));
55 }
56
57 let (ident, vdata, fields) = match substr.fields {
58 Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
59 EnumMatching(v, fields) => (v.ident, &v.data, fields),
60 AllFieldlessEnum(enum_def) => return show_fieldless_enum(cx, span, enum_def, substr),
61 EnumDiscr(..) | StaticStruct(..) | StaticEnum(..) => {
62 cx.dcx().span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
63 }
64 };
65
66 let name = cx.expr_str(span, ident.name);
67 let fmt = substr.nonselflike_args[0].clone();
68
69 if fmt_detail == FmtDebug::Shallow {
71 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
72 let expr = cx.expr_call_global(span, fn_path_write_str, thin_vec![fmt, name]);
73 return BlockOrExpr::new_expr(expr);
74 }
75
76 let (is_struct, args_per_field) = match vdata {
79 ast::VariantData::Unit(..) => {
80 assert!(fields.is_empty());
82 (false, 0)
83 }
84 ast::VariantData::Tuple(..) => (false, 1),
85 ast::VariantData::Struct { .. } => (true, 2),
86 };
87
88 const CUTOFF: usize = 5;
90
91 fn expr_for_field(
92 cx: &ExtCtxt<'_>,
93 field: &FieldInfo,
94 index: usize,
95 len: usize,
96 ) -> ast::ptr::P<ast::Expr> {
97 if index < len - 1 {
98 field.self_expr.clone()
99 } else {
100 cx.expr_addr_of(field.span, field.self_expr.clone())
103 }
104 }
105
106 if fields.is_empty() {
107 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
109 let expr = cx.expr_call_global(span, fn_path_write_str, thin_vec![fmt, name]);
110 BlockOrExpr::new_expr(expr)
111 } else if fields.len() <= CUTOFF {
112 let debug = if is_struct {
114 format!("debug_struct_field{}_finish", fields.len())
115 } else {
116 format!("debug_tuple_field{}_finish", fields.len())
117 };
118 let fn_path_debug = cx.std_path(&[sym::fmt, sym::Formatter, Symbol::intern(&debug)]);
119
120 let mut args = ThinVec::with_capacity(2 + fields.len() * args_per_field);
121 args.extend([fmt, name]);
122 for i in 0..fields.len() {
123 let field = &fields[i];
124 if is_struct {
125 let name = cx.expr_str(field.span, field.name.unwrap().name);
126 args.push(name);
127 }
128
129 let field = expr_for_field(cx, field, i, fields.len());
130 args.push(field);
131 }
132 let expr = cx.expr_call_global(span, fn_path_debug, args);
133 BlockOrExpr::new_expr(expr)
134 } else {
135 let mut name_exprs = ThinVec::with_capacity(fields.len());
137 let mut value_exprs = ThinVec::with_capacity(fields.len());
138
139 for i in 0..fields.len() {
140 let field = &fields[i];
141 if is_struct {
142 name_exprs.push(cx.expr_str(field.span, field.name.unwrap().name));
143 }
144
145 let field = expr_for_field(cx, field, i, fields.len());
146 value_exprs.push(field);
147 }
148
149 let names_let = is_struct.then(|| {
151 let lt_static = Some(cx.lifetime_static(span));
152 let ty_static_ref = cx.ty_ref(span, cx.ty_infer(span), lt_static, ast::Mutability::Not);
153 cx.stmt_let_ty(
154 span,
155 false,
156 Ident::new(sym::names, span),
157 Some(ty_static_ref),
158 cx.expr_array_ref(span, name_exprs),
159 )
160 });
161
162 let path_debug = cx.path_global(span, cx.std_path(&[sym::fmt, sym::Debug]));
164 let ty_dyn_debug = cx.ty(
165 span,
166 ast::TyKind::TraitObject(
167 vec![cx.trait_bound(path_debug, false)],
168 ast::TraitObjectSyntax::Dyn,
169 ),
170 );
171 let ty_slice = cx.ty(
172 span,
173 ast::TyKind::Slice(cx.ty_ref(span, ty_dyn_debug, None, ast::Mutability::Not)),
174 );
175 let values_let = cx.stmt_let_ty(
176 span,
177 false,
178 Ident::new(sym::values, span),
179 Some(cx.ty_ref(span, ty_slice, None, ast::Mutability::Not)),
180 cx.expr_array_ref(span, value_exprs),
181 );
182
183 let sym_debug = if is_struct {
186 sym::debug_struct_fields_finish
187 } else {
188 sym::debug_tuple_fields_finish
189 };
190 let fn_path_debug_internal = cx.std_path(&[sym::fmt, sym::Formatter, sym_debug]);
191
192 let mut args = ThinVec::with_capacity(4);
193 args.push(fmt);
194 args.push(name);
195 if is_struct {
196 args.push(cx.expr_ident(span, Ident::new(sym::names, span)));
197 }
198 args.push(cx.expr_ident(span, Ident::new(sym::values, span)));
199 let expr = cx.expr_call_global(span, fn_path_debug_internal, args);
200
201 let mut stmts = ThinVec::with_capacity(2);
202 if is_struct {
203 stmts.push(names_let.unwrap());
204 }
205 stmts.push(values_let);
206 BlockOrExpr::new_mixed(stmts, Some(expr))
207 }
208}
209
210fn show_fieldless_enum(
224 cx: &ExtCtxt<'_>,
225 span: Span,
226 def: &EnumDef,
227 substr: &Substructure<'_>,
228) -> BlockOrExpr {
229 let fmt = substr.nonselflike_args[0].clone();
230 let arms = def
231 .variants
232 .iter()
233 .map(|v| {
234 let variant_path = cx.path(span, vec![substr.type_ident, v.ident]);
235 let pat = match &v.data {
236 ast::VariantData::Tuple(fields, _) => {
237 debug_assert!(fields.is_empty());
238 cx.pat_tuple_struct(span, variant_path, ThinVec::new())
239 }
240 ast::VariantData::Struct { fields, .. } => {
241 debug_assert!(fields.is_empty());
242 cx.pat_struct(span, variant_path, ThinVec::new())
243 }
244 ast::VariantData::Unit(_) => cx.pat_path(span, variant_path),
245 };
246 cx.arm(span, pat, cx.expr_str(span, v.ident.name))
247 })
248 .collect::<ThinVec<_>>();
249 let name = cx.expr_match(span, cx.expr_self(span), arms);
250 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
251 BlockOrExpr::new_expr(cx.expr_call_global(span, fn_path_write_str, thin_vec![fmt, name]))
252}