Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/derive_more-impl/src/fmt/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 12 kB image not shown  

Quelle  debug.rs   Sprache: unbekannt

 
//! Implementation of a [`fmt::Debug`] derive macro.
//!
//! [`fmt::Debug`]: std::fmt::Debug

use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{
    parse::{Error, Parse, ParseStream, Result},
    parse_quote,
    spanned::Spanned as _,
    Ident,
};

use super::{BoundsAttribute, FmtAttribute};

/// Expands a [`fmt::Debug`] derive macro.
///
/// [`fmt::Debug`]: std::fmt::Debug
pub fn expand(input: &syn::DeriveInput, _: &str) -> Result<TokenStream> {
    let attrs = ContainerAttributes::parse_attrs(&input.attrs)?;
    let ident = &input.ident;

    let (bounds, body) = match &input.data {
        syn::Data::Struct(s) => expand_struct(attrs, ident, s),
        syn::Data::Enum(e) => expand_enum(attrs, e),
        syn::Data::Union(_) => {
            return Err(Error::new(
                input.span(),
                "`Debug` cannot be derived for unions",
            ));
        }
    }?;

    let (impl_gens, ty_gens, where_clause) = {
        let (impl_gens, ty_gens, where_clause) = input.generics.split_for_impl();
        let mut where_clause = where_clause
            .cloned()
            .unwrap_or_else(|| parse_quote! { where });
        where_clause.predicates.extend(bounds);
        (impl_gens, ty_gens, where_clause)
    };

    Ok(quote! {
        #[automatically_derived]
        impl #impl_gens ::core::fmt::Debug for #ident #ty_gens
             #where_clause
        {
            fn fmt(
                &self, __derive_more_f: &mut ::core::fmt::Formatter<'_>
            ) -> ::core::fmt::Result {
                #body
            }
        }
    })
}

/// Expands a [`fmt::Debug`] derive macro for the provided struct.
///
/// [`fmt::Debug`]: std::fmt::Debug
fn expand_struct(
    attrs: ContainerAttributes,
    ident: &Ident,
    s: &syn::DataStruct,
) -> Result<(Vec<syn::WherePredicate>, TokenStream)> {
    let s = Expansion {
        attr: &attrs,
        fields: &s.fields,
        ident,
    };
    let bounds = s.generate_bounds()?;
    let body = s.generate_body()?;

    let vars = s.fields.iter().enumerate().map(|(i, f)| {
        let var = f.ident.clone().unwrap_or_else(|| format_ident!("_{i}"));
        let member = f
            .ident
            .clone()
            .map_or_else(|| syn::Member::Unnamed(i.into()), syn::Member::Named);
        quote! { let #var = &self.#member; }
    });

    let body = quote! {
        #( #vars )*
        #body
    };

    Ok((bounds, body))
}

/// Expands a [`fmt::Debug`] derive macro for the provided enum.
///
/// [`fmt::Debug`]: std::fmt::Debug
fn expand_enum(
    attrs: ContainerAttributes,
    e: &syn::DataEnum,
) -> Result<(Vec<syn::WherePredicate>, TokenStream)> {
    let (bounds, match_arms) = e.variants.iter().try_fold(
        (Vec::new(), TokenStream::new()),
        |(mut bounds, mut arms), variant| {
            let ident = &variant.ident;

            let v = Expansion {
                attr: &attrs,
                fields: &variant.fields,
                ident,
            };
            let arm_body = v.generate_body()?;
            bounds.extend(v.generate_bounds()?);

            let fields_idents =
                variant.fields.iter().enumerate().map(|(i, f)| {
                    f.ident.clone().unwrap_or_else(|| format_ident!("_{i}"))
                });
            let matcher = match variant.fields {
                syn::Fields::Named(_) => {
                    quote! { Self::#ident { #( #fields_idents ),* } }
                }
                syn::Fields::Unnamed(_) => {
                    quote! { Self::#ident ( #( #fields_idents ),* ) }
                }
                syn::Fields::Unit => quote! { Self::#ident },
            };

            arms.extend([quote! { #matcher => { #arm_body }, }]);

            Ok::<_, Error>((bounds, arms))
        },
    )?;

    let body = match_arms
        .is_empty()
        .then(|| quote! { match *self {} })
        .unwrap_or_else(|| quote! { match self { #match_arms } });

    Ok((bounds, body))
}

/// Representation of a [`fmt::Debug`] derive macro container attribute.
///
/// ```rust,ignore
/// #[debug(bound(<bounds>))]
/// ```
///
/// [`fmt::Debug`]: std::fmt::Debug
#[derive(Debug, Default)]
struct ContainerAttributes {
    /// Additional trait bounds.
    bounds: BoundsAttribute,
}

impl ContainerAttributes {
    /// Parses [`ContainerAttributes`] from the provided [`syn::Attribute`]s.
    fn parse_attrs(attrs: impl AsRef<[syn::Attribute]>) -> Result<Self> {
        attrs
            .as_ref()
            .iter()
            .filter(|attr| attr.path().is_ident("debug"))
            .try_fold(ContainerAttributes::default(), |mut attrs, attr| {
                let attr = attr.parse_args::<ContainerAttributes>()?;
                attrs.bounds.0.extend(attr.bounds.0);
                Ok(attrs)
            })
    }
}

impl Parse for ContainerAttributes {
    fn parse(input: ParseStream) -> Result<Self> {
        BoundsAttribute::check_legacy_fmt(input)?;

        input.parse().map(|bounds| ContainerAttributes { bounds })
    }
}

/// Representation of a [`fmt::Debug`] derive macro field attribute.
///
/// ```rust,ignore
/// #[debug("<fmt_literal>", <fmt_args>)]
/// #[debug(skip)]
/// ```
///
/// [`fmt::Debug`]: std::fmt::Debug
enum FieldAttribute {
    /// [`fmt`] attribute.
    ///
    /// [`fmt`]: std::fmt
    Fmt(FmtAttribute),

    /// Attribute for skipping field.
    Skip,
}

impl FieldAttribute {
    /// Parses [`ContainerAttributes`] from the provided [`syn::Attribute`]s.
    fn parse_attrs(attrs: impl AsRef<[syn::Attribute]>) -> Result<Option<Self>> {
        Ok(attrs
            .as_ref()
            .iter()
            .filter(|attr| attr.path().is_ident("debug"))
            .try_fold(None, |mut attrs, attr| {
                let field_attr = attr.parse_args::<FieldAttribute>()?;
                if let Some((path, _)) = attrs.replace((attr.path(), field_attr)) {
                    Err(Error::new(
                        path.span(),
                        "only single `#[debug(...)]` attribute is allowed here",
                    ))
                } else {
                    Ok(attrs)
                }
            })?
            .map(|(_, attr)| attr))
    }
}

impl Parse for FieldAttribute {
    fn parse(input: ParseStream) -> Result<Self> {
        FmtAttribute::check_legacy_fmt(input)?;

        if input.peek(syn::LitStr) {
            input.parse().map(Self::Fmt)
        } else {
            let _ = input.parse::<syn::Path>().and_then(|p| {
                if ["skip", "ignore"].into_iter().any(|i| p.is_ident(i)) {
                    Ok(p)
                } else {
                    Err(Error::new(
                        p.span(),
                        "unknown attribute, expected `skip` or `ignore`",
                    ))
                }
            })?;
            Ok(Self::Skip)
        }
    }
}

/// Helper struct to generate [`Debug::fmt()`] implementation body and trait
/// bounds for a struct or an enum variant.
///
/// [`Debug::fmt()`]: std::fmt::Debug::fmt()
#[derive(Debug)]
struct Expansion<'a> {
    attr: &'a ContainerAttributes,

    /// Struct or enum [`Ident`](struct@Ident).
    ident: &'a Ident,

    /// Struct or enum [`syn::Fields`].
    fields: &'a syn::Fields,
}

impl<'a> Expansion<'a> {
    /// Generates [`Debug::fmt()`] implementation for a struct or an enum variant.
    ///
    /// [`Debug::fmt()`]: std::fmt::Debug::fmt()
    fn generate_body(&self) -> Result<TokenStream> {
        match self.fields {
            syn::Fields::Unit => {
                let ident = self.ident.to_string();
                Ok(quote! {
                    ::core::fmt::Formatter::write_str(
                        __derive_more_f,
                        #ident,
                    )
                })
            }
            syn::Fields::Unnamed(unnamed) => {
                let mut exhaustive = true;
                let ident_str = self.ident.to_string();

                let out = quote! {
                    &mut ::derive_more::__private::debug_tuple(
                        __derive_more_f,
                        #ident_str,
                    )
                };
                let out = unnamed.unnamed.iter().enumerate().try_fold(
                    out,
                    |out, (i, field)| match FieldAttribute::parse_attrs(&field.attrs)? {
                        Some(FieldAttribute::Skip) => {
                            exhaustive = false;
                            Ok::<_, Error>(out)
                        }
                        Some(FieldAttribute::Fmt(fmt)) => Ok(quote! {
                            ::derive_more::__private::DebugTuple::field(
                                #out,
                                &::core::format_args!(#fmt),
                            )
                        }),
                        None => {
                            let ident = format_ident!("_{i}");
                            Ok(quote! {
                                ::derive_more::__private::DebugTuple::field(#out, #ident)
                            })
                        }
                    },
                )?;
                Ok(if exhaustive {
                    quote! { ::derive_more::__private::DebugTuple::finish(#out) }
                } else {
                    quote! { ::derive_more::__private::DebugTuple::finish_non_exhaustive(#out) }
                })
            }
            syn::Fields::Named(named) => {
                let mut exhaustive = true;
                let ident = self.ident.to_string();

                let out = quote! {
                    &mut ::core::fmt::Formatter::debug_struct(
                        __derive_more_f,
                        #ident,
                    )
                };
                let out = named.named.iter().try_fold(out, |out, field| {
                        let field_ident = field.ident.as_ref().unwrap_or_else(|| {
                            unreachable!("`syn::Fields::Named`");
                        });
                        let field_str = field_ident.to_string();
                        match FieldAttribute::parse_attrs(&field.attrs)? {
                            Some(FieldAttribute::Skip) => {
                                exhaustive = false;
                                Ok::<_, Error>(out)
                            }
                            Some(FieldAttribute::Fmt(fmt)) => Ok(quote! {
                                ::core::fmt::DebugStruct::field(
                                    #out,
                                    #field_str,
                                    &::core::format_args!(#fmt),
                                )
                            }),
                            None => Ok(quote! {
                                ::core::fmt::DebugStruct::field(#out, #field_str, #field_ident)
                            }),
                        }
                    })?;
                Ok(if exhaustive {
                    quote! { ::core::fmt::DebugStruct::finish(#out) }
                } else {
                    quote! { ::core::fmt::DebugStruct::finish_non_exhaustive(#out) }
                })
            }
        }
    }

    /// Generates trait bounds for a struct or an enum variant.
    fn generate_bounds(&self) -> Result<Vec<syn::WherePredicate>> {
        self.fields.iter().try_fold(
            self.attr.bounds.0.clone().into_iter().collect::<Vec<_>>(),
            |mut out, field| {
                let fmt_attr =
                    FieldAttribute::parse_attrs(&field.attrs)?.and_then(|attr| {
                        match attr {
                            FieldAttribute::Fmt(fmt) => Some(fmt),
                            FieldAttribute::Skip => None,
                        }
                    });
                let ty = &field.ty;

                if let Some(attr) = fmt_attr {
                    out.extend(attr.bounded_types(self.fields).map(
                        |(ty, trait_name)| {
                            let trait_name = format_ident!("{trait_name}");
                            parse_quote! { #ty: ::core::fmt::#trait_name }
                        },
                    ));
                } else {
                    out.extend([parse_quote! { #ty: ::core::fmt::Debug }]);
                }

                Ok(out)
            },
        )
    }
}

[ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet)  ]