summaryrefslogtreecommitdiffstats
path: root/third_party/rust/derive_more-impl/src/fmt/debug.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/derive_more-impl/src/fmt/debug.rs')
-rw-r--r--third_party/rust/derive_more-impl/src/fmt/debug.rs373
1 files changed, 373 insertions, 0 deletions
diff --git a/third_party/rust/derive_more-impl/src/fmt/debug.rs b/third_party/rust/derive_more-impl/src/fmt/debug.rs
new file mode 100644
index 0000000000..d1f427e1f6
--- /dev/null
+++ b/third_party/rust/derive_more-impl/src/fmt/debug.rs
@@ -0,0 +1,373 @@
+//! 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)
+ },
+ )
+ }
+}