summaryrefslogtreecommitdiffstats
path: root/third_party/rust/derive_more-impl/src/from.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/derive_more-impl/src/from.rs')
-rw-r--r--third_party/rust/derive_more-impl/src/from.rs478
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"),
+ ))
+}