rustc_builtin_macros/deriving/
debug.rs1use 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::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);
20
21 let trait_def = TraitDef {
22 span,
23 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),
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::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: 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::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 {
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!(fmt::Result)),
34 attributes: thin_vec![cx.attr_word(sym::inline, span)],
35 fieldless_variants_strategy:
36 FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless,
37 combine_substructure: combine_substructure(show_substructure),
38 }],
39 associated_types: SmallVec::new(),
40 is_const,
41 safety: Safety::Default,
42 document: true,
43 };
44 trait_def.expand(cx, item, push)
45}
46
47fn show_substructure(cx: &ExtCtxt<'_>, span: Span, substr: Substructure<'_>) -> BlockOrExpr {
48 let span = cx.with_def_site_ctxt(span);
50
51 let fmt_detail = cx.sess.opts.unstable_opts.fmt_debug;
52 if fmt_detail == FmtDebug::None {
53 return BlockOrExpr::new_expr(cx.expr_ok(span, cx.expr_tuple(span, ThinVec::new())));
54 }
55
56 let (ident, vdata, fields) = match substr.fields {
57 Struct(vdata, fields) => (substr.type_ident, vdata, fields),
58 EnumMatching(v, fields) => (v.ident, &v.data, fields),
59 AllFieldlessEnum(enum_def) => return show_fieldless_enum(cx, span, enum_def, substr),
60 _ => cx.dcx().span_bug(span, "unexpected substructure in `derive(Debug)`"),
61 };
62
63 let name = cx.expr_str(span, ident.name);
64 let fmt = substr.nonselflike_args[0].clone();
65
66 if fmt_detail == FmtDebug::Shallow {
68 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
69 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]);
70 return BlockOrExpr::new_expr(expr);
71 }
72
73 let (is_struct, args_per_field) = match vdata {
76 ast::VariantData::Unit(..) => {
77 if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};assert!(fields.is_empty());
79 (false, 0)
80 }
81 ast::VariantData::Tuple(..) => (false, 1),
82 ast::VariantData::Struct { .. } => (true, 2),
83 };
84
85 const CUTOFF: usize = 5;
87
88 let expr_for_field = |field: &FieldInfo, index: usize| -> Box<ast::Expr> {
89 if index < fields.len() - 1 {
90 field.self_expr.clone()
91 } else {
92 cx.expr_addr_of(field.span, field.self_expr.clone())
95 }
96 };
97
98 if fields.is_empty() {
99 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
101 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]);
102 BlockOrExpr::new_expr(expr)
103 } else if fields.len() <= CUTOFF {
104 let debug = if is_struct {
106 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("debug_struct_field{0}_finish",
fields.len()))
})format!("debug_struct_field{}_finish", fields.len())
107 } else {
108 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("debug_tuple_field{0}_finish",
fields.len()))
})format!("debug_tuple_field{}_finish", fields.len())
109 };
110 let fn_path_debug = cx.std_path(&[sym::fmt, sym::Formatter, Symbol::intern(&debug)]);
111
112 let mut args = ThinVec::with_capacity(2 + fields.len() * args_per_field);
113 args.extend([fmt, name]);
114 for i in 0..fields.len() {
115 let field = &fields[i];
116 if is_struct {
117 let name = cx.expr_str(field.span, field.name.unwrap().name);
118 args.push(name);
119 }
120
121 let field = expr_for_field(field, i);
122 args.push(field);
123 }
124 let expr = cx.expr_call_global(span, fn_path_debug, args);
125 BlockOrExpr::new_expr(expr)
126 } else {
127 let mut name_exprs = ThinVec::with_capacity(fields.len());
129 let mut value_exprs = ThinVec::with_capacity(fields.len());
130
131 for i in 0..fields.len() {
132 let field = &fields[i];
133 if is_struct {
134 name_exprs.push(cx.expr_str(field.span, field.name.unwrap().name));
135 }
136
137 let field = expr_for_field(field, i);
138 value_exprs.push(field);
139 }
140
141 let names_let = is_struct.then(|| {
143 let lt_static = Some(cx.lifetime_static(span));
144 let ty_static_ref = cx.ty_ref(span, cx.ty_infer(span), lt_static, ast::Mutability::Not);
145 cx.stmt_let_ty(
146 span,
147 false,
148 Ident::new(sym::names, span),
149 Some(ty_static_ref),
150 cx.expr_array_ref(span, name_exprs),
151 )
152 });
153
154 let path_debug = cx.path_global(span, cx.std_path(&[sym::fmt, sym::Debug]));
156 let ty_dyn_debug = cx.ty(
157 span,
158 ast::TyKind::TraitObject(
159 {
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)],
160 ast::TraitObjectSyntax::Dyn,
161 ),
162 );
163 let ty_slice = cx.ty(
164 span,
165 ast::TyKind::Slice(cx.ty_ref(span, ty_dyn_debug, None, ast::Mutability::Not)),
166 );
167 let values_let = cx.stmt_let_ty(
168 span,
169 false,
170 Ident::new(sym::values, span),
171 Some(cx.ty_ref(span, ty_slice, None, ast::Mutability::Not)),
172 cx.expr_array_ref(span, value_exprs),
173 );
174
175 let sym_debug = if is_struct {
178 sym::debug_struct_fields_finish
179 } else {
180 sym::debug_tuple_fields_finish
181 };
182 let fn_path_debug_internal = cx.std_path(&[sym::fmt, sym::Formatter, sym_debug]);
183
184 let mut args = ThinVec::with_capacity(4);
185 args.push(fmt);
186 args.push(name);
187 if is_struct {
188 args.push(cx.expr_ident(span, Ident::new(sym::names, span)));
189 }
190 args.push(cx.expr_ident(span, Ident::new(sym::values, span)));
191 let expr = cx.expr_call_global(span, fn_path_debug_internal, args);
192
193 let mut stmts = ThinVec::with_capacity(2);
194 if is_struct {
195 stmts.push(names_let.unwrap());
196 }
197 stmts.push(values_let);
198 BlockOrExpr::new_mixed(stmts, Some(expr))
199 }
200}
201
202fn show_fieldless_enum(
216 cx: &ExtCtxt<'_>,
217 span: Span,
218 def: &EnumDef,
219 substr: Substructure<'_>,
220) -> BlockOrExpr {
221 let fmt = substr.nonselflike_args[0].clone();
222 let arms = def
223 .variants
224 .iter()
225 .map(|v| {
226 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]);
227 let pat = match &v.data {
228 ast::VariantData::Tuple(fields, _) => {
229 if true {
if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};
};debug_assert!(fields.is_empty());
230 cx.pat_tuple_struct(span, variant_path, ThinVec::new())
231 }
232 ast::VariantData::Struct { fields, .. } => {
233 if true {
if !fields.is_empty() {
::core::panicking::panic("assertion failed: fields.is_empty()")
};
};debug_assert!(fields.is_empty());
234 cx.pat_struct(span, variant_path, ThinVec::new())
235 }
236 ast::VariantData::Unit(_) => cx.pat_path(span, variant_path),
237 };
238 cx.arm(span, pat, cx.expr_str(span, v.ident.name))
239 })
240 .collect::<ThinVec<_>>();
241 let name = cx.expr_match(span, cx.expr_self(span), arms);
242 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
243 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]))
244}