1 use crate::deriving
::generic
::ty
::*;
2 use crate::deriving
::generic
::*;
3 use crate::deriving
::path_std
;
5 use rustc_ast
::{self as ast, MetaItem}
;
6 use rustc_expand
::base
::{Annotatable, ExtCtxt}
;
7 use rustc_span
::symbol
::{sym, Ident, Symbol}
;
10 pub fn expand_deriving_debug(
15 push
: &mut dyn FnMut(Annotatable
),
18 // &mut ::std::fmt::Formatter
19 let fmtr
= Ref(Box
::new(Path(path_std
!(fmt
::Formatter
))), ast
::Mutability
::Mut
);
21 let trait_def
= TraitDef
{
23 path
: path_std
!(fmt
::Debug
),
24 skip_path_as_bound
: false,
25 additional_bounds
: Vec
::new(),
26 supports_unions
: false,
27 methods
: vec
![MethodDef
{
29 generics
: Bounds
::empty(),
31 nonself_args
: vec
![(fmtr
, sym
::f
)],
32 ret_ty
: Path(path_std
!(fmt
::Result
)),
33 attributes
: ast
::AttrVec
::new(),
34 unify_fieldless_variants
: false,
35 combine_substructure
: combine_substructure(Box
::new(|a
, b
, c
| {
36 show_substructure(a
, b
, c
)
39 associated_types
: Vec
::new(),
42 trait_def
.expand(cx
, mitem
, item
, push
)
45 fn show_substructure(cx
: &mut ExtCtxt
<'_
>, span
: Span
, substr
: &Substructure
<'_
>) -> BlockOrExpr
{
46 let (ident
, vdata
, fields
) = match substr
.fields
{
47 Struct(vdata
, fields
) => (substr
.type_ident
, *vdata
, fields
),
48 EnumMatching(_
, _
, v
, fields
) => (v
.ident
, &v
.data
, fields
),
49 EnumTag(..) | StaticStruct(..) | StaticEnum(..) => {
50 cx
.span_bug(span
, "nonsensical .fields in `#[derive(Debug)]`")
54 // We want to make sure we have the ctxt set so that we can use unstable methods
55 let span
= cx
.with_def_site_ctxt(span
);
56 let name
= cx
.expr_str(span
, ident
.name
);
57 let fmt
= substr
.nonselflike_args
[0].clone();
59 // Struct and tuples are similar enough that we use the same code for both,
60 // with some extra pieces for structs due to the field names.
61 let (is_struct
, args_per_field
) = match vdata
{
62 ast
::VariantData
::Unit(..) => {
63 // Special fast path for unit variants.
64 assert
!(fields
.is_empty());
67 ast
::VariantData
::Tuple(..) => (false, 1),
68 ast
::VariantData
::Struct(..) => (true, 2),
71 // The number of fields that can be handled without an array.
72 const CUTOFF
: usize = 5;
74 if fields
.is_empty() {
75 // Special case for no fields.
76 let fn_path_write_str
= cx
.std_path(&[sym
::fmt
, sym
::Formatter
, sym
::write_str
]);
77 let expr
= cx
.expr_call_global(span
, fn_path_write_str
, vec
![fmt
, name
]);
78 BlockOrExpr
::new_expr(expr
)
79 } else if fields
.len() <= CUTOFF
{
80 // Few enough fields that we can use a specific-length method.
81 let debug
= if is_struct
{
82 format
!("debug_struct_field{}_finish", fields
.len())
84 format
!("debug_tuple_field{}_finish", fields
.len())
86 let fn_path_debug
= cx
.std_path(&[sym
::fmt
, sym
::Formatter
, Symbol
::intern(&debug
)]);
88 let mut args
= Vec
::with_capacity(2 + fields
.len() * args_per_field
);
89 args
.extend([fmt
, name
]);
90 for i
in 0..fields
.len() {
91 let field
= &fields
[i
];
93 let name
= cx
.expr_str(field
.span
, field
.name
.unwrap().name
);
96 // Use an extra indirection to make sure this works for unsized types.
97 let field
= cx
.expr_addr_of(field
.span
, field
.self_expr
.clone());
100 let expr
= cx
.expr_call_global(span
, fn_path_debug
, args
);
101 BlockOrExpr
::new_expr(expr
)
103 // Enough fields that we must use the any-length method.
104 let mut name_exprs
= Vec
::with_capacity(fields
.len());
105 let mut value_exprs
= Vec
::with_capacity(fields
.len());
107 for field
in fields
{
109 name_exprs
.push(cx
.expr_str(field
.span
, field
.name
.unwrap().name
));
112 // Use an extra indirection to make sure this works for unsized types.
113 let field
= cx
.expr_addr_of(field
.span
, field
.self_expr
.clone());
114 value_exprs
.push(field
);
117 // `let names: &'static _ = &["field1", "field2"];`
118 let names_let
= if is_struct
{
119 let lt_static
= Some(cx
.lifetime_static(span
));
121 cx
.ty_rptr(span
, cx
.ty_infer(span
), lt_static
, ast
::Mutability
::Not
);
125 Ident
::new(sym
::names
, span
),
127 cx
.expr_array_ref(span
, name_exprs
),
133 // `let values: &[&dyn Debug] = &[&&self.field1, &&self.field2];`
134 let path_debug
= cx
.path_global(span
, cx
.std_path(&[sym
::fmt
, sym
::Debug
]));
135 let ty_dyn_debug
= cx
.ty(
137 ast
::TyKind
::TraitObject(vec
![cx
.trait_bound(path_debug
)], ast
::TraitObjectSyntax
::Dyn
),
139 let ty_slice
= cx
.ty(
141 ast
::TyKind
::Slice(cx
.ty_rptr(span
, ty_dyn_debug
, None
, ast
::Mutability
::Not
)),
143 let values_let
= cx
.stmt_let_ty(
146 Ident
::new(sym
::values
, span
),
147 Some(cx
.ty_rptr(span
, ty_slice
, None
, ast
::Mutability
::Not
)),
148 cx
.expr_array_ref(span
, value_exprs
),
151 // `fmt::Formatter::debug_struct_fields_finish(fmt, name, names, values)` or
152 // `fmt::Formatter::debug_tuple_fields_finish(fmt, name, values)`
153 let sym_debug
= if is_struct
{
154 sym
::debug_struct_fields_finish
156 sym
::debug_tuple_fields_finish
158 let fn_path_debug_internal
= cx
.std_path(&[sym
::fmt
, sym
::Formatter
, sym_debug
]);
160 let mut args
= Vec
::with_capacity(4);
164 args
.push(cx
.expr_ident(span
, Ident
::new(sym
::names
, span
)));
166 args
.push(cx
.expr_ident(span
, Ident
::new(sym
::values
, span
)));
167 let expr
= cx
.expr_call_global(span
, fn_path_debug_internal
, args
);
169 let mut stmts
= Vec
::with_capacity(3);
171 stmts
.push(names_let
.unwrap());
173 stmts
.push(values_let
);
174 BlockOrExpr
::new_mixed(stmts
, Some(expr
))