1 use crate::deriving::generic::ty::*;
2 use crate::deriving::generic::*;
3 use crate::deriving::path_std;
4
5 use ast::EnumDef;
6 use rustc_ast::{self as ast, MetaItem};
7 use rustc_expand::base::{Annotatable, ExtCtxt};
8 use rustc_span::symbol::{sym, Ident, Symbol};
9 use rustc_span::Span;
10 use thin_vec::{thin_vec, ThinVec};
11
expand_deriving_debug( cx: &mut ExtCtxt<'_>, span: Span, mitem: &MetaItem, item: &Annotatable, push: &mut dyn FnMut(Annotatable), is_const: bool, )12 pub fn expand_deriving_debug(
13 cx: &mut ExtCtxt<'_>,
14 span: Span,
15 mitem: &MetaItem,
16 item: &Annotatable,
17 push: &mut dyn FnMut(Annotatable),
18 is_const: bool,
19 ) {
20 // &mut ::std::fmt::Formatter
21 let fmtr = Ref(Box::new(Path(path_std!(fmt::Formatter))), ast::Mutability::Mut);
22
23 let trait_def = TraitDef {
24 span,
25 path: path_std!(fmt::Debug),
26 skip_path_as_bound: false,
27 needs_copy_as_bound_if_packed: true,
28 additional_bounds: Vec::new(),
29 supports_unions: false,
30 methods: vec![MethodDef {
31 name: sym::fmt,
32 generics: Bounds::empty(),
33 explicit_self: true,
34 nonself_args: vec![(fmtr, sym::f)],
35 ret_ty: Path(path_std!(fmt::Result)),
36 attributes: ast::AttrVec::new(),
37 fieldless_variants_strategy:
38 FieldlessVariantsStrategy::SpecializeIfAllVariantsFieldless,
39 combine_substructure: combine_substructure(Box::new(|a, b, c| {
40 show_substructure(a, b, c)
41 })),
42 }],
43 associated_types: Vec::new(),
44 is_const,
45 };
46 trait_def.expand(cx, mitem, item, push)
47 }
48
show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr49 fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>) -> BlockOrExpr {
50 // We want to make sure we have the ctxt set so that we can use unstable methods
51 let span = cx.with_def_site_ctxt(span);
52
53 let (ident, vdata, fields) = match substr.fields {
54 Struct(vdata, fields) => (substr.type_ident, *vdata, fields),
55 EnumMatching(_, _, v, fields) => (v.ident, &v.data, fields),
56 AllFieldlessEnum(enum_def) => return show_fieldless_enum(cx, span, enum_def, substr),
57 EnumTag(..) | StaticStruct(..) | StaticEnum(..) => {
58 cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
59 }
60 };
61
62 let name = cx.expr_str(span, ident.name);
63 let fmt = substr.nonselflike_args[0].clone();
64
65 // Struct and tuples are similar enough that we use the same code for both,
66 // with some extra pieces for structs due to the field names.
67 let (is_struct, args_per_field) = match vdata {
68 ast::VariantData::Unit(..) => {
69 // Special fast path for unit variants.
70 assert!(fields.is_empty());
71 (false, 0)
72 }
73 ast::VariantData::Tuple(..) => (false, 1),
74 ast::VariantData::Struct(..) => (true, 2),
75 };
76
77 // The number of fields that can be handled without an array.
78 const CUTOFF: usize = 5;
79
80 fn expr_for_field(
81 cx: &ExtCtxt<'_>,
82 field: &FieldInfo,
83 index: usize,
84 len: usize,
85 ) -> ast::ptr::P<ast::Expr> {
86 if index < len - 1 {
87 field.self_expr.clone()
88 } else {
89 // Unsized types need an extra indirection, but only the last field
90 // may be unsized.
91 cx.expr_addr_of(field.span, field.self_expr.clone())
92 }
93 }
94
95 if fields.is_empty() {
96 // Special case for no fields.
97 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
98 let expr = cx.expr_call_global(span, fn_path_write_str, thin_vec![fmt, name]);
99 BlockOrExpr::new_expr(expr)
100 } else if fields.len() <= CUTOFF {
101 // Few enough fields that we can use a specific-length method.
102 let debug = if is_struct {
103 format!("debug_struct_field{}_finish", fields.len())
104 } else {
105 format!("debug_tuple_field{}_finish", fields.len())
106 };
107 let fn_path_debug = cx.std_path(&[sym::fmt, sym::Formatter, Symbol::intern(&debug)]);
108
109 let mut args = ThinVec::with_capacity(2 + fields.len() * args_per_field);
110 args.extend([fmt, name]);
111 for i in 0..fields.len() {
112 let field = &fields[i];
113 if is_struct {
114 let name = cx.expr_str(field.span, field.name.unwrap().name);
115 args.push(name);
116 }
117
118 let field = expr_for_field(cx, field, i, fields.len());
119 args.push(field);
120 }
121 let expr = cx.expr_call_global(span, fn_path_debug, args);
122 BlockOrExpr::new_expr(expr)
123 } else {
124 // Enough fields that we must use the any-length method.
125 let mut name_exprs = ThinVec::with_capacity(fields.len());
126 let mut value_exprs = ThinVec::with_capacity(fields.len());
127
128 for i in 0..fields.len() {
129 let field = &fields[i];
130 if is_struct {
131 name_exprs.push(cx.expr_str(field.span, field.name.unwrap().name));
132 }
133
134 let field = expr_for_field(cx, field, i, fields.len());
135 value_exprs.push(field);
136 }
137
138 // `let names: &'static _ = &["field1", "field2"];`
139 let names_let = is_struct.then(|| {
140 let lt_static = Some(cx.lifetime_static(span));
141 let ty_static_ref = cx.ty_ref(span, cx.ty_infer(span), lt_static, ast::Mutability::Not);
142 cx.stmt_let_ty(
143 span,
144 false,
145 Ident::new(sym::names, span),
146 Some(ty_static_ref),
147 cx.expr_array_ref(span, name_exprs),
148 )
149 });
150
151 // `let values: &[&dyn Debug] = &[&&self.field1, &&self.field2];`
152 let path_debug = cx.path_global(span, cx.std_path(&[sym::fmt, sym::Debug]));
153 let ty_dyn_debug = cx.ty(
154 span,
155 ast::TyKind::TraitObject(
156 vec![cx.trait_bound(path_debug, false)],
157 ast::TraitObjectSyntax::Dyn,
158 ),
159 );
160 let ty_slice = cx.ty(
161 span,
162 ast::TyKind::Slice(cx.ty_ref(span, ty_dyn_debug, None, ast::Mutability::Not)),
163 );
164 let values_let = cx.stmt_let_ty(
165 span,
166 false,
167 Ident::new(sym::values, span),
168 Some(cx.ty_ref(span, ty_slice, None, ast::Mutability::Not)),
169 cx.expr_array_ref(span, value_exprs),
170 );
171
172 // `fmt::Formatter::debug_struct_fields_finish(fmt, name, names, values)` or
173 // `fmt::Formatter::debug_tuple_fields_finish(fmt, name, values)`
174 let sym_debug = if is_struct {
175 sym::debug_struct_fields_finish
176 } else {
177 sym::debug_tuple_fields_finish
178 };
179 let fn_path_debug_internal = cx.std_path(&[sym::fmt, sym::Formatter, sym_debug]);
180
181 let mut args = ThinVec::with_capacity(4);
182 args.push(fmt);
183 args.push(name);
184 if is_struct {
185 args.push(cx.expr_ident(span, Ident::new(sym::names, span)));
186 }
187 args.push(cx.expr_ident(span, Ident::new(sym::values, span)));
188 let expr = cx.expr_call_global(span, fn_path_debug_internal, args);
189
190 let mut stmts = ThinVec::with_capacity(2);
191 if is_struct {
192 stmts.push(names_let.unwrap());
193 }
194 stmts.push(values_let);
195 BlockOrExpr::new_mixed(stmts, Some(expr))
196 }
197 }
198
199 /// Special case for enums with no fields. Builds:
200 /// ```text
201 /// impl ::core::fmt::Debug for A {
202 /// fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
203 /// ::core::fmt::Formatter::write_str(f,
204 /// match self {
205 /// A::A => "A",
206 /// A::B() => "B",
207 /// A::C {} => "C",
208 /// })
209 /// }
210 /// }
211 /// ```
show_fieldless_enum( cx: &mut ExtCtxt<'_>, span: Span, def: &EnumDef, substr: &Substructure<'_>, ) -> BlockOrExpr212 fn show_fieldless_enum(
213 cx: &mut ExtCtxt<'_>,
214 span: Span,
215 def: &EnumDef,
216 substr: &Substructure<'_>,
217 ) -> BlockOrExpr {
218 let fmt = substr.nonselflike_args[0].clone();
219 let arms = def
220 .variants
221 .iter()
222 .map(|v| {
223 let variant_path = cx.path(span, vec![substr.type_ident, v.ident]);
224 let pat = match &v.data {
225 ast::VariantData::Tuple(fields, _) => {
226 debug_assert!(fields.is_empty());
227 cx.pat_tuple_struct(span, variant_path, ThinVec::new())
228 }
229 ast::VariantData::Struct(fields, _) => {
230 debug_assert!(fields.is_empty());
231 cx.pat_struct(span, variant_path, ThinVec::new())
232 }
233 ast::VariantData::Unit(_) => cx.pat_path(span, variant_path),
234 };
235 cx.arm(span, pat, cx.expr_str(span, v.ident.name))
236 })
237 .collect::<ThinVec<_>>();
238 let name = cx.expr_match(span, cx.expr_self(span), arms);
239 let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
240 BlockOrExpr::new_expr(cx.expr_call_global(span, fn_path_write_str, thin_vec![fmt, name]))
241 }
242