1use rustc_ast::{self as ast, EnumDef, ExprKind, MetaItem, Safety, TyKind, token};
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(generic::ty::Path::new({
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[sym::fmt, sym::Formatter]))
})path_std!(fmt::Formatter))), ast::Mutability::Mut);
21
22 let trait_def = TraitDef {
23 span,
24 path: generic::ty::Path::new({
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[sym::fmt, sym::Debug]))
})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: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[MethodDef {
name: sym::fmt,
generics: Bounds::empty(),
explicit_self: true,
nonself_args: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[(fmtr, sym::character('f'))])),
ret_ty: Path(generic::ty::Path::new({
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[sym::fmt, sym::Result]))
})),
attributes: {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.attr_word(sym::inline, span));
vec
},
fieldless_variants_strategy: FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless,
combine_substructure: combine_substructure(Box::new(|a, b,
c| { show_substructure(a, b, c) })),
}]))vec![MethodDef {
30 name: sym::fmt,
31 generics: Bounds::empty(),
32 explicit_self: true,
33 nonself_args: vec![(fmtr, sym::character('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 safety: Safety::Default,
46 document: true,
47 };
48 trait_def.expand(cx, mitem, item, push)
49}
50
51fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
52 let span = cx.with_def_site_ctxt(span);
54
55 let fmt_detail = cx.sess.opts.unstable_opts.fmt_debug;
56 if fmt_detail == FmtDebug::None {
57 return BlockOrExpr::new_expr(cx.expr_ok(span, cx.expr_tuple(span, ThinVec::new())));
58 }
59
60 let (ident, vdata, fields) = match substr.fields {
61 Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
62 EnumMatching(v, fields) => (v.ident, &v.data, fields),
63 AllFieldlessEnum(enum_def) => return show_fieldless_enum(cx, span, enum_def, substr),
64 EnumDiscr(..) | StaticStruct(..) | StaticEnum(..) => {
65 cx.dcx().span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
66 }
67 };
68
69 let name = cx.expr_str(span, ident.name);
70 let fmt = substr.nonselflike_args[0].clone();
71
72 if fmt_detail == FmtDebug::Shallow {
74 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
75 let expr = cx.expr_call_global(span, fn_path_write_str, {
let len = [(), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(fmt);
vec.push(name);
vec
}thin_vec![fmt, name]);
76 return BlockOrExpr::new_expr(expr);
77 }
78
79 let (is_struct, args_per_field) = match vdata {
82 ast::VariantData::Unit(..) => {
83 if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};assert!(fields.is_empty());
85 (false, 0)
86 }
87 ast::VariantData::Tuple(..) => (false, 1),
88 ast::VariantData::Struct { .. } => (true, 2),
89 };
90
91 const CUTOFF: usize = 5;
93
94 fn expr_for_field(
95 cx: &ExtCtxt<'_>,
96 field: &FieldInfo,
97 index: usize,
98 len: usize,
99 ) -> Box<ast::Expr> {
100 if index < len - 1 {
101 field.self_expr.clone()
102 } else {
103 cx.expr_addr_of(field.span, field.self_expr.clone())
106 }
107 }
108
109 if fields.is_empty() {
110 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
112 let expr = cx.expr_call_global(span, fn_path_write_str, {
let len = [(), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(fmt);
vec.push(name);
vec
}thin_vec![fmt, name]);
113 BlockOrExpr::new_expr(expr)
114 } else if fields.len() <= CUTOFF {
115 let debug = if is_struct {
117 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("debug_struct_field{0}_finish",
fields.len()))
})format!("debug_struct_field{}_finish", fields.len())
118 } else {
119 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("debug_tuple_field{0}_finish",
fields.len()))
})format!("debug_tuple_field{}_finish", fields.len())
120 };
121 let fn_path_debug = cx.std_path(&[sym::fmt, sym::Formatter, Symbol::intern(&debug)]);
122
123 let mut args = ThinVec::with_capacity(2 + fields.len() * args_per_field);
124 args.extend([fmt, name]);
125 for i in 0..fields.len() {
126 let field = &fields[i];
127 if is_struct {
128 let name = cx.expr_str(field.span, field.name.unwrap().name);
129 args.push(name);
130 }
131
132 let field = expr_for_field(cx, field, i, fields.len());
133 args.push(field);
134 }
135 let expr = cx.expr_call_global(span, fn_path_debug, args);
136 BlockOrExpr::new_expr(expr)
137 } else {
138 let mut name_exprs = ThinVec::with_capacity(fields.len());
140 let mut value_exprs = ThinVec::with_capacity(fields.len());
141
142 for i in 0..fields.len() {
143 let field = &fields[i];
144 if is_struct {
145 name_exprs.push(cx.expr_str(field.span, field.name.unwrap().name));
146 }
147
148 let field = expr_for_field(cx, field, i, fields.len());
149 value_exprs.push(field);
150 }
151
152 let names_let = is_struct.then(|| {
154 let lt_static = Some(cx.lifetime_static(span));
155 let ty_static_ref = cx.ty_ref(span, cx.ty_infer(span), lt_static, ast::Mutability::Not);
156 cx.stmt_let_ty(
157 span,
158 false,
159 Ident::new(sym::names, span),
160 Some(ty_static_ref),
161 cx.expr_array_ref(span, name_exprs),
162 )
163 });
164
165 let path_debug = cx.path_global(span, cx.std_path(&[sym::fmt, sym::Debug]));
167 let ty_dyn_debug = cx.ty(
168 span,
169 ast::TyKind::TraitObject(
170 {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.trait_bound(path_debug, false));
vec
}thin_vec![cx.trait_bound(path_debug, false)],
171 ast::TraitObjectSyntax::Dyn,
172 ),
173 );
174 let ty_slice = cx.ty(
175 span,
176 ast::TyKind::Slice(cx.ty_ref(span, ty_dyn_debug, None, ast::Mutability::Not)),
177 );
178 let values_let = cx.stmt_let_ty(
179 span,
180 false,
181 Ident::new(sym::values, span),
182 Some(cx.ty_ref(span, ty_slice, None, ast::Mutability::Not)),
183 cx.expr_array_ref(span, value_exprs),
184 );
185
186 let sym_debug = if is_struct {
189 sym::debug_struct_fields_finish
190 } else {
191 sym::debug_tuple_fields_finish
192 };
193 let fn_path_debug_internal = cx.std_path(&[sym::fmt, sym::Formatter, sym_debug]);
194
195 let mut args = ThinVec::with_capacity(4);
196 args.push(fmt);
197 args.push(name);
198 if is_struct {
199 args.push(cx.expr_ident(span, Ident::new(sym::names, span)));
200 }
201 args.push(cx.expr_ident(span, Ident::new(sym::values, span)));
202 let expr = cx.expr_call_global(span, fn_path_debug_internal, args);
203
204 let mut stmts = ThinVec::with_capacity(2);
205 if is_struct {
206 stmts.push(names_let.unwrap());
207 }
208 stmts.push(values_let);
209 BlockOrExpr::new_mixed(stmts, Some(expr))
210 }
211}
212
213fn show_fieldless_enum(
227 cx: &ExtCtxt<'_>,
228 span: Span,
229 def: &EnumDef,
230 substr: &Substructure<'_>,
231) -> BlockOrExpr {
232 let fmt = substr.nonselflike_args[0].clone();
233 if let Some((stmts, expr)) = show_fieldless_enum_concat_str(cx, span, def, fmt.clone()) {
234 return BlockOrExpr::new_mixed(stmts, Some(expr));
235 }
236 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
237 let arms = def
238 .variants
239 .iter()
240 .map(|v| {
241 let variant_path = cx.path(span, ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[substr.type_ident, v.ident]))vec![substr.type_ident, v.ident]);
242 let pat = match &v.data {
243 ast::VariantData::Tuple(fields, _) => {
244 if true {
if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};
};debug_assert!(fields.is_empty());
245 cx.pat_tuple_struct(span, variant_path, ThinVec::new())
246 }
247 ast::VariantData::Struct { fields, .. } => {
248 if true {
if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};
};debug_assert!(fields.is_empty());
249 cx.pat_struct(span, variant_path, ThinVec::new())
250 }
251 ast::VariantData::Unit(_) => cx.pat_path(span, variant_path),
252 };
253 cx.arm(span, pat, cx.expr_str(span, v.ident.name))
254 })
255 .collect::<ThinVec<_>>();
256 let name = cx.expr_match(span, cx.expr_self(span), arms);
257 BlockOrExpr::new_expr(cx.expr_call_global(span, fn_path_write_str, {
let len = [(), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(fmt);
vec.push(name);
vec
}thin_vec![fmt, name]))
258}
259
260fn show_fieldless_enum_concat_str(
272 cx: &ExtCtxt<'_>,
273 span: Span,
274 def: &EnumDef,
275 fmt: Box<ast::Expr>,
276) -> Option<(ThinVec<ast::Stmt>, Box<ast::Expr>)> {
277 const THRESHOLD: usize = 10;
280 let variants_count = def.variants.len();
281 if variants_count < THRESHOLD {
282 return None;
283 }
284
285 let variant_names = def
286 .variants
287 .iter()
288 .map(|v| v.disr_expr.is_none().then_some(v.ident.name.as_str()))
289 .collect::<Option<ThinVec<_>>>()?;
290
291 let total_bytes: usize = variant_names.iter().map(|n| n.len()).sum();
292 let mut concatenated_names = String::with_capacity(total_bytes);
293 let mut offset_indices = Vec::with_capacity(variant_names.len() + 1);
294 offset_indices.push(0);
295
296 for name in variant_names.iter() {
297 concatenated_names.push_str(name);
298 offset_indices.push(concatenated_names.len());
299 }
300
301 let names_ident = Ident::from_str_and_span("__NAMES", span);
303 let str_ty = cx.ty(
304 span,
305 TyKind::Ref(
306 None,
307 ast::MutTy {
308 ty: cx.ty(
309 span,
310 TyKind::Path(None, ast::Path::from_ident(Ident::new(sym::str, span))),
311 ),
312 mutbl: ast::Mutability::Not,
313 },
314 ),
315 );
316 let names_str_body = cx.expr_str(span, Symbol::intern(&concatenated_names));
317 let names_static_item =
318 cx.item_static(span, names_ident, str_ty, ast::Mutability::Not, names_str_body);
319
320 let offset_ident = Ident::from_str_and_span("__OFFSET", span);
322 let offset_index_exprs =
323 offset_indices.iter().map(|s| cx.expr_usize(span, *s)).collect::<ThinVec<_>>();
324 let starts_array_body = cx.expr_array(span, offset_index_exprs);
325 let usize_ty =
326 cx.ty(span, TyKind::Path(None, ast::Path::from_ident(Ident::new(sym::usize, span))));
327 let offset_array_len_expr = cx.anon_const(
328 span,
329 ExprKind::Lit(token::Lit::new(
330 token::LitKind::Integer,
331 Symbol::intern(&(variants_count + 1).to_string()),
332 None,
333 )),
334 );
335 let offset_static_item = cx.item_static(
336 span,
337 offset_ident,
338 cx.ty(span, TyKind::Array(usize_ty, offset_array_len_expr)),
339 ast::Mutability::Not,
340 starts_array_body,
341 );
342
343 let discriminant_ident = Ident::from_str_and_span("__d", span);
345 let discriminant_intrinsic_path = cx.std_path(&[sym::intrinsics, sym::discriminant_value]);
346 let discriminant_cast_expr = cx.expr(
347 span,
348 ast::ExprKind::Cast(
349 cx.expr_call_global(span, discriminant_intrinsic_path, {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.expr_self(span));
vec
}thin_vec![cx.expr_self(span)]),
350 cx.ty_path(ast::Path::from_ident(Ident::new(sym::usize, span))),
351 ),
352 );
353 let discriminant_let_stmt =
354 cx.stmt_let(span, false, discriminant_ident, discriminant_cast_expr);
355
356 let discriminant_expr = cx.expr_ident(span, discriminant_ident);
358
359 let names_expr = cx.expr_ident(span, names_ident);
361
362 let offset_ref_expr = cx.expr_addr_of(span, cx.expr_ident(span, offset_ident));
364
365 let fn_path = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_c_like_enum_write_str]);
367 let call_expr = cx.expr_call_global(
368 span,
369 fn_path,
370 {
let len = [(), (), (), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(fmt);
vec.push(names_expr);
vec.push(offset_ref_expr);
vec.push(discriminant_expr);
vec
}thin_vec![fmt, names_expr, offset_ref_expr, discriminant_expr],
371 );
372
373 Some((
374 {
let len = [(), (), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.stmt_item(span, names_static_item));
vec.push(cx.stmt_item(span, offset_static_item));
vec.push(discriminant_let_stmt);
vec
}thin_vec![
375 cx.stmt_item(span, names_static_item),
376 cx.stmt_item(span, offset_static_item),
377 discriminant_let_stmt,
378 ],
379 call_expr,
380 ))
381}