1use rustc_ast::{self as ast, EnumDef, Safety};
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(new_path(cx, span, { &[sym::fmt, sym::Formatter] }, ::alloc::vec::Vec::new())path_std!(cx, span, fmt::Formatter)),
21 None,
22 ast::Mutability::Mut,
23 );
24
25 let trait_def = TraitDef {
26 span,
27 path: new_path(cx, span, { &[sym::fmt, sym::Debug] }, ::alloc::vec::Vec::new())path_std!(cx, span, fmt::Debug),
28 skip_path_as_bound: false,
29 needs_copy_as_bound_if_packed: true,
30 additional_bounds: SmallVec::new(),
31 supports_unions: false,
32 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::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'))])))
}
},
has_other_selflike_arg: false,
ret_ty: cx.ty_path(new_path(cx, span,
{ &[sym::fmt, sym::Result] }, ::alloc::vec::Vec::new())),
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(|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::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'))])))
}
},
has_other_selflike_arg: false,
ret_ty: cx.ty_path(new_path(cx, span,
{ &[sym::fmt, sym::Result] }, ::alloc::vec::Vec::new())),
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(|cx, span,
substr|
show_substructure(cx, span, substr,
item.kind.ident().unwrap())),
}])))
}
}smallvec![MethodDef {
33 name: sym::fmt,
34 generics: cx.empty_generics(span),
35 explicit_self: true,
36 nonself_args: smallvec![(fmtr, sym::character('f'))],
37 has_other_selflike_arg: false,
38 ret_ty: cx.ty_path(path_std!(cx, span, fmt::Result)),
39 attributes: thin_vec![cx.attr_word(sym::inline, span)],
40 fieldless_variants_strategy:
41 FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless,
42 combine_substructure: combine_substructure(|cx, span, substr| show_substructure(
43 cx,
44 span,
45 substr,
46 item.kind.ident().unwrap()
47 )),
48 }],
49 is_const,
50 safety: Safety::Default,
51 document: true,
52 };
53 trait_def.expand(cx, item, push)
54}
55
56fn formatter_ident(cx: &ExtCtxt<'_>, span: Span) -> Box<ast::Expr> {
57 cx.expr_ident_sym(span, sym::character('f'))
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.fields {
72 Struct(vdata, fields) => (type_ident, vdata, fields),
73 EnumMatching(v, fields) => (v.ident, &v.data, fields),
74 AllFieldlessEnum(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 name = cx.expr_str(span, ident.name);
79 let fmt = formatter_ident(cx, span);
80
81 if fmt_detail == FmtDebug::Shallow {
83 let fn_path_write_str = cx.std_path(&[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(name);
vec
}thin_vec![fmt, name]);
85 return BlockOrExpr::new_expr(expr);
86 }
87
88 let (is_struct, args_per_field) = 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, 0)
95 }
96 ast::VariantData::Tuple(..) => (false, 1),
97 ast::VariantData::Struct { .. } => (true, 2),
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(&[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(name);
vec
}thin_vec![fmt, name]);
118 BlockOrExpr::new_expr(expr)
119 } else if fields.len() <= CUTOFF {
120 let debug = if is_struct {
122 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("debug_struct_field{0}_finish",
fields.len()))
})format!("debug_struct_field{}_finish", fields.len())
123 } else {
124 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("debug_tuple_field{0}_finish",
fields.len()))
})format!("debug_tuple_field{}_finish", fields.len())
125 };
126 let fn_path_debug = cx.std_path(&[sym::fmt, sym::Formatter, Symbol::intern(&debug)]);
127
128 let mut args = ThinVec::with_capacity(2 + fields.len() * args_per_field);
129 args.extend([fmt, name]);
130 for (i, field) in fields.into_iter().enumerate() {
131 if is_struct {
132 let name = cx.expr_str(field.span, field.name.unwrap().name);
133 args.push(name);
134 }
135
136 let field = expr_for_field(field, i);
137 args.push(field);
138 }
139 let expr = cx.expr_call_global(span, fn_path_debug, args);
140 BlockOrExpr::new_expr(expr)
141 } else {
142 let mut name_exprs = ThinVec::with_capacity(fields.len());
144 let mut value_exprs = ThinVec::with_capacity(fields.len());
145
146 for (i, field) in fields.into_iter().enumerate() {
147 if is_struct {
148 name_exprs.push(cx.expr_str(field.span, field.name.unwrap().name));
149 }
150
151 let field = expr_for_field(field, i);
152 value_exprs.push(field);
153 }
154
155 let names_let = is_struct.then(|| {
157 let lt_static = Some(cx.lifetime_static(span));
158 let ty_static_ref = cx.ty_ref(span, cx.ty_infer(span), lt_static, ast::Mutability::Not);
159 cx.stmt_let_ty(
160 span,
161 false,
162 Ident::new(sym::names, span),
163 Some(ty_static_ref),
164 cx.expr_array_ref(span, name_exprs),
165 )
166 });
167
168 let path_debug = cx.path_global(span, cx.std_path(&[sym::fmt, sym::Debug]));
170 let ty_dyn_debug = cx.ty(
171 span,
172 ast::TyKind::TraitObject(
173 {
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)],
174 ast::TraitObjectSyntax::Dyn,
175 ),
176 );
177 let ty_slice = cx.ty(
178 span,
179 ast::TyKind::Slice(cx.ty_ref(span, ty_dyn_debug, None, ast::Mutability::Not)),
180 );
181 let values_let = cx.stmt_let_ty(
182 span,
183 false,
184 Ident::new(sym::values, span),
185 Some(cx.ty_ref(span, ty_slice, None, ast::Mutability::Not)),
186 cx.expr_array_ref(span, value_exprs),
187 );
188
189 let sym_debug = if is_struct {
192 sym::debug_struct_fields_finish
193 } else {
194 sym::debug_tuple_fields_finish
195 };
196 let fn_path_debug_internal = cx.std_path(&[sym::fmt, sym::Formatter, sym_debug]);
197
198 let mut args = ThinVec::with_capacity(4);
199 args.push(fmt);
200 args.push(name);
201 if is_struct {
202 args.push(cx.expr_ident_sym(span, sym::names));
203 }
204 args.push(cx.expr_ident_sym(span, sym::values));
205 let expr = cx.expr_call_global(span, fn_path_debug_internal, args);
206
207 let mut stmts = ThinVec::with_capacity(2);
208 if is_struct {
209 stmts.push(names_let.unwrap());
210 }
211 stmts.push(values_let);
212 BlockOrExpr::new_mixed(stmts, Some(expr))
213 }
214}
215
216fn show_fieldless_enum(
230 cx: &ExtCtxt<'_>,
231 span: Span,
232 def: &EnumDef,
233 type_ident: Ident,
234) -> BlockOrExpr {
235 let fmt = formatter_ident(cx, span);
236 let arms = def
237 .variants
238 .iter()
239 .map(|v| {
240 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(),
[type_ident, v.ident]))vec![type_ident, v.ident]);
241 let pat = match &v.data {
242 ast::VariantData::Tuple(fields, _) => {
243 if true {
if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};
};debug_assert!(fields.is_empty());
244 cx.pat_tuple_struct(span, variant_path, ThinVec::new())
245 }
246 ast::VariantData::Struct { fields, .. } => {
247 if true {
if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};
};debug_assert!(fields.is_empty());
248 cx.pat_struct(span, variant_path, ThinVec::new())
249 }
250 ast::VariantData::Unit(_) => cx.pat_path(span, variant_path),
251 };
252 cx.arm(span, pat, cx.expr_str(span, v.ident.name))
253 })
254 .collect::<ThinVec<_>>();
255 let name = cx.expr_match(span, cx.expr_self(span), arms);
256 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
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}