diff options
Diffstat (limited to 'third_party/rust/derive_more-impl/src/from.rs')
-rw-r--r-- | third_party/rust/derive_more-impl/src/from.rs | 478 |
1 files changed, 478 insertions, 0 deletions
diff --git a/third_party/rust/derive_more-impl/src/from.rs b/third_party/rust/derive_more-impl/src/from.rs new file mode 100644 index 0000000000..cefab9baac --- /dev/null +++ b/third_party/rust/derive_more-impl/src/from.rs @@ -0,0 +1,478 @@ +//! Implementation of a [`From`] derive macro. + +use std::iter; + +use proc_macro2::{Span, TokenStream}; +use quote::{format_ident, quote, ToTokens as _, TokenStreamExt as _}; +use syn::{ + parse::{discouraged::Speculative as _, Parse, ParseStream}, + parse_quote, + punctuated::Punctuated, + spanned::Spanned as _, + token, Ident, +}; + +use crate::{ + parsing::Type, + utils::{polyfill, Either}, +}; + +/// Expands a [`From`] derive macro. +pub fn expand(input: &syn::DeriveInput, _: &'static str) -> syn::Result<TokenStream> { + match &input.data { + syn::Data::Struct(data) => Expansion { + attrs: StructAttribute::parse_attrs(&input.attrs, &data.fields)? + .map(Into::into) + .as_ref(), + ident: &input.ident, + variant: None, + fields: &data.fields, + generics: &input.generics, + has_explicit_from: false, + } + .expand(), + syn::Data::Enum(data) => { + let mut has_explicit_from = false; + let attrs = data + .variants + .iter() + .map(|variant| { + let attrs = + VariantAttribute::parse_attrs(&variant.attrs, &variant.fields)?; + if matches!( + attrs, + Some( + VariantAttribute::From + | VariantAttribute::Types(_) + | VariantAttribute::Forward + ), + ) { + has_explicit_from = true; + } + Ok(attrs) + }) + .collect::<syn::Result<Vec<_>>>()?; + + data.variants + .iter() + .zip(&attrs) + .map(|(variant, attrs)| { + Expansion { + attrs: attrs.as_ref(), + ident: &input.ident, + variant: Some(&variant.ident), + fields: &variant.fields, + generics: &input.generics, + has_explicit_from, + } + .expand() + }) + .collect() + } + syn::Data::Union(data) => Err(syn::Error::new( + data.union_token.span(), + "`From` cannot be derived for unions", + )), + } +} + +/// Representation of a [`From`] derive macro struct container attribute. +/// +/// ```rust,ignore +/// #[from(<types>)] +/// #[from(forward)] +/// ``` +enum StructAttribute { + /// [`Type`]s to derive [`From`]. + Types(Punctuated<Type, token::Comma>), + + /// Forward [`From`] implementation. + Forward, +} + +impl StructAttribute { + /// Parses a [`StructAttribute`] from the provided [`syn::Attribute`]s. + fn parse_attrs( + attrs: impl AsRef<[syn::Attribute]>, + fields: &syn::Fields, + ) -> syn::Result<Option<Self>> { + Ok(attrs + .as_ref() + .iter() + .filter(|attr| attr.path().is_ident("from")) + .try_fold(None, |attrs, attr| { + let field_attr = attr.parse_args_with(|stream: ParseStream<'_>| { + Self::parse(stream, fields) + })?; + match (attrs, field_attr) { + ( + Some((path, StructAttribute::Types(mut tys))), + StructAttribute::Types(more), + ) => { + tys.extend(more); + Ok(Some((path, StructAttribute::Types(tys)))) + } + (None, field_attr) => Ok(Some((attr.path(), field_attr))), + _ => Err(syn::Error::new( + attr.path().span(), + "only single `#[from(...)]` attribute is allowed here", + )), + } + })? + .map(|(_, attr)| attr)) + } + + /// Parses single [`StructAttribute`]. + fn parse(input: ParseStream<'_>, fields: &syn::Fields) -> syn::Result<Self> { + let ahead = input.fork(); + match ahead.parse::<syn::Path>() { + Ok(p) if p.is_ident("forward") => { + input.advance_to(&ahead); + Ok(Self::Forward) + } + Ok(p) if p.is_ident("types") => legacy_error(&ahead, input.span(), fields), + _ => input + .parse_terminated(Type::parse, token::Comma) + .map(Self::Types), + } + } +} + +/// Representation of a [`From`] derive macro enum variant attribute. +/// +/// ```rust,ignore +/// #[from] +/// #[from(<types>)] +/// #[from(forward)] +/// #[from(skip)] +/// ``` +enum VariantAttribute { + /// Explicitly derive [`From`]. + From, + + /// [`Type`]s to derive [`From`]. + Types(Punctuated<Type, token::Comma>), + + /// Forward [`From`] implementation. + Forward, + + /// Skip variant. + Skip, +} + +impl VariantAttribute { + /// Parses a [`VariantAttribute`] from the provided [`syn::Attribute`]s. + fn parse_attrs( + attrs: impl AsRef<[syn::Attribute]>, + fields: &syn::Fields, + ) -> syn::Result<Option<Self>> { + Ok(attrs + .as_ref() + .iter() + .filter(|attr| attr.path().is_ident("from")) + .try_fold(None, |mut attrs, attr| { + let field_attr = Self::parse_attr(attr, fields)?; + if let Some((path, _)) = attrs.replace((attr.path(), field_attr)) { + Err(syn::Error::new( + path.span(), + "only single `#[from(...)]` attribute is allowed here", + )) + } else { + Ok(attrs) + } + })? + .map(|(_, attr)| attr)) + } + + /// Parses a [`VariantAttribute`] from the single provided [`syn::Attribute`]. + fn parse_attr(attr: &syn::Attribute, fields: &syn::Fields) -> syn::Result<Self> { + if matches!(attr.meta, syn::Meta::Path(_)) { + return Ok(Self::From); + } + + attr.parse_args_with(|input: ParseStream<'_>| { + let ahead = input.fork(); + match ahead.parse::<syn::Path>() { + Ok(p) if p.is_ident("forward") => { + input.advance_to(&ahead); + Ok(Self::Forward) + } + Ok(p) if p.is_ident("skip") || p.is_ident("ignore") => { + input.advance_to(&ahead); + Ok(Self::Skip) + } + Ok(p) if p.is_ident("types") => { + legacy_error(&ahead, input.span(), fields) + } + _ => input + .parse_terminated(Type::parse, token::Comma) + .map(Self::Types), + } + }) + } +} + +impl From<StructAttribute> for VariantAttribute { + fn from(value: StructAttribute) -> Self { + match value { + StructAttribute::Types(tys) => Self::Types(tys), + StructAttribute::Forward => Self::Forward, + } + } +} + +/// Expansion of a macro for generating [`From`] implementation of a struct or +/// enum. +struct Expansion<'a> { + /// [`From`] attributes. + /// + /// As a [`VariantAttribute`] is superset of a [`StructAttribute`], we use + /// it for both derives. + attrs: Option<&'a VariantAttribute>, + + /// Struct or enum [`Ident`]. + ident: &'a Ident, + + /// Variant [`Ident`] in case of enum expansion. + variant: Option<&'a Ident>, + + /// Struct or variant [`syn::Fields`]. + fields: &'a syn::Fields, + + /// Struct or enum [`syn::Generics`]. + generics: &'a syn::Generics, + + /// Indicator whether one of the enum variants has + /// [`VariantAttribute::From`], [`VariantAttribute::Types`] or + /// [`VariantAttribute::Forward`]. + /// + /// Always [`false`] for structs. + has_explicit_from: bool, +} + +impl<'a> Expansion<'a> { + /// Expands [`From`] implementations for a struct or an enum variant. + fn expand(&self) -> syn::Result<TokenStream> { + use crate::utils::FieldsExt as _; + + let ident = self.ident; + let field_tys = self.fields.iter().map(|f| &f.ty).collect::<Vec<_>>(); + let (impl_gens, ty_gens, where_clause) = self.generics.split_for_impl(); + + let skip_variant = self.has_explicit_from + || (self.variant.is_some() && self.fields.is_empty()); + match (self.attrs, skip_variant) { + (Some(VariantAttribute::Types(tys)), _) => { + tys.iter().map(|ty| { + let variant = self.variant.iter(); + + let mut from_tys = self.fields.validate_type(ty)?; + let init = self.expand_fields(|ident, ty, index| { + let ident = ident.into_iter(); + let index = index.into_iter(); + let from_ty = from_tys.next().unwrap_or_else(|| unreachable!()); + quote! { + #( #ident: )* <#ty as ::core::convert::From<#from_ty>>::from( + value #( .#index )* + ), + } + }); + + Ok(quote! { + #[automatically_derived] + impl #impl_gens ::core::convert::From<#ty> + for #ident #ty_gens #where_clause + { + #[inline] + fn from(value: #ty) -> Self { + #ident #( :: #variant )* #init + } + } + }) + }) + .collect() + } + (Some(VariantAttribute::From), _) | (None, false) => { + let variant = self.variant.iter(); + let init = self.expand_fields(|ident, _, index| { + let ident = ident.into_iter(); + let index = index.into_iter(); + quote! { #( #ident: )* value #( . #index )*, } + }); + + Ok(quote! { + #[automatically_derived] + impl #impl_gens ::core::convert::From<(#( #field_tys ),*)> + for #ident #ty_gens #where_clause + { + #[inline] + fn from(value: (#( #field_tys ),*)) -> Self { + #ident #( :: #variant )* #init + } + } + }) + } + (Some(VariantAttribute::Forward), _) => { + let mut i = 0; + let mut gen_idents = Vec::with_capacity(self.fields.len()); + let init = self.expand_fields(|ident, ty, index| { + let ident = ident.into_iter(); + let index = index.into_iter(); + let gen_ident = format_ident!("__FromT{i}"); + let out = quote! { + #( #ident: )* <#ty as ::core::convert::From<#gen_ident>>::from( + value #( .#index )* + ), + }; + gen_idents.push(gen_ident); + i += 1; + out + }); + + let variant = self.variant.iter(); + let generics = { + let mut generics = self.generics.clone(); + for (ty, ident) in field_tys.iter().zip(&gen_idents) { + generics.make_where_clause().predicates.push( + parse_quote! { #ty: ::core::convert::From<#ident> }, + ); + generics + .params + .push(syn::TypeParam::from(ident.clone()).into()); + } + generics + }; + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + Ok(quote! { + #[automatically_derived] + impl #impl_gens ::core::convert::From<(#( #gen_idents ),*)> + for #ident #ty_gens #where_clause + { + #[inline] + fn from(value: (#( #gen_idents ),*)) -> Self { + #ident #(:: #variant)* #init + } + } + }) + } + (Some(VariantAttribute::Skip), _) | (None, true) => { + Ok(TokenStream::new()) + } + } + } + + /// Expands fields initialization wrapped into [`token::Brace`]s in case of + /// [`syn::FieldsNamed`], or [`token::Paren`] in case of + /// [`syn::FieldsUnnamed`]. + fn expand_fields( + &self, + mut wrap: impl FnMut(Option<&Ident>, &syn::Type, Option<syn::Index>) -> TokenStream, + ) -> TokenStream { + let surround = match self.fields { + syn::Fields::Named(_) | syn::Fields::Unnamed(_) => { + Some(|tokens| match self.fields { + syn::Fields::Named(named) => { + let mut out = TokenStream::new(); + named + .brace_token + .surround(&mut out, |out| out.append_all(tokens)); + out + } + syn::Fields::Unnamed(unnamed) => { + let mut out = TokenStream::new(); + unnamed + .paren_token + .surround(&mut out, |out| out.append_all(tokens)); + out + } + syn::Fields::Unit => unreachable!(), + }) + } + syn::Fields::Unit => None, + }; + + surround + .map(|surround| { + surround(if self.fields.len() == 1 { + let field = self + .fields + .iter() + .next() + .unwrap_or_else(|| unreachable!("self.fields.len() == 1")); + wrap(field.ident.as_ref(), &field.ty, None) + } else { + self.fields + .iter() + .enumerate() + .map(|(i, field)| { + wrap(field.ident.as_ref(), &field.ty, Some(i.into())) + }) + .collect() + }) + }) + .unwrap_or_default() + } +} + +/// Constructs a [`syn::Error`] for legacy syntax: `#[from(types(i32, "&str"))]`. +fn legacy_error<T>( + tokens: ParseStream<'_>, + span: Span, + fields: &syn::Fields, +) -> syn::Result<T> { + let content; + syn::parenthesized!(content in tokens); + + let types = content + .parse_terminated(polyfill::NestedMeta::parse, token::Comma)? + .into_iter() + .map(|meta| { + let value = match meta { + polyfill::NestedMeta::Meta(meta) => { + meta.into_token_stream().to_string() + } + polyfill::NestedMeta::Lit(syn::Lit::Str(str)) => str.value(), + polyfill::NestedMeta::Lit(_) => unreachable!(), + }; + if fields.len() > 1 { + format!( + "({})", + fields + .iter() + .map(|_| value.clone()) + .collect::<Vec<_>>() + .join(", "), + ) + } else { + value + } + }) + .chain(match fields.len() { + 0 => Either::Left(iter::empty()), + 1 => Either::Right(iter::once( + fields + .iter() + .next() + .unwrap_or_else(|| unreachable!("fields.len() == 1")) + .ty + .to_token_stream() + .to_string(), + )), + _ => Either::Right(iter::once(format!( + "({})", + fields + .iter() + .map(|f| f.ty.to_token_stream().to_string()) + .collect::<Vec<_>>() + .join(", ") + ))), + }) + .collect::<Vec<_>>() + .join(", "); + + Err(syn::Error::new( + span, + format!("legacy syntax, remove `types` and use `{types}` instead"), + )) +} |