bindgen/codegen/
impl_debug.rs

1use crate::ir::comp::{BitfieldUnit, CompKind, Field, FieldData, FieldMethods};
2use crate::ir::context::BindgenContext;
3use crate::ir::item::{HasTypeParamInArray, IsOpaque, Item, ItemCanonicalName};
4use crate::ir::ty::{TypeKind, RUST_DERIVE_IN_ARRAY_LIMIT};
5use std::fmt::Write as _;
6
7pub(crate) fn gen_debug_impl(
8    ctx: &BindgenContext,
9    fields: &[Field],
10    item: &Item,
11    kind: CompKind,
12) -> proc_macro2::TokenStream {
13    let struct_name = item.canonical_name(ctx);
14    let mut format_string = format!("{struct_name} {{{{ ");
15    let mut tokens = vec![];
16
17    if item.is_opaque(ctx, &()) {
18        format_string.push_str("opaque");
19    } else {
20        match kind {
21            CompKind::Union => {
22                format_string.push_str("union");
23            }
24            CompKind::Struct => {
25                let processed_fields = fields.iter().filter_map(|f| match f {
26                    Field::DataMember(ref fd) => fd.impl_debug(ctx, ()),
27                    Field::Bitfields(ref bu) => bu.impl_debug(ctx, ()),
28                });
29
30                for (i, (fstring, toks)) in processed_fields.enumerate() {
31                    if i > 0 {
32                        format_string.push_str(", ");
33                    }
34                    tokens.extend(toks);
35                    format_string.push_str(&fstring);
36                }
37            }
38        }
39    }
40
41    format_string.push_str(" }}");
42    tokens.insert(0, quote! { #format_string });
43
44    let prefix = ctx.trait_prefix();
45
46    quote! {
47        fn fmt(&self, f: &mut ::#prefix::fmt::Formatter<'_>) -> ::#prefix ::fmt::Result {
48            write!(f, #( #tokens ),*)
49        }
50    }
51}
52
53/// A trait for the things which we can codegen tokens that contribute towards a
54/// generated `impl Debug`.
55pub(crate) trait ImplDebug<'a> {
56    /// Any extra parameter required by this a particular `ImplDebug` implementation.
57    type Extra;
58
59    /// Generate a format string snippet to be included in the larger `impl Debug`
60    /// format string, and the code to get the format string's interpolation values.
61    fn impl_debug(
62        &self,
63        ctx: &BindgenContext,
64        extra: Self::Extra,
65    ) -> Option<(String, Vec<proc_macro2::TokenStream>)>;
66}
67
68impl ImplDebug<'_> for FieldData {
69    type Extra = ();
70
71    fn impl_debug(
72        &self,
73        ctx: &BindgenContext,
74        _: Self::Extra,
75    ) -> Option<(String, Vec<proc_macro2::TokenStream>)> {
76        if let Some(name) = self.name() {
77            ctx.resolve_item(self.ty()).impl_debug(ctx, name)
78        } else {
79            None
80        }
81    }
82}
83
84impl ImplDebug<'_> for BitfieldUnit {
85    type Extra = ();
86
87    fn impl_debug(
88        &self,
89        ctx: &BindgenContext,
90        _: Self::Extra,
91    ) -> Option<(String, Vec<proc_macro2::TokenStream>)> {
92        let mut format_string = String::new();
93        let mut tokens = vec![];
94        for (i, bitfield) in self.bitfields().iter().enumerate() {
95            if i > 0 {
96                format_string.push_str(", ");
97            }
98
99            if let Some(bitfield_name) = bitfield.name() {
100                let _ = write!(format_string, "{bitfield_name} : {{:?}}");
101                let getter_name = bitfield.getter_name();
102                let name_ident = ctx.rust_ident_raw(getter_name);
103                tokens.push(quote! {
104                    self.#name_ident ()
105                });
106            }
107        }
108
109        Some((format_string, tokens))
110    }
111}
112
113impl<'a> ImplDebug<'a> for Item {
114    type Extra = &'a str;
115
116    fn impl_debug(
117        &self,
118        ctx: &BindgenContext,
119        name: &str,
120    ) -> Option<(String, Vec<proc_macro2::TokenStream>)> {
121        let name_ident = ctx.rust_ident(name);
122
123        // We don't know if blocklisted items `impl Debug` or not, so we can't
124        // add them to the format string we're building up.
125        if !ctx.allowlisted_items().contains(&self.id()) {
126            return None;
127        }
128
129        let ty = self.as_type()?;
130
131        fn debug_print(
132            name: &str,
133            name_ident: proc_macro2::TokenStream,
134        ) -> Option<(String, Vec<proc_macro2::TokenStream>)> {
135            Some((
136                format!("{name}: {{:?}}"),
137                vec![quote! {
138                    self.#name_ident
139                }],
140            ))
141        }
142
143        match *ty.kind() {
144            // Handle the simple cases.
145            TypeKind::Void |
146            TypeKind::NullPtr |
147            TypeKind::Int(..) |
148            TypeKind::Float(..) |
149            TypeKind::Complex(..) |
150            TypeKind::Function(..) |
151            TypeKind::Enum(..) |
152            TypeKind::Reference(..) |
153            TypeKind::UnresolvedTypeRef(..) |
154            TypeKind::ObjCInterface(..) |
155            TypeKind::ObjCId |
156            TypeKind::Comp(..) |
157            TypeKind::ObjCSel => debug_print(name, quote! { #name_ident }),
158
159            TypeKind::TemplateInstantiation(ref inst) => {
160                if inst.is_opaque(ctx, self) {
161                    Some((format!("{name}: opaque"), vec![]))
162                } else {
163                    debug_print(name, quote! { #name_ident })
164                }
165            }
166
167            // The generic is not required to implement Debug, so we can not debug print that type
168            TypeKind::TypeParam => {
169                Some((format!("{name}: Non-debuggable generic"), vec![]))
170            }
171
172            TypeKind::Array(_, len) => {
173                // Generics are not required to implement Debug
174                if self.has_type_param_in_array(ctx) {
175                    Some((format!("{name}: Array with length {len}"), vec![]))
176                } else if len < RUST_DERIVE_IN_ARRAY_LIMIT ||
177                    ctx.options().rust_features().larger_arrays
178                {
179                    // The simple case
180                    debug_print(name, quote! { #name_ident })
181                } else if ctx.options().use_core {
182                    // There is no String in core; reducing field visibility to avoid breaking
183                    // no_std setups.
184                    Some((format!("{name}: [...]"), vec![]))
185                } else {
186                    // Let's implement our own print function
187                    Some((
188                        format!("{name}: [{{}}]"),
189                        vec![quote! {{
190                            use std::fmt::Write as _;
191                            let mut output = String::new();
192                            let mut iter = self.#name_ident.iter();
193                            if let Some(value) = iter.next() {
194                                let _ = write!(output, "{value:?}");
195                                for value in iter {
196                                    let _ = write!(output, ", {value:?}");
197                                }
198                            }
199                            output
200                        }}],
201                    ))
202                }
203            }
204            TypeKind::Vector(_, len) => {
205                if ctx.options().use_core {
206                    // There is no format! in core; reducing field visibility to avoid breaking
207                    // no_std setups.
208                    Some((format!("{name}(...)"), vec![]))
209                } else {
210                    let self_ids = 0..len;
211                    Some((
212                        format!("{name}({{}})"),
213                        vec![quote! {
214                            #(format!("{:?}", self.#self_ids)),*
215                        }],
216                    ))
217                }
218            }
219
220            TypeKind::ResolvedTypeRef(t) |
221            TypeKind::TemplateAlias(t, _) |
222            TypeKind::Alias(t) |
223            TypeKind::BlockPointer(t) => {
224                // We follow the aliases
225                ctx.resolve_item(t).impl_debug(ctx, name)
226            }
227
228            TypeKind::Pointer(inner) => {
229                let inner_type = ctx.resolve_type(inner).canonical_type(ctx);
230                match *inner_type.kind() {
231                    TypeKind::Function(ref sig)
232                        if !sig.function_pointers_can_derive() =>
233                    {
234                        Some((format!("{name}: FunctionPointer"), vec![]))
235                    }
236                    _ => debug_print(name, quote! { #name_ident }),
237                }
238            }
239
240            TypeKind::Opaque => None,
241        }
242    }
243}