mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			245 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			245 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
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(crate) 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(crate) 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<proc_macro2::TokenStream>)>;
 | 
						|
}
 | 
						|
 | 
						|
impl<'a> ImplDebug<'a> for FieldData {
 | 
						|
    type Extra = ();
 | 
						|
 | 
						|
    fn impl_debug(
 | 
						|
        &self,
 | 
						|
        ctx: &BindgenContext,
 | 
						|
        _: Self::Extra,
 | 
						|
    ) -> Option<(String, Vec<proc_macro2::TokenStream>)> {
 | 
						|
        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<proc_macro2::TokenStream>)> {
 | 
						|
        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<proc_macro2::TokenStream>)> {
 | 
						|
        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<proc_macro2::TokenStream>)> {
 | 
						|
            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::<String>()
 | 
						|
                        }],
 | 
						|
                    ))
 | 
						|
                }
 | 
						|
            }
 | 
						|
            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,
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |