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::ty::*;
8use crate::deriving::generic::*;
9use crate::deriving::path_std;
10
11pub(crate) fn expand_deriving_debug(
12 cx: &ExtCtxt<'_>,
13 span: Span,
14 item: &ast::Item,
15 push: &mut dyn FnMut(Box<ast::Item>),
16 is_const: bool,
17) {
18 let fmtr = Ref(Box::new(Path(generic::ty::new_path(cx, span, { &[sym::fmt, sym::Formatter] }, &[])path_std!(cx, span, fmt::Formatter))), ast::Mutability::Mut);
20
21 let trait_def = TraitDef {
22 span,
23 path: generic::ty::new_path(cx, span, { &[sym::fmt, sym::Debug] }, &[])path_std!(cx, span, fmt::Debug),
24 skip_path_as_bound: false,
25 needs_copy_as_bound_if_packed: true,
26 additional_bounds: SmallVec::new(),
27 supports_unions: false,
28 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'))])))
}
},
ret_ty: Path(generic::ty::new_path(cx, span,
{ &[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(|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'))])))
}
},
ret_ty: Path(generic::ty::new_path(cx, span,
{ &[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(|cx, span,
substr|
show_substructure(cx, span, substr,
item.kind.ident().unwrap())),
}])))
}
}smallvec![MethodDef {
29 name: sym::fmt,
30 generics: cx.empty_generics(span),
31 explicit_self: true,
32 nonself_args: smallvec![(fmtr, sym::character('f'))],
33 ret_ty: Path(path_std!(cx, span, fmt::Result)),
34 attributes: thin_vec![cx.attr_word(sym::inline, span)],
35 fieldless_variants_strategy:
36 FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless,
37 combine_substructure: combine_substructure(|cx, span, substr| show_substructure(
38 cx,
39 span,
40 substr,
41 item.kind.ident().unwrap()
42 )),
43 }],
44 associated_types: SmallVec::new(),
45 is_const,
46 safety: Safety::Default,
47 document: true,
48 };
49 trait_def.expand(cx, item, push)
50}
51
52fn formatter_ident(cx: &ExtCtxt<'_>, span: Span) -> Box<ast::Expr> {
53 cx.expr_ident(span, Ident::new(sym::character('f'), span))
54}
55
56fn show_substructure(
57 cx: &ExtCtxt<'_>,
58 span: Span,
59 substr: Substructure<'_>,
60 type_ident: Ident,
61) -> BlockOrExpr {
62 let fmt_detail = cx.sess.opts.unstable_opts.fmt_debug;
63 if fmt_detail == FmtDebug::None {
64 return BlockOrExpr::new_expr(cx.expr_ok(span, cx.expr_tuple(span, ThinVec::new())));
65 }
66
67 let (ident, vdata, fields) = match substr {
68 Struct(vdata, fields) => (type_ident, vdata, fields),
69 EnumMatching(v, fields) => (v.ident, &v.data, fields),
70 AllFieldlessEnum(enum_def) => return show_fieldless_enum(cx, span, enum_def, type_ident),
71 _ => cx.dcx().span_bug(span, "unexpected substructure in `derive(Debug)`"),
72 };
73
74 let name = cx.expr_str(span, ident.name);
75 let fmt = formatter_ident(cx, span);
76
77 if fmt_detail == FmtDebug::Shallow {
79 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
80 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]);
81 return BlockOrExpr::new_expr(expr);
82 }
83
84 let (is_struct, args_per_field) = match vdata {
87 ast::VariantData::Unit(..) => {
88 if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};assert!(fields.is_empty());
90 (false, 0)
91 }
92 ast::VariantData::Tuple(..) => (false, 1),
93 ast::VariantData::Struct { .. } => (true, 2),
94 };
95
96 const CUTOFF: usize = 5;
98
99 let len = fields.len();
100 let expr_for_field = |field: FieldInfo, index: usize| -> Box<ast::Expr> {
101 if index < len - 1 {
102 field.self_expr
103 } else {
104 cx.expr_addr_of(field.span, field.self_expr)
107 }
108 };
109
110 if fields.is_empty() {
111 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
113 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]);
114 BlockOrExpr::new_expr(expr)
115 } else if fields.len() <= CUTOFF {
116 let debug = if is_struct {
118 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("debug_struct_field{0}_finish",
fields.len()))
})format!("debug_struct_field{}_finish", fields.len())
119 } else {
120 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("debug_tuple_field{0}_finish",
fields.len()))
})format!("debug_tuple_field{}_finish", fields.len())
121 };
122 let fn_path_debug = cx.std_path(&[sym::fmt, sym::Formatter, Symbol::intern(&debug)]);
123
124 let mut args = ThinVec::with_capacity(2 + fields.len() * args_per_field);
125 args.extend([fmt, name]);
126 for (i, field) in fields.into_iter().enumerate() {
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(field, i);
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, field) in fields.into_iter().enumerate() {
143 if is_struct {
144 name_exprs.push(cx.expr_str(field.span, field.name.unwrap().name));
145 }
146
147 let field = expr_for_field(field, i);
148 value_exprs.push(field);
149 }
150
151 let names_let = is_struct.then(|| {
153 let lt_static = Some(cx.lifetime_static(span));
154 let ty_static_ref = cx.ty_ref(span, cx.ty_infer(span), lt_static, ast::Mutability::Not);
155 cx.stmt_let_ty(
156 span,
157 false,
158 Ident::new(sym::names, span),
159 Some(ty_static_ref),
160 cx.expr_array_ref(span, name_exprs),
161 )
162 });
163
164 let path_debug = cx.path_global(span, cx.std_path(&[sym::fmt, sym::Debug]));
166 let ty_dyn_debug = cx.ty(
167 span,
168 ast::TyKind::TraitObject(
169 {
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)],
170 ast::TraitObjectSyntax::Dyn,
171 ),
172 );
173 let ty_slice = cx.ty(
174 span,
175 ast::TyKind::Slice(cx.ty_ref(span, ty_dyn_debug, None, ast::Mutability::Not)),
176 );
177 let values_let = cx.stmt_let_ty(
178 span,
179 false,
180 Ident::new(sym::values, span),
181 Some(cx.ty_ref(span, ty_slice, None, ast::Mutability::Not)),
182 cx.expr_array_ref(span, value_exprs),
183 );
184
185 let sym_debug = if is_struct {
188 sym::debug_struct_fields_finish
189 } else {
190 sym::debug_tuple_fields_finish
191 };
192 let fn_path_debug_internal = cx.std_path(&[sym::fmt, sym::Formatter, sym_debug]);
193
194 let mut args = ThinVec::with_capacity(4);
195 args.push(fmt);
196 args.push(name);
197 if is_struct {
198 args.push(cx.expr_ident(span, Ident::new(sym::names, span)));
199 }
200 args.push(cx.expr_ident(span, Ident::new(sym::values, span)));
201 let expr = cx.expr_call_global(span, fn_path_debug_internal, args);
202
203 let mut stmts = ThinVec::with_capacity(2);
204 if is_struct {
205 stmts.push(names_let.unwrap());
206 }
207 stmts.push(values_let);
208 BlockOrExpr::new_mixed(stmts, Some(expr))
209 }
210}
211
212fn show_fieldless_enum(
226 cx: &ExtCtxt<'_>,
227 span: Span,
228 def: &EnumDef,
229 type_ident: Ident,
230) -> BlockOrExpr {
231 let fmt = formatter_ident(cx, span);
232 let arms = def
233 .variants
234 .iter()
235 .map(|v| {
236 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]);
237 let pat = match &v.data {
238 ast::VariantData::Tuple(fields, _) => {
239 if true {
if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};
};debug_assert!(fields.is_empty());
240 cx.pat_tuple_struct(span, variant_path, ThinVec::new())
241 }
242 ast::VariantData::Struct { fields, .. } => {
243 if true {
if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};
};debug_assert!(fields.is_empty());
244 cx.pat_struct(span, variant_path, ThinVec::new())
245 }
246 ast::VariantData::Unit(_) => cx.pat_path(span, variant_path),
247 };
248 cx.arm(span, pat, cx.expr_str(span, v.ident.name))
249 })
250 .collect::<ThinVec<_>>();
251 let name = cx.expr_match(span, cx.expr_self(span), arms);
252 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
253 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]))
254}