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: SmallVec::new(),
28 supports_unions: false,
29 methods: {
let count = 0usize + 1usize;
let mut vec = ::smallvec::SmallVec::new();
if count <= vec.inline_size() {
vec.push(MethodDef {
name: sym::fmt,
generics: Bounds::empty(),
explicit_self: true,
nonself_args: {
let count = 0usize + 1usize;
let mut vec = ::smallvec::SmallVec::new();
if count <= vec.inline_size() {
vec.push((fmtr, sym::character('f')));
vec
} else {
::smallvec::SmallVec::from_vec(::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(show_substructure),
});
vec
} else {
::smallvec::SmallVec::from_vec(::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: {
let count = 0usize + 1usize;
let mut vec = ::smallvec::SmallVec::new();
if count <= vec.inline_size() {
vec.push((fmtr, sym::character('f')));
vec
} else {
::smallvec::SmallVec::from_vec(::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(show_substructure),
}])))
}
}smallvec![MethodDef {
30 name: sym::fmt,
31 generics: Bounds::empty(),
32 explicit_self: true,
33 nonself_args: smallvec![(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(show_substructure),
39 }],
40 associated_types: SmallVec::new(),
41 is_const,
42 safety: Safety::Default,
43 document: true,
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, {
let len = [(), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(fmt);
vec.push(name);
vec
}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 if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};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 ) -> Box<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, {
let len = [(), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(fmt);
vec.push(name);
vec
}thin_vec![fmt, name]);
110 BlockOrExpr::new_expr(expr)
111 } else if fields.len() <= CUTOFF {
112 let debug = if is_struct {
114 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("debug_struct_field{0}_finish",
fields.len()))
})format!("debug_struct_field{}_finish", fields.len())
115 } else {
116 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("debug_tuple_field{0}_finish",
fields.len()))
})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 {
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)],
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 if let Some((stmts, expr)) = show_fieldless_enum_concat_str(cx, span, def, fmt.clone()) {
231 return BlockOrExpr::new_mixed(stmts, Some(expr));
232 }
233 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
234 let arms = def
235 .variants
236 .iter()
237 .map(|v| {
238 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]);
239 let pat = match &v.data {
240 ast::VariantData::Tuple(fields, _) => {
241 if true {
if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};
};debug_assert!(fields.is_empty());
242 cx.pat_tuple_struct(span, variant_path, ThinVec::new())
243 }
244 ast::VariantData::Struct { fields, .. } => {
245 if true {
if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};
};debug_assert!(fields.is_empty());
246 cx.pat_struct(span, variant_path, ThinVec::new())
247 }
248 ast::VariantData::Unit(_) => cx.pat_path(span, variant_path),
249 };
250 cx.arm(span, pat, cx.expr_str(span, v.ident.name))
251 })
252 .collect::<ThinVec<_>>();
253 let name = cx.expr_match(span, cx.expr_self(span), arms);
254 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]))
255}
256
257fn show_fieldless_enum_concat_str(
269 cx: &ExtCtxt<'_>,
270 span: Span,
271 def: &EnumDef,
272 fmt: Box<ast::Expr>,
273) -> Option<(ThinVec<ast::Stmt>, Box<ast::Expr>)> {
274 const THRESHOLD: usize = 10;
277 let variants_count = def.variants.len();
278 if variants_count < THRESHOLD {
279 return None;
280 }
281
282 let variant_names = def
283 .variants
284 .iter()
285 .map(|v| v.disr_expr.is_none().then_some(v.ident.name.as_str()))
286 .collect::<Option<ThinVec<_>>>()?;
287
288 let total_bytes: usize = variant_names.iter().map(|n| n.len()).sum();
289 let mut concatenated_names = String::with_capacity(total_bytes);
290 let mut offset_indices = Vec::with_capacity(variant_names.len() + 1);
291 offset_indices.push(0);
292
293 for name in variant_names.iter() {
294 concatenated_names.push_str(name);
295 offset_indices.push(concatenated_names.len());
296 }
297
298 let names_ident = Ident::from_str_and_span("__NAMES", span);
300 let str_ty = cx.ty(
301 span,
302 TyKind::Ref(
303 None,
304 ast::MutTy {
305 ty: cx.ty(
306 span,
307 TyKind::Path(None, ast::Path::from_ident(Ident::new(sym::str, span))),
308 ),
309 mutbl: ast::Mutability::Not,
310 },
311 ),
312 );
313 let names_str_body = cx.expr_str(span, Symbol::intern(&concatenated_names));
314 let names_static_item =
315 cx.item_static(span, names_ident, str_ty, ast::Mutability::Not, names_str_body);
316
317 let offset_ident = Ident::from_str_and_span("__OFFSET", span);
319 let offset_index_exprs =
320 offset_indices.iter().map(|s| cx.expr_usize(span, *s)).collect::<ThinVec<_>>();
321 let starts_array_body = cx.expr_array(span, offset_index_exprs);
322 let usize_ty =
323 cx.ty(span, TyKind::Path(None, ast::Path::from_ident(Ident::new(sym::usize, span))));
324 let offset_array_len_expr = cx.anon_const(
325 span,
326 ExprKind::Lit(token::Lit::new(
327 token::LitKind::Integer,
328 Symbol::intern(&(variants_count + 1).to_string()),
329 None,
330 )),
331 );
332 let offset_static_item = cx.item_static(
333 span,
334 offset_ident,
335 cx.ty(span, TyKind::Array(usize_ty, offset_array_len_expr)),
336 ast::Mutability::Not,
337 starts_array_body,
338 );
339
340 let discriminant_ident = Ident::from_str_and_span("__d", span);
342 let discriminant_intrinsic_path = cx.std_path(&[sym::intrinsics, sym::discriminant_value]);
343 let discriminant_cast_expr = cx.expr(
344 span,
345 ast::ExprKind::Cast(
346 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)]),
347 cx.ty_path(ast::Path::from_ident(Ident::new(sym::usize, span))),
348 ),
349 );
350 let discriminant_let_stmt =
351 cx.stmt_let(span, false, discriminant_ident, discriminant_cast_expr);
352
353 let discriminant_expr = cx.expr_ident(span, discriminant_ident);
355
356 let names_expr = cx.expr_ident(span, names_ident);
358
359 let offset_ref_expr = cx.expr_addr_of(span, cx.expr_ident(span, offset_ident));
361
362 let fn_path = cx.std_path(&[sym::fmt, sym::Formatter, sym::debug_c_like_enum_write_str]);
364 let call_expr = cx.expr_call_global(
365 span,
366 fn_path,
367 {
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],
368 );
369
370 Some((
371 {
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![
372 cx.stmt_item(span, names_static_item),
373 cx.stmt_item(span, offset_static_item),
374 discriminant_let_stmt,
375 ],
376 call_expr,
377 ))
378}