1 use crate::deriving::generic::ty::*;
2 use crate::deriving::generic::*;
3 use crate::deriving::path_std;
6 use rustc_ast::{self as ast, MetaItem};
7 use rustc_expand::base::{Annotatable, ExtCtxt};
8 use rustc_span::symbol::{sym, Ident, Symbol};
11 pub fn expand_deriving_debug(
16 push: &mut dyn FnMut(Annotatable),
19 // &mut ::std::fmt::Formatter
20 let fmtr = Ref(Box::new(Path(path_std!(fmt::Formatter))), ast::Mutability::Mut);
22 let trait_def = TraitDef {
24 path: path_std!(fmt::Debug),
25 skip_path_as_bound: false,
26 needs_copy_as_bound_if_packed: true,
27 additional_bounds: Vec::new(),
28 supports_unions: false,
29 methods: vec![MethodDef {
31 generics: Bounds::empty(),
33 nonself_args: vec![(fmtr, sym::f)],
34 ret_ty: Path(path_std!(fmt::Result)),
35 attributes: ast::AttrVec::new(),
36 fieldless_variants_strategy:
37 FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless,
38 combine_substructure: combine_substructure(Box::new(|a, b, c| {
39 show_substructure(a, b, c)
42 associated_types: Vec::new(),
45 trait_def.expand(cx, mitem, item, push)
48 fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
49 // We want to make sure we have the ctxt set so that we can use unstable methods
50 let span = cx.with_def_site_ctxt(span);
52 let (ident, vdata, fields) = match substr.fields {
53 Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
54 EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields),
55 AllFieldlessEnum(enum_def) => return show_fieldless_enum(cx, span, enum_def, substr),
56 EnumTag(..) | StaticStruct(..) | StaticEnum(..) => {
57 cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
61 let name = cx.expr_str(span, ident.name);
62 let fmt = substr.nonselflike_args[0].clone();
64 // Struct and tuples are similar enough that we use the same code for both,
65 // with some extra pieces for structs due to the field names.
66 let (is_struct, args_per_field) = match vdata {
67 ast::VariantData::Unit(..) => {
68 // Special fast path for unit variants.
69 assert!(fields.is_empty());
72 ast::VariantData::Tuple(..) => (false, 1),
73 ast::VariantData::Struct(..) => (true, 2),
76 // The number of fields that can be handled without an array.
77 const CUTOFF: usize = 5;
84 ) -> ast::ptr::P<ast::Expr> {
86 field.self_expr.clone()
88 // Unsized types need an extra indirection, but only the last field
90 cx.expr_addr_of(field.span, field.self_expr.clone())
94 if fields.is_empty() {
95 // Special case for no fields.
96 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
97 let expr = cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]);
98 BlockOrExpr::new_expr(expr)
99 } else if fields.len() <= CUTOFF {
100 // Few enough fields that we can use a specific-length method.
101 let debug = if is_struct {
102 format!("debug_struct_field{}_finish", fields.len())
104 format!("debug_tuple_field{}_finish", fields.len())
106 let fn_path_debug = cx.std_path(&[sym::fmt, sym::Formatter, Symbol::intern(&debug)]);
108 let mut args = Vec::with_capacity(2 + fields.len() * args_per_field);
109 args.extend([fmt, name]);
110 for i in 0..fields.len() {
111 let field = &fields[i];
113 let name = cx.expr_str(field.span, field.name.unwrap().name);
117 let field = expr_for_field(cx, field, i, fields.len());
120 let expr = cx.expr_call_global(span, fn_path_debug, args);
121 BlockOrExpr::new_expr(expr)
123 // Enough fields that we must use the any-length method.
124 let mut name_exprs = Vec::with_capacity(fields.len());
125 let mut value_exprs = Vec::with_capacity(fields.len());
127 for i in 0..fields.len() {
128 let field = &fields[i];
130 name_exprs.push(cx.expr_str(field.span, field.name.unwrap().name));
133 let field = expr_for_field(cx, field, i, fields.len());
134 value_exprs.push(field);
137 // `let names: &'static _ = &["field1", "field2"];`
138 let names_let = if is_struct {
139 let lt_static = Some(cx.lifetime_static(span));
140 let ty_static_ref = cx.ty_ref(span, cx.ty_infer(span), lt_static, ast::Mutability::Not);
144 Ident::new(sym::names, span),
146 cx.expr_array_ref(span, name_exprs),
152 // `let values: &[&dyn Debug] = &[&&self.field1, &&self.field2];`
153 let path_debug = cx.path_global(span, cx.std_path(&[sym::fmt, sym::Debug]));
154 let ty_dyn_debug = cx.ty(
156 ast::TyKind::TraitObject(
157 vec![cx.trait_bound(path_debug, false)],
158 ast::TraitObjectSyntax::Dyn,
161 let ty_slice = cx.ty(
163 ast::TyKind::Slice(cx.ty_ref(span, ty_dyn_debug, None, ast::Mutability::Not)),
165 let values_let = cx.stmt_let_ty(
168 Ident::new(sym::values, span),
169 Some(cx.ty_ref(span, ty_slice, None, ast::Mutability::Not)),
170 cx.expr_array_ref(span, value_exprs),
173 // `fmt::Formatter::debug_struct_fields_finish(fmt, name, names, values)` or
174 // `fmt::Formatter::debug_tuple_fields_finish(fmt, name, values)`
175 let sym_debug = if is_struct {
176 sym::debug_struct_fields_finish
178 sym::debug_tuple_fields_finish
180 let fn_path_debug_internal = cx.std_path(&[sym::fmt, sym::Formatter, sym_debug]);
182 let mut args = Vec::with_capacity(4);
186 args.push(cx.expr_ident(span, Ident::new(sym::names, span)));
188 args.push(cx.expr_ident(span, Ident::new(sym::values, span)));
189 let expr = cx.expr_call_global(span, fn_path_debug_internal, args);
191 let mut stmts = Vec::with_capacity(3);
193 stmts.push(names_let.unwrap());
195 stmts.push(values_let);
196 BlockOrExpr::new_mixed(stmts, Some(expr))
200 /// Special case for enums with no fields. Builds:
202 /// impl ::core::fmt::Debug for A {
203 /// fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
204 /// ::core::fmt::Formatter::write_str(f,
213 fn show_fieldless_enum(
214 cx: &mut ExtCtxt<'_>,
217 substr: &Substructure<'_>,
219 let fmt = substr.nonselflike_args[0].clone();
224 let variant_path = cx.path(span, vec![substr.type_ident, v.ident]);
225 let pat = match &v.data {
226 ast::VariantData::Tuple(fields, _) => {
227 debug_assert!(fields.is_empty());
228 cx.pat_tuple_struct(span, variant_path, vec![])
230 ast::VariantData::Struct(fields, _) => {
231 debug_assert!(fields.is_empty());
232 cx.pat_struct(span, variant_path, vec![])
234 ast::VariantData::Unit(_) => cx.pat_path(span, variant_path),
236 cx.arm(span, pat, cx.expr_str(span, v.ident.name))
238 .collect::<Vec<_>>();
239 let name = cx.expr_match(span, cx.expr_self(span), arms);
240 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
241 BlockOrExpr::new_expr(cx.expr_call_global(span, fn_path_write_str, vec![fmt, name]))