1use rustc_ast::{self as ast, EnumDef};
2use rustc_expand::base::ExtCtxt;
3use rustc_session::config::FmtDebug;
4use rustc_span::{Ident, Span, Symbol, sym};
5use thin_vec::{ThinVec, thin_vec};
6
7use crate::deriving::generic::*;
8use crate::deriving::path_std;
9
10pub(crate) fn expand_deriving_debug(
11 cx: &ExtCtxt<'_>,
12 span: Span,
13 item: &ast::Item,
14 push: &mut dyn FnMut(Box<ast::Item>),
15 is_const: bool,
16) {
17 let fmtr = cx.ty_ref(
19 span,
20 cx.ty_path(cx.std_path(span, { &[sym::fmt, sym::Formatter] })path_std!(cx, span, fmt::Formatter)),
21 None,
22 ast::Mutability::Mut,
23 );
24
25 let all_fieldless = match &item.kind {
26 ast::ItemKind::Enum(_, _, def) => {
27 def.variants.len() > 1 && def.variants.iter().all(|v| v.data.fields().is_empty())
28 }
29 _ => false,
30 };
31
32 let trait_def = TraitDef {
33 span,
34 path: cx.std_path(span, { &[sym::fmt, sym::Debug] })path_std!(cx, span, fmt::Debug),
35 needs_copy_as_bound_if_packed: true,
36 additional_bounds: SmallVec::new(),
37 supports_unions: false,
38 methods: {
let count = 0usize + 1usize;
let mut vec = ::smallvec::SmallVec::new();
if count <= vec.inline_size() {
vec.push(MethodDef {
name: sym::fmt,
generics: cx.empty_generics(span),
explicit_self: true,
nonself_args: {
let count = 0usize + 1usize;
let mut vec = ::smallvec::SmallVec::new();
if count <= vec.inline_size() {
vec.push((fmtr, sym::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::f)])))
}
},
has_other_selflike_arg: false,
ret_ty: cx.ty_path(cx.std_path(span,
{ &[sym::fmt, sym::Result] })),
attributes: {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.generated_attr(span,
ast::GeneratedAttr::Inline));
vec
},
fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
combine_substructure: combine_substructure(|cx, span, substr|
show_substructure(cx, span, substr,
item.kind.ident().unwrap())),
});
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: cx.empty_generics(span),
explicit_self: true,
nonself_args: {
let count = 0usize + 1usize;
let mut vec = ::smallvec::SmallVec::new();
if count <= vec.inline_size() {
vec.push((fmtr, sym::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::f)])))
}
},
has_other_selflike_arg: false,
ret_ty: cx.ty_path(cx.std_path(span,
{ &[sym::fmt, sym::Result] })),
attributes: {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.generated_attr(span,
ast::GeneratedAttr::Inline));
vec
},
fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
combine_substructure: combine_substructure(|cx, span,
substr|
show_substructure(cx, span, substr,
item.kind.ident().unwrap())),
}])))
}
}smallvec![MethodDef {
39 name: sym::fmt,
40 generics: cx.empty_generics(span),
41 explicit_self: true,
42 nonself_args: smallvec![(fmtr, sym::f)],
43 has_other_selflike_arg: false,
44 ret_ty: cx.ty_path(path_std!(cx, span, fmt::Result)),
45 attributes: thin_vec![cx.generated_attr(span, ast::GeneratedAttr::Inline)],
46 fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
47 combine_substructure: combine_substructure(|cx, span, substr| show_substructure(
48 cx,
49 span,
50 substr,
51 item.kind.ident().unwrap()
52 )),
53 }],
54 is_const,
55 ..
56 };
57 trait_def.expand_ext(cx, item, push, all_fieldless);
58}
59
60fn show_substructure(
61 cx: &ExtCtxt<'_>,
62 span: Span,
63 substr: Substructure<'_>,
64 type_ident: Ident,
65) -> BlockOrExpr {
66 let fmt_detail = cx.sess.opts.unstable_opts.fmt_debug;
67 if fmt_detail == FmtDebug::None {
68 return BlockOrExpr::new_expr(cx.expr_ok(span, cx.expr_tuple(span, ThinVec::new())));
69 }
70
71 let (ident, vdata, fields) = match substr {
72 Struct(vdata, fields) => (type_ident, vdata, fields),
73 EnumMatching(v, fields) => (v.ident, &v.data, fields),
74 StaticEnum(enum_def) => return show_fieldless_enum(cx, span, enum_def, type_ident),
75 _ => cx.dcx().span_bug(span, "unexpected substructure in `derive(Debug)`"),
76 };
77
78 let expr_name = || cx.expr_str(span, ident.name);
79 let fmt = cx.expr_name(span, sym::f);
80
81 if fmt_detail == FmtDebug::Shallow {
83 let fn_path_write_str = cx.std_path(span, &[sym::fmt, sym::Formatter, sym::write_str]);
84 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(expr_name());
vec
}thin_vec![fmt, expr_name()]);
85 return BlockOrExpr::new_expr(expr);
86 }
87
88 let is_struct = match vdata {
91 ast::VariantData::Unit(..) => {
92 if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};assert!(fields.is_empty());
94 false
95 }
96 ast::VariantData::Tuple(..) => false,
97 ast::VariantData::Struct { .. } => true,
98 };
99
100 const CUTOFF: usize = 5;
102
103 let len = fields.len();
104 let expr_for_field = |field: FieldInfo, index: usize| -> Box<ast::Expr> {
105 if index < len - 1 {
106 field.self_expr
107 } else {
108 cx.expr_addr_of(field.span, field.self_expr)
111 }
112 };
113
114 if fields.is_empty() {
115 let fn_path_write_str = cx.std_path(span, &[sym::fmt, sym::Formatter, sym::write_str]);
117 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(expr_name());
vec
}thin_vec![fmt, expr_name()]);
118 BlockOrExpr::new_expr(expr)
119 } else if fields.len() <= CUTOFF {
120 let debug = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("debug_struct_field{0}_finish",
fields.len()))
})format!("debug_struct_field{}_finish", fields.len());
122 let fn_path_debug = cx.std_path(span, &[sym::fmt, sym::Formatter, Symbol::intern(&debug)]);
123
124 let mut args = ThinVec::with_capacity(2 + fields.len());
125 args.extend([fmt]);
126
127 if is_struct {
128 let mut names = String::new();
129 names.push_str(ident.name.as_str());
130
131 for field in fields.iter() {
132 names.push(' ');
133 names.push_str(field.name.unwrap().name.as_str());
134 }
135
136 let name_expr = cx.expr_str(span, Symbol::intern(&names));
137 args.push(name_expr);
138 } else {
139 args.push(expr_name());
140 };
141
142 for (i, field) in fields.into_iter().enumerate() {
143 let field = expr_for_field(field, i);
144 args.push(field);
145 }
146 let expr = cx.expr_call_global(span, fn_path_debug, args);
147 BlockOrExpr::new_expr(expr)
148 } else {
149 let mut names = String::new();
151 if is_struct {
152 names.push_str(ident.name.as_str());
153 }
154 let mut value_exprs = ThinVec::with_capacity(fields.len());
155
156 for (i, field) in fields.into_iter().enumerate() {
157 if is_struct {
158 names.push(' ');
159 names.push_str(field.name.unwrap().name.as_str());
160 }
161
162 let field = expr_for_field(field, i);
163 value_exprs.push(field);
164 }
165
166 let names_let = is_struct.then(|| {
168 let lt_static = Some(cx.lifetime_static(span));
169 let ty_static_ref = cx.ty_ref(span, cx.ty_infer(span), lt_static, ast::Mutability::Not);
170 let names_expr = cx.expr_str(span, Symbol::intern(&names));
171 cx.stmt_let_ty(span, false, sym::names, Some(ty_static_ref), names_expr)
172 });
173
174 let path_debug = cx.std_path(span, &[sym::fmt, sym::Debug]);
176 let ty_dyn_debug = cx.ty(
177 span,
178 ast::TyKind::TraitObject(
179 {
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)],
180 ast::TraitObjectSyntax::Dyn,
181 ),
182 );
183 let ty_slice = cx.ty(
184 span,
185 ast::TyKind::Slice(cx.ty_ref(span, ty_dyn_debug, None, ast::Mutability::Not)),
186 );
187 let values_let = cx.stmt_let_ty(
188 span,
189 false,
190 sym::values,
191 Some(cx.ty_ref(span, ty_slice, None, ast::Mutability::Not)),
192 cx.expr_array_ref(span, value_exprs),
193 );
194
195 let sym_debug = sym::debug_struct_fields_finish;
197 let fn_path_debug_internal = cx.std_path(span, &[sym::fmt, sym::Formatter, sym_debug]);
198
199 let args = {
let len = [(), (), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(fmt);
vec.push(if is_struct {
cx.expr_name(span, sym::names)
} else { expr_name() });
vec.push(cx.expr_name(span, sym::values));
vec
}thin_vec![
200 fmt,
201 if is_struct { cx.expr_name(span, sym::names) } else { expr_name() },
202 cx.expr_name(span, sym::values)
203 ];
204
205 let expr = cx.expr_call_global(span, fn_path_debug_internal, args);
206
207 let stmts = if is_struct {
208 {
let len = [(), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(names_let.unwrap());
vec.push(values_let);
vec
}thin_vec![names_let.unwrap(), values_let]
209 } else {
210 {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(values_let);
vec
}thin_vec![values_let]
211 };
212
213 BlockOrExpr::new_mixed(stmts, Some(expr))
214 }
215}
216
217fn show_fieldless_enum(
231 cx: &ExtCtxt<'_>,
232 span: Span,
233 def: &EnumDef,
234 type_ident: Ident,
235) -> BlockOrExpr {
236 if let Some(expr) = show_fieldless_enum_concat_str(cx, span, def, type_ident) {
237 return BlockOrExpr::new_expr(expr);
238 }
239 let fmt = cx.expr_name(span, sym::f);
240 let arms = def
241 .variants
242 .iter()
243 .map(|v| {
244 let variant_path = cx.path(span, &[type_ident, v.ident]);
245 let pat = match &v.data {
246 ast::VariantData::Tuple(fields, _) => {
247 if true {
if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};
};debug_assert!(fields.is_empty());
248 cx.pat_tuple_struct(span, variant_path, ThinVec::new())
249 }
250 ast::VariantData::Struct { fields, .. } => {
251 if true {
if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};
};debug_assert!(fields.is_empty());
252 cx.pat_struct(span, variant_path, ThinVec::new())
253 }
254 ast::VariantData::Unit(_) => cx.pat_path(span, variant_path),
255 };
256 cx.arm(span, pat, cx.expr_str(span, v.ident.name))
257 })
258 .collect::<ThinVec<_>>();
259 let name = cx.expr_match(span, cx.expr_self(span), arms);
260 let fn_path_write_str = cx.std_path(span, &[sym::fmt, sym::Formatter, sym::write_str]);
261 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]))
262}
263
264fn show_fieldless_enum_concat_str(
282 cx: &ExtCtxt<'_>,
283 span: Span,
284 def: &EnumDef,
285 type_ident: Ident,
286) -> Option<Box<ast::Expr>> {
287 const THRESHOLD: usize = 5;
304 let variants_count = def.variants.len();
305 if variants_count < THRESHOLD {
306 return None;
307 }
308 let fmt = cx.expr_name(span, sym::f);
309
310 let mut concatenated_names = String::new();
311
312 let arms = def
313 .variants
314 .iter()
315 .enumerate()
316 .map(|(i, v)| {
317 let name_offset = concatenated_names.len();
318
319 let name = v.ident.name.as_str();
320 concatenated_names.push_str(name);
321
322 let is_last = i == def.variants.len() - 1;
323 if !is_last {
324 concatenated_names.push(' ');
325 }
326
327 let variant_path = cx.path(span, &[type_ident, v.ident]);
328 let pat = match &v.data {
329 ast::VariantData::Tuple(fields, _) => {
330 if true {
if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};
};debug_assert!(fields.is_empty());
331 cx.pat_tuple_struct(span, variant_path, ThinVec::new())
332 }
333 ast::VariantData::Struct { fields, .. } => {
334 if true {
if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};
};debug_assert!(fields.is_empty());
335 cx.pat_struct(span, variant_path, ThinVec::new())
336 }
337 ast::VariantData::Unit(_) => cx.pat_path(span, variant_path),
338 };
339
340 cx.arm(span, pat, cx.expr_usize(span, name_offset))
341 })
342 .collect::<ThinVec<_>>();
343
344 let names_str_body = cx.expr_str(span, Symbol::intern(&concatenated_names));
345
346 let variant_index_expr = cx.expr_match(span, cx.expr_self(span), arms);
347
348 let fn_path = cx.std_path(span, &[sym::fmt, sym::Formatter, sym::debug_c_like_enum_write_str]);
349 let call_expr =
350 cx.expr_call_global(span, fn_path, {
let len = [(), (), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(fmt);
vec.push(names_str_body);
vec.push(variant_index_expr);
vec
}thin_vec![fmt, names_str_body, variant_index_expr]);
351
352 Some(call_expr)
353}