Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  debug.rs

  Sprache: Rust
 

//! 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)
            },
        )
    }
}

Messung V0.5 in Prozent
C=94 H=96 G=94

¤ Dauer der Verarbeitung: 0.11 Sekunden  (vorverarbeitet am  2026-06-23) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik