• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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