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