use crate::ir::comp::{BitfieldUnit, CompKind, Field, FieldData, FieldMethods}; use crate::ir::context::BindgenContext; use crate::ir::item::{HasTypeParamInArray, IsOpaque, Item, ItemCanonicalName}; use crate::ir::ty::{TypeKind, RUST_DERIVE_IN_ARRAY_LIMIT}; pub fn gen_debug_impl( ctx: &BindgenContext, fields: &[Field], item: &Item, kind: CompKind, ) -> proc_macro2::TokenStream { let struct_name = item.canonical_name(ctx); let mut format_string = format!("{} {{{{ ", struct_name); let mut tokens = vec![]; if item.is_opaque(ctx, &()) { format_string.push_str("opaque"); } else { match kind { CompKind::Union => { format_string.push_str("union"); } CompKind::Struct => { let processed_fields = fields.iter().filter_map(|f| match f { Field::DataMember(ref fd) => fd.impl_debug(ctx, ()), Field::Bitfields(ref bu) => bu.impl_debug(ctx, ()), }); for (i, (fstring, toks)) in processed_fields.enumerate() { if i > 0 { format_string.push_str(", "); } tokens.extend(toks); format_string.push_str(&fstring); } } } } format_string.push_str(" }}"); tokens.insert(0, quote! { #format_string }); let prefix = ctx.trait_prefix(); quote! { fn fmt(&self, f: &mut ::#prefix::fmt::Formatter<'_>) -> ::#prefix ::fmt::Result { write!(f, #( #tokens ),*) } } } /// A trait for the things which we can codegen tokens that contribute towards a /// generated `impl Debug`. pub trait ImplDebug<'a> { /// Any extra parameter required by this a particular `ImplDebug` implementation. type Extra; /// Generate a format string snippet to be included in the larger `impl Debug` /// format string, and the code to get the format string's interpolation values. fn impl_debug( &self, ctx: &BindgenContext, extra: Self::Extra, ) -> Option<(String, Vec)>; } impl<'a> ImplDebug<'a> for FieldData { type Extra = (); fn impl_debug( &self, ctx: &BindgenContext, _: Self::Extra, ) -> Option<(String, Vec)> { if let Some(name) = self.name() { ctx.resolve_item(self.ty()).impl_debug(ctx, name) } else { None } } } impl<'a> ImplDebug<'a> for BitfieldUnit { type Extra = (); fn impl_debug( &self, ctx: &BindgenContext, _: Self::Extra, ) -> Option<(String, Vec)> { let mut format_string = String::new(); let mut tokens = vec![]; for (i, bitfield) in self.bitfields().iter().enumerate() { if i > 0 { format_string.push_str(", "); } if let Some(bitfield_name) = bitfield.name() { format_string.push_str(&format!("{} : {{:?}}", bitfield_name)); let getter_name = bitfield.getter_name(); let name_ident = ctx.rust_ident_raw(getter_name); tokens.push(quote! { self.#name_ident () }); } } Some((format_string, tokens)) } } impl<'a> ImplDebug<'a> for Item { type Extra = &'a str; fn impl_debug( &self, ctx: &BindgenContext, name: &str, ) -> Option<(String, Vec)> { let name_ident = ctx.rust_ident(name); // We don't know if blocklisted items `impl Debug` or not, so we can't // add them to the format string we're building up. if !ctx.allowlisted_items().contains(&self.id()) { return None; } let ty = match self.as_type() { Some(ty) => ty, None => { return None; } }; fn debug_print( name: &str, name_ident: proc_macro2::TokenStream, ) -> Option<(String, Vec)> { Some(( format!("{}: {{:?}}", name), vec![quote! { self.#name_ident }], )) } match *ty.kind() { // Handle the simple cases. TypeKind::Void | TypeKind::NullPtr | TypeKind::Int(..) | TypeKind::Float(..) | TypeKind::Complex(..) | TypeKind::Function(..) | TypeKind::Enum(..) | TypeKind::Reference(..) | TypeKind::UnresolvedTypeRef(..) | TypeKind::ObjCInterface(..) | TypeKind::ObjCId | TypeKind::Comp(..) | TypeKind::ObjCSel => debug_print(name, quote! { #name_ident }), TypeKind::TemplateInstantiation(ref inst) => { if inst.is_opaque(ctx, self) { Some((format!("{}: opaque", name), vec![])) } else { debug_print(name, quote! { #name_ident }) } } // The generic is not required to implement Debug, so we can not debug print that type TypeKind::TypeParam => { Some((format!("{}: Non-debuggable generic", name), vec![])) } TypeKind::Array(_, len) => { // Generics are not required to implement Debug if self.has_type_param_in_array(ctx) { Some(( format!("{}: Array with length {}", name, len), vec![], )) } else if len < RUST_DERIVE_IN_ARRAY_LIMIT || ctx.options().rust_features().larger_arrays { // The simple case debug_print(name, quote! { #name_ident }) } else if ctx.options().use_core { // There is no String in core; reducing field visibility to avoid breaking // no_std setups. Some((format!("{}: [...]", name), vec![])) } else { // Let's implement our own print function Some(( format!("{}: [{{}}]", name), vec![quote! { self.#name_ident .iter() .enumerate() .map(|(i, v)| format!("{}{:?}", if i > 0 { ", " } else { "" }, v)) .collect::() }], )) } } TypeKind::Vector(_, len) => { if ctx.options().use_core { // There is no format! in core; reducing field visibility to avoid breaking // no_std setups. Some((format!("{}(...)", name), vec![])) } else { let self_ids = 0..len; Some(( format!("{}({{}})", name), vec![quote! { #(format!("{:?}", self.#self_ids)),* }], )) } } TypeKind::ResolvedTypeRef(t) | TypeKind::TemplateAlias(t, _) | TypeKind::Alias(t) | TypeKind::BlockPointer(t) => { // We follow the aliases ctx.resolve_item(t).impl_debug(ctx, name) } TypeKind::Pointer(inner) => { let inner_type = ctx.resolve_type(inner).canonical_type(ctx); match *inner_type.kind() { TypeKind::Function(ref sig) if !sig.function_pointers_can_derive() => { Some((format!("{}: FunctionPointer", name), vec![])) } _ => debug_print(name, quote! { #name_ident }), } } TypeKind::Opaque => None, } } }