#![cfg_attr( not(all(feature = "add", feature = "mul")), allow(dead_code, unused_mut) )] use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; use syn::{ parse_quote, punctuated::Punctuated, spanned::Spanned, Attribute, Data, DeriveInput, Error, Field, Fields, FieldsNamed, FieldsUnnamed, GenericParam, Generics, Ident, ImplGenerics, Index, Result, Token, Type, TypeGenerics, TypeParamBound, Variant, WhereClause, }; #[cfg(any( feature = "as_ref", feature = "debug", feature = "display", feature = "from", feature = "into", feature = "try_from", ))] pub(crate) use self::either::Either; #[cfg(any(feature = "from", feature = "into"))] pub(crate) use self::fields_ext::FieldsExt; #[cfg(feature = "as_ref")] pub(crate) use self::generics_search::GenericsSearch; #[cfg(any( feature = "as_ref", feature = "debug", feature = "display", feature = "from", feature = "into", feature = "try_from", ))] pub(crate) use self::spanning::Spanning; #[derive(Clone, Copy, Default)] pub struct DeterministicState; impl std::hash::BuildHasher for DeterministicState { type Hasher = std::collections::hash_map::DefaultHasher; fn build_hasher(&self) -> Self::Hasher { Self::Hasher::default() } } pub type HashMap = std::collections::HashMap; pub type HashSet = std::collections::HashSet; #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum RefType { No, Ref, Mut, } impl RefType { pub fn lifetime(self) -> TokenStream { match self { RefType::No => quote! {}, _ => quote! { '__deriveMoreLifetime }, } } pub fn reference(self) -> TokenStream { match self { RefType::No => quote! {}, RefType::Ref => quote! { & }, RefType::Mut => quote! { &mut }, } } pub fn mutability(self) -> TokenStream { match self { RefType::Mut => quote! { mut }, _ => quote! {}, } } pub fn pattern_ref(self) -> TokenStream { match self { RefType::Ref => quote! { ref }, RefType::Mut => quote! { ref mut }, RefType::No => quote! {}, } } pub fn reference_with_lifetime(self) -> TokenStream { if !self.is_ref() { return quote! {}; } let lifetime = self.lifetime(); let mutability = self.mutability(); quote! { &#lifetime #mutability } } pub fn is_ref(self) -> bool { !matches!(self, RefType::No) } pub fn from_attr_name(name: &str) -> Self { match name { "owned" => RefType::No, "ref" => RefType::Ref, "ref_mut" => RefType::Mut, _ => panic!("`{name}` is not a `RefType`"), } } } pub fn numbered_vars(count: usize, prefix: &str) -> Vec { (0..count).map(|i| format_ident!("__{prefix}{i}")).collect() } pub fn field_idents<'a>(fields: &'a [&'a Field]) -> Vec<&'a Ident> { fields .iter() .map(|f| { f.ident .as_ref() .expect("Tried to get field names of a tuple struct") }) .collect() } pub fn get_field_types_iter<'a>( fields: &'a [&'a Field], ) -> Box + 'a> { Box::new(fields.iter().map(|f| &f.ty)) } pub fn get_field_types<'a>(fields: &'a [&'a Field]) -> Vec<&'a Type> { get_field_types_iter(fields).collect() } pub fn add_extra_type_param_bound_op_output<'a>( generics: &'a Generics, trait_ident: &'a Ident, ) -> Generics { let mut generics = generics.clone(); for type_param in &mut generics.type_params_mut() { let type_ident = &type_param.ident; let bound: TypeParamBound = parse_quote! { derive_more::core::ops::#trait_ident }; type_param.bounds.push(bound) } generics } pub fn add_extra_ty_param_bound_op<'a>( generics: &'a Generics, trait_ident: &'a Ident, ) -> Generics { add_extra_ty_param_bound(generics, "e! { derive_more::core::ops::#trait_ident }) } pub fn add_extra_ty_param_bound<'a>( generics: &'a Generics, bound: &'a TokenStream, ) -> Generics { let mut generics = generics.clone(); let bound: TypeParamBound = parse_quote! { #bound }; for type_param in &mut generics.type_params_mut() { type_param.bounds.push(bound.clone()) } generics } pub fn add_extra_generic_param( generics: &Generics, generic_param: TokenStream, ) -> Generics { let generic_param: GenericParam = parse_quote! { #generic_param }; let mut generics = generics.clone(); generics.params.push(generic_param); generics } pub fn add_extra_generic_type_param( generics: &Generics, generic_param: TokenStream, ) -> Generics { let generic_param: GenericParam = parse_quote! { #generic_param }; let lifetimes: Vec = generics.lifetimes().map(|x| x.clone().into()).collect(); let type_params: Vec = generics.type_params().map(|x| x.clone().into()).collect(); let const_params: Vec = generics.const_params().map(|x| x.clone().into()).collect(); let mut generics = generics.clone(); generics.params = Default::default(); generics.params.extend(lifetimes); generics.params.extend(type_params); generics.params.push(generic_param); generics.params.extend(const_params); generics } pub fn add_extra_where_clauses( generics: &Generics, type_where_clauses: TokenStream, ) -> Generics { let mut type_where_clauses: WhereClause = parse_quote! { #type_where_clauses }; let mut new_generics = generics.clone(); if let Some(old_where) = new_generics.where_clause { type_where_clauses.predicates.extend(old_where.predicates) } new_generics.where_clause = Some(type_where_clauses); new_generics } pub fn add_where_clauses_for_new_ident<'a>( generics: &'a Generics, fields: &[&'a Field], type_ident: &Ident, type_where_clauses: TokenStream, sized: bool, ) -> Generics { let generic_param = if fields.len() > 1 { quote! { #type_ident: derive_more::core::marker::Copy } } else if sized { quote! { #type_ident } } else { quote! { #type_ident: ?derive_more::core::marker::Sized } }; let generics = add_extra_where_clauses(generics, type_where_clauses); add_extra_generic_type_param(&generics, generic_param) } pub fn unnamed_to_vec(fields: &FieldsUnnamed) -> Vec<&Field> { fields.unnamed.iter().collect() } pub fn named_to_vec(fields: &FieldsNamed) -> Vec<&Field> { fields.named.iter().collect() } fn panic_one_field(trait_name: &str, trait_attr: &str) -> ! { panic!( "derive({trait_name}) only works when forwarding to a single field. \ Try putting #[{trait_attr}] or #[{trait_attr}(ignore)] on the fields in the struct", ) } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum DeriveType { Unnamed, Named, Enum, } pub struct State<'input> { pub input: &'input DeriveInput, pub trait_name: &'static str, pub method_ident: Ident, pub trait_path: TokenStream, pub trait_path_params: Vec, pub trait_attr: String, pub derive_type: DeriveType, pub fields: Vec<&'input Field>, pub variants: Vec<&'input Variant>, pub variant_states: Vec>, pub variant: Option<&'input Variant>, pub generics: Generics, pub default_info: FullMetaInfo, full_meta_infos: Vec, } #[derive(Default, Clone)] pub struct AttrParams { pub enum_: Vec<&'static str>, pub variant: Vec<&'static str>, pub struct_: Vec<&'static str>, pub field: Vec<&'static str>, } impl AttrParams { pub fn new(params: Vec<&'static str>) -> AttrParams { AttrParams { enum_: params.clone(), struct_: params.clone(), variant: params.clone(), field: params, } } pub fn struct_(params: Vec<&'static str>) -> AttrParams { AttrParams { enum_: vec![], struct_: params, variant: vec![], field: vec![], } } } impl<'input> State<'input> { pub fn new<'arg_input>( input: &'arg_input DeriveInput, trait_name: &'static str, trait_attr: String, ) -> Result> { State::new_impl(input, trait_name, trait_attr, AttrParams::default(), true) } pub fn with_field_ignore<'arg_input>( input: &'arg_input DeriveInput, trait_name: &'static str, trait_attr: String, ) -> Result> { State::new_impl( input, trait_name, trait_attr, AttrParams::new(vec!["ignore"]), true, ) } pub fn with_field_ignore_and_forward<'arg_input>( input: &'arg_input DeriveInput, trait_name: &'static str, trait_attr: String, ) -> Result> { State::new_impl( input, trait_name, trait_attr, AttrParams::new(vec!["ignore", "forward"]), true, ) } pub fn with_field_ignore_and_refs<'arg_input>( input: &'arg_input DeriveInput, trait_name: &'static str, trait_attr: String, ) -> Result> { State::new_impl( input, trait_name, trait_attr, AttrParams::new(vec!["ignore", "owned", "ref", "ref_mut"]), true, ) } pub fn with_attr_params<'arg_input>( input: &'arg_input DeriveInput, trait_name: &'static str, trait_attr: String, allowed_attr_params: AttrParams, ) -> Result> { State::new_impl(input, trait_name, trait_attr, allowed_attr_params, true) } fn new_impl<'arg_input>( input: &'arg_input DeriveInput, trait_name: &'static str, trait_attr: String, allowed_attr_params: AttrParams, add_type_bound: bool, ) -> Result> { let trait_name = trait_name.trim_end_matches("ToInner"); let trait_ident = format_ident!("{trait_name}"); let method_ident = format_ident!("{trait_attr}"); let trait_path = quote! { derive_more::#trait_ident }; let (derive_type, fields, variants): (_, Vec<_>, Vec<_>) = match input.data { Data::Struct(ref data_struct) => match data_struct.fields { Fields::Unnamed(ref fields) => { (DeriveType::Unnamed, unnamed_to_vec(fields), vec![]) } Fields::Named(ref fields) => { (DeriveType::Named, named_to_vec(fields), vec![]) } Fields::Unit => (DeriveType::Named, vec![], vec![]), }, Data::Enum(ref data_enum) => ( DeriveType::Enum, vec![], data_enum.variants.iter().collect(), ), Data::Union(_) => { panic!("cannot derive({trait_name}) for union") } }; let attrs: Vec<_> = if derive_type == DeriveType::Enum { variants.iter().map(|v| &v.attrs).collect() } else { fields.iter().map(|f| &f.attrs).collect() }; let (allowed_attr_params_outer, allowed_attr_params_inner) = if derive_type == DeriveType::Enum { (&allowed_attr_params.enum_, &allowed_attr_params.variant) } else { (&allowed_attr_params.struct_, &allowed_attr_params.field) }; let struct_meta_info = get_meta_info(&trait_attr, &input.attrs, allowed_attr_params_outer)?; let meta_infos: Result> = attrs .iter() .map(|attrs| get_meta_info(&trait_attr, attrs, allowed_attr_params_inner)) .collect(); let meta_infos = meta_infos?; let first_match = meta_infos .iter() .find_map(|info| info.enabled.map(|_| info)); // Default to enabled true, except when first attribute has explicit // enabling. // // Except for derive Error. // // The way `else` case works is that if any field have any valid // attribute specified, then all fields without any attributes // specified are filtered out from `State::enabled_fields`. // // However, derive Error *infers* fields and there are cases when // one of the fields may have an attribute specified, but another field // would be inferred. So, for derive Error macro we default enabled // to true unconditionally (i.e., even if some fields have attributes // specified). let default_enabled = if trait_name == "Error" { true } else { first_match.map_or(true, |info| !info.enabled.unwrap()) }; let defaults = struct_meta_info.into_full(FullMetaInfo { enabled: default_enabled, forward: false, // Default to owned true, except when first attribute has one of owned, // ref or ref_mut // - not a single attribute means default true // - an attribute, but non of owned, ref or ref_mut means default true // - an attribute, and owned, ref or ref_mut means default false owned: first_match.map_or(true, |info| { info.owned.is_none() && info.ref_.is_none() || info.ref_mut.is_none() }), ref_: false, ref_mut: false, info: MetaInfo::default(), }); let full_meta_infos: Vec<_> = meta_infos .into_iter() .map(|info| info.into_full(defaults.clone())) .collect(); let variant_states: Result> = if derive_type == DeriveType::Enum { variants .iter() .zip(full_meta_infos.iter().cloned()) .map(|(variant, info)| { State::from_variant( input, trait_name, trait_attr.clone(), allowed_attr_params.clone(), variant, info, ) }) .collect() } else { Ok(vec![]) }; let generics = if add_type_bound { add_extra_ty_param_bound(&input.generics, &trait_path) } else { input.generics.clone() }; Ok(State { input, trait_name, method_ident, trait_path, trait_path_params: vec![], trait_attr, // input, fields, variants, variant_states: variant_states?, variant: None, derive_type, generics, full_meta_infos, default_info: defaults, }) } pub fn from_variant<'arg_input>( input: &'arg_input DeriveInput, trait_name: &'static str, trait_attr: String, allowed_attr_params: AttrParams, variant: &'arg_input Variant, default_info: FullMetaInfo, ) -> Result> { let trait_name = trait_name.trim_end_matches("ToInner"); let trait_ident = format_ident!("{trait_name}"); let method_ident = format_ident!("{trait_attr}"); let trait_path = quote! { derive_more::#trait_ident }; let (derive_type, fields): (_, Vec<_>) = match variant.fields { Fields::Unnamed(ref fields) => { (DeriveType::Unnamed, unnamed_to_vec(fields)) } Fields::Named(ref fields) => (DeriveType::Named, named_to_vec(fields)), Fields::Unit => (DeriveType::Named, vec![]), }; let meta_infos: Result> = fields .iter() .map(|f| &f.attrs) .map(|attrs| get_meta_info(&trait_attr, attrs, &allowed_attr_params.field)) .collect(); let meta_infos = meta_infos?; let full_meta_infos: Vec<_> = meta_infos .into_iter() .map(|info| info.into_full(default_info.clone())) .collect(); let generics = add_extra_ty_param_bound(&input.generics, &trait_path); Ok(State { input, trait_name, trait_path, trait_path_params: vec![], trait_attr, method_ident, // input, fields, variants: vec![], variant_states: vec![], variant: Some(variant), derive_type, generics, full_meta_infos, default_info, }) } pub fn add_trait_path_type_param(&mut self, param: TokenStream) { self.trait_path_params.push(param); } pub fn assert_single_enabled_field<'state>( &'state self, ) -> SingleFieldData<'input, 'state> { if self.derive_type == DeriveType::Enum { panic_one_field(self.trait_name, &self.trait_attr); } let data = self.enabled_fields_data(); if data.fields.len() != 1 { panic_one_field(self.trait_name, &self.trait_attr); }; SingleFieldData { input_type: data.input_type, field: data.fields[0], field_type: data.field_types[0], member: data.members[0].clone(), info: data.infos[0].clone(), trait_path: data.trait_path, trait_path_with_params: data.trait_path_with_params.clone(), casted_trait: data.casted_traits[0].clone(), impl_generics: data.impl_generics.clone(), ty_generics: data.ty_generics.clone(), where_clause: data.where_clause, multi_field_data: data, } } pub fn enabled_fields_data<'state>(&'state self) -> MultiFieldData<'input, 'state> { if self.derive_type == DeriveType::Enum { panic!("cannot derive({}) for enum", self.trait_name) } let fields = self.enabled_fields(); let field_idents = self.enabled_fields_idents(); let field_indexes = self.enabled_fields_indexes(); let field_types: Vec<_> = fields.iter().map(|f| &f.ty).collect(); let members: Vec<_> = field_idents .iter() .map(|ident| quote! { self.#ident }) .collect(); let trait_path = &self.trait_path; let trait_path_with_params = if !self.trait_path_params.is_empty() { let params = self.trait_path_params.iter(); quote! { #trait_path<#(#params),*> } } else { self.trait_path.clone() }; let casted_traits: Vec<_> = field_types .iter() .map(|field_type| quote! { <#field_type as #trait_path_with_params> }) .collect(); let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); let input_type = &self.input.ident; let (variant_name, variant_type) = self.variant.map_or_else( || (None, quote! { #input_type }), |v| { let variant_name = &v.ident; (Some(variant_name), quote! { #input_type::#variant_name }) }, ); MultiFieldData { input_type, variant_type, variant_name, variant_info: self.default_info.clone(), fields, field_types, field_indexes, members, infos: self.enabled_infos(), field_idents, method_ident: &self.method_ident, trait_path, trait_path_with_params, casted_traits, impl_generics, ty_generics, where_clause, state: self, } } pub fn enabled_variant_data<'state>( &'state self, ) -> MultiVariantData<'input, 'state> { if self.derive_type != DeriveType::Enum { panic!("can only derive({}) for enum", self.trait_name) } let variants = self.enabled_variants(); MultiVariantData { variants, variant_states: self.enabled_variant_states(), infos: self.enabled_infos(), } } fn enabled_variants(&self) -> Vec<&'input Variant> { self.variants .iter() .zip(self.full_meta_infos.iter().map(|info| info.enabled)) .filter(|(_, ig)| *ig) .map(|(v, _)| *v) .collect() } fn enabled_variant_states(&self) -> Vec<&State<'input>> { self.variant_states .iter() .zip(self.full_meta_infos.iter().map(|info| info.enabled)) .filter(|(_, ig)| *ig) .map(|(v, _)| v) .collect() } pub fn enabled_fields(&self) -> Vec<&'input Field> { self.fields .iter() .zip(self.full_meta_infos.iter().map(|info| info.enabled)) .filter(|(_, ig)| *ig) .map(|(f, _)| *f) .collect() } fn field_idents(&self) -> Vec { if self.derive_type == DeriveType::Named { self.fields .iter() .map(|f| { f.ident .as_ref() .expect("Tried to get field names of a tuple struct") .to_token_stream() }) .collect() } else { let count = self.fields.len(); (0..count) .map(|i| Index::from(i).to_token_stream()) .collect() } } fn enabled_fields_idents(&self) -> Vec { self.field_idents() .into_iter() .zip(self.full_meta_infos.iter().map(|info| info.enabled)) .filter(|(_, ig)| *ig) .map(|(f, _)| f) .collect() } fn enabled_fields_indexes(&self) -> Vec { self.full_meta_infos .iter() .map(|info| info.enabled) .enumerate() .filter(|(_, ig)| *ig) .map(|(i, _)| i) .collect() } fn enabled_infos(&self) -> Vec { self.full_meta_infos .iter() .filter(|info| info.enabled) .cloned() .collect() } } #[derive(Clone)] pub struct SingleFieldData<'input, 'state> { pub input_type: &'input Ident, pub field: &'input Field, pub field_type: &'input Type, pub member: TokenStream, pub info: FullMetaInfo, pub trait_path: &'state TokenStream, pub trait_path_with_params: TokenStream, pub casted_trait: TokenStream, pub impl_generics: ImplGenerics<'state>, pub ty_generics: TypeGenerics<'state>, pub where_clause: Option<&'state WhereClause>, multi_field_data: MultiFieldData<'input, 'state>, } #[derive(Clone)] pub struct MultiFieldData<'input, 'state> { pub input_type: &'input Ident, pub variant_type: TokenStream, pub variant_name: Option<&'input Ident>, pub variant_info: FullMetaInfo, pub fields: Vec<&'input Field>, pub field_types: Vec<&'input Type>, pub field_idents: Vec, pub field_indexes: Vec, pub members: Vec, pub infos: Vec, pub method_ident: &'state Ident, pub trait_path: &'state TokenStream, pub trait_path_with_params: TokenStream, pub casted_traits: Vec, pub impl_generics: ImplGenerics<'state>, pub ty_generics: TypeGenerics<'state>, pub where_clause: Option<&'state WhereClause>, pub state: &'state State<'input>, } pub struct MultiVariantData<'input, 'state> { pub variants: Vec<&'input Variant>, pub variant_states: Vec<&'state State<'input>>, pub infos: Vec, } impl<'input, 'state> MultiFieldData<'input, 'state> { pub fn initializer(&self, initializers: &[T]) -> TokenStream { let MultiFieldData { variant_type, field_idents, .. } = self; if self.state.derive_type == DeriveType::Named { quote! { #variant_type{#(#field_idents: #initializers),*} } } else { quote! { #variant_type(#(#initializers),*) } } } pub fn matcher( &self, indexes: &[usize], bindings: &[T], ) -> TokenStream { let MultiFieldData { variant_type, .. } = self; let full_bindings = (0..self.state.fields.len()).map(|i| { indexes.iter().position(|index| i == *index).map_or_else( || quote! { _ }, |found_index| bindings[found_index].to_token_stream(), ) }); if self.state.derive_type == DeriveType::Named { let field_idents = self.state.field_idents(); quote! { #variant_type{#(#field_idents: #full_bindings),*} } } else { quote! { #variant_type(#(#full_bindings),*) } } } } impl<'input, 'state> SingleFieldData<'input, 'state> { pub fn initializer(&self, initializers: &[T]) -> TokenStream { self.multi_field_data.initializer(initializers) } } fn get_meta_info( trait_attr: &str, attrs: &[Attribute], allowed_attr_params: &[&str], ) -> Result { let mut it = attrs.iter().filter(|a| { a.meta .path() .segments .first() .map(|p| p.ident == trait_attr) .unwrap_or_default() }); let mut info = MetaInfo::default(); let Some(attr) = it.next() else { return Ok(info); }; if allowed_attr_params.is_empty() { return Err(Error::new(attr.span(), "Attribute is not allowed here")); } info.enabled = Some(true); if let Some(another_attr) = it.next() { return Err(Error::new( another_attr.span(), "Only a single attribute is allowed", )); } let list = match &attr.meta { syn::Meta::Path(_) => { if allowed_attr_params.contains(&"ignore") { return Ok(info); } else { return Err(Error::new( attr.span(), format!( "Empty attribute is not allowed, add one of the following parameters: {}", allowed_attr_params.join(", "), ), )); } } syn::Meta::List(list) => list, syn::Meta::NameValue(val) => { return Err(Error::new( val.span(), "Attribute doesn't support name-value format here", )); } }; parse_punctuated_nested_meta( &mut info, &list.parse_args_with(Punctuated::parse_terminated)?, allowed_attr_params, None, )?; Ok(info) } fn parse_punctuated_nested_meta( info: &mut MetaInfo, meta: &Punctuated, allowed_attr_params: &[&str], wrapper_name: Option<&str>, ) -> Result<()> { for meta in meta.iter() { match meta { polyfill::Meta::List(list) if list.path.is_ident("not") => { if wrapper_name.is_some() { // Only single top-level `not` attribute is allowed. return Err(Error::new( list.span(), "Attribute doesn't support multiple multiple or nested `not` parameters", )); } parse_punctuated_nested_meta( info, &list.parse_args_with(Punctuated::parse_terminated)?, allowed_attr_params, Some("not"), )?; } polyfill::Meta::List(list) => { let path = &list.path; if !allowed_attr_params.iter().any(|param| path.is_ident(param)) { return Err(Error::new( meta.span(), format!( "Attribute nested parameter not supported. \ Supported attribute parameters are: {}", allowed_attr_params.join(", "), ), )); } let mut parse_nested = true; let attr_name = path.get_ident().unwrap().to_string(); match (wrapper_name, attr_name.as_str()) { (None, "owned") => info.owned = Some(true), (None, "ref") => info.ref_ = Some(true), (None, "ref_mut") => info.ref_mut = Some(true), #[cfg(any(feature = "from", feature = "into"))] (None, "types") | (Some("owned"), "types") | (Some("ref"), "types") | (Some("ref_mut"), "types") => { parse_nested = false; for meta in &list.parse_args_with( Punctuated::::parse_terminated, )? { let typ: syn::Type = match meta { polyfill::NestedMeta::Meta(meta) => { let polyfill::Meta::Path(path) = meta else { return Err(Error::new( meta.span(), format!( "Attribute doesn't support type {}", quote! { #meta }, ), )); }; syn::TypePath { qself: None, path: path.clone().into(), } .into() } polyfill::NestedMeta::Lit(syn::Lit::Str(s)) => s.parse()?, polyfill::NestedMeta::Lit(lit) => return Err(Error::new( lit.span(), "Attribute doesn't support nested literals here", )), }; for ref_type in wrapper_name .map(|n| vec![RefType::from_attr_name(n)]) .unwrap_or_else(|| { vec![RefType::No, RefType::Ref, RefType::Mut] }) { if info .types .entry(ref_type) .or_default() .replace(typ.clone()) .is_some() { return Err(Error::new( typ.span(), format!( "Duplicate type `{}` specified", quote! { #path }, ), )); } } } } _ => { return Err(Error::new( list.span(), format!( "Attribute doesn't support nested parameter `{}` here", quote! { #path }, ), )) } }; if parse_nested { parse_punctuated_nested_meta( info, &list.parse_args_with(Punctuated::parse_terminated)?, allowed_attr_params, Some(&attr_name), )?; } } polyfill::Meta::Path(path) => { if !allowed_attr_params.iter().any(|param| path.is_ident(param)) { return Err(Error::new( meta.span(), format!( "Attribute parameter not supported. \ Supported attribute parameters are: {}", allowed_attr_params.join(", "), ), )); } let attr_name = path.get_ident().unwrap().to_string(); match (wrapper_name, attr_name.as_str()) { (None, "ignore") => info.enabled = Some(false), (None, "forward") => info.forward = Some(true), (Some("not"), "forward") => info.forward = Some(false), (None, "owned") => info.owned = Some(true), (None, "ref") => info.ref_ = Some(true), (None, "ref_mut") => info.ref_mut = Some(true), (None, "source") => info.source = Some(true), (Some("not"), "source") => info.source = Some(false), (None, "backtrace") => info.backtrace = Some(true), (Some("not"), "backtrace") => info.backtrace = Some(false), _ => { return Err(Error::new( path.span(), format!( "Attribute doesn't support parameter `{}` here", quote! { #path } ), )) } } } } } Ok(()) } // TODO: Remove this eventually, once all macros migrate to // custom typed attributes parsing. /// Polyfill for [`syn`] 1.x AST. pub(crate) mod polyfill { use proc_macro2::TokenStream; use quote::ToTokens; use syn::{ ext::IdentExt as _, parse::{Parse, ParseStream, Parser}, token, Token, }; #[derive(Clone)] pub(crate) enum PathOrKeyword { Path(syn::Path), Keyword(syn::Ident), } impl Parse for PathOrKeyword { fn parse(input: ParseStream<'_>) -> syn::Result { if input.fork().parse::().is_ok() { return input.parse().map(Self::Path); } syn::Ident::parse_any(input).map(Self::Keyword) } } impl ToTokens for PathOrKeyword { fn to_tokens(&self, tokens: &mut TokenStream) { match self { Self::Path(p) => p.to_tokens(tokens), Self::Keyword(i) => i.to_tokens(tokens), } } } impl PathOrKeyword { pub(crate) fn is_ident(&self, ident: &I) -> bool where syn::Ident: PartialEq, { match self { Self::Path(p) => p.is_ident(ident), Self::Keyword(i) => i == ident, } } pub fn get_ident(&self) -> Option<&syn::Ident> { match self { Self::Path(p) => p.get_ident(), Self::Keyword(i) => Some(i), } } } impl From for syn::Path { fn from(p: PathOrKeyword) -> Self { match p { PathOrKeyword::Path(p) => p, PathOrKeyword::Keyword(i) => i.into(), } } } #[derive(Clone)] pub(crate) struct MetaList { pub(crate) path: PathOrKeyword, pub(crate) tokens: TokenStream, } impl Parse for MetaList { fn parse(input: ParseStream<'_>) -> syn::Result { let path = input.parse::()?; let tokens; _ = syn::parenthesized!(tokens in input); Ok(Self { path, tokens: tokens.parse()?, }) } } impl ToTokens for MetaList { fn to_tokens(&self, tokens: &mut TokenStream) { self.path.to_tokens(tokens); token::Paren::default() .surround(tokens, |tokens| self.tokens.to_tokens(tokens)) } } impl MetaList { pub fn parse_args_with(&self, parser: F) -> syn::Result { parser.parse2(self.tokens.clone()) } } #[derive(Clone)] pub(crate) enum Meta { Path(PathOrKeyword), List(MetaList), } impl Parse for Meta { fn parse(input: ParseStream<'_>) -> syn::Result { let path = input.parse::()?; Ok(if input.peek(token::Paren) { let tokens; _ = syn::parenthesized!(tokens in input); Self::List(MetaList { path, tokens: tokens.parse()?, }) } else { Self::Path(path) }) } } impl ToTokens for Meta { fn to_tokens(&self, tokens: &mut TokenStream) { match self { Self::Path(p) => p.to_tokens(tokens), Self::List(l) => l.to_tokens(tokens), } } } #[derive(Clone)] pub(crate) enum NestedMeta { Meta(Meta), Lit(syn::Lit), } impl Parse for NestedMeta { fn parse(input: ParseStream<'_>) -> syn::Result { if input.peek(syn::Lit) && !(input.peek(syn::LitBool) && input.peek2(Token![=])) { input.parse().map(Self::Lit) } else if input.peek(syn::Ident::peek_any) || input.peek(Token![::]) && input.peek3(syn::Ident::peek_any) { input.parse().map(Self::Meta) } else { Err(input.error("expected identifier or literal")) } } } impl ToTokens for NestedMeta { fn to_tokens(&self, tokens: &mut TokenStream) { match self { Self::Meta(m) => m.to_tokens(tokens), Self::Lit(l) => l.to_tokens(tokens), } } } } #[derive(Clone, Debug, Default)] pub struct FullMetaInfo { pub enabled: bool, pub forward: bool, pub owned: bool, pub ref_: bool, pub ref_mut: bool, pub info: MetaInfo, } #[derive(Clone, Debug, Default)] pub struct MetaInfo { pub enabled: Option, pub forward: Option, pub owned: Option, pub ref_: Option, pub ref_mut: Option, pub source: Option, pub backtrace: Option, #[cfg(any(feature = "from", feature = "into"))] pub types: HashMap>, } impl MetaInfo { fn into_full(self, defaults: FullMetaInfo) -> FullMetaInfo { FullMetaInfo { enabled: self.enabled.unwrap_or(defaults.enabled), forward: self.forward.unwrap_or(defaults.forward), owned: self.owned.unwrap_or(defaults.owned), ref_: self.ref_.unwrap_or(defaults.ref_), ref_mut: self.ref_mut.unwrap_or(defaults.ref_mut), info: self, } } } impl FullMetaInfo { pub fn ref_types(&self) -> Vec { let mut ref_types = vec![]; if self.owned { ref_types.push(RefType::No); } if self.ref_ { ref_types.push(RefType::Ref); } if self.ref_mut { ref_types.push(RefType::Mut); } ref_types } } pub fn get_if_type_parameter_used_in_type( type_parameters: &HashSet, ty: &syn::Type, ) -> Option { is_type_parameter_used_in_type(type_parameters, ty).then(|| match ty { syn::Type::Reference(syn::TypeReference { elem: ty, .. }) => (**ty).clone(), ty => ty.clone(), }) } pub fn is_type_parameter_used_in_type( type_parameters: &HashSet, ty: &syn::Type, ) -> bool { match ty { syn::Type::Path(ty) => { if let Some(qself) = &ty.qself { if is_type_parameter_used_in_type(type_parameters, &qself.ty) { return true; } } if let Some(segment) = ty.path.segments.first() { if type_parameters.contains(&segment.ident) { return true; } } ty.path.segments.iter().any(|segment| { if let syn::PathArguments::AngleBracketed(arguments) = &segment.arguments { arguments.args.iter().any(|argument| match argument { syn::GenericArgument::Type(ty) => { is_type_parameter_used_in_type(type_parameters, ty) } syn::GenericArgument::Constraint(constraint) => { type_parameters.contains(&constraint.ident) } _ => false, }) } else { false } }) } syn::Type::Reference(ty) => { is_type_parameter_used_in_type(type_parameters, &ty.elem) } _ => false, } } #[cfg(any( feature = "as_ref", feature = "debug", feature = "display", feature = "from", feature = "into", feature = "try_from", ))] mod either { use proc_macro2::TokenStream; use quote::ToTokens; use syn::parse::{discouraged::Speculative as _, Parse, ParseStream}; /// Either [`Left`] or [`Right`]. /// /// [`Left`]: Either::Left /// [`Right`]: Either::Right #[derive(Clone, Copy, Debug)] pub(crate) enum Either { /// Left variant. Left(L), /// Right variant. Right(R), } impl Parse for Either where L: Parse, R: Parse, { fn parse(input: ParseStream<'_>) -> syn::Result { let ahead = input.fork(); if let Ok(left) = ahead.parse::() { input.advance_to(&ahead); Ok(Self::Left(left)) } else { input.parse::().map(Self::Right) } } } impl Iterator for Either where L: Iterator, R: Iterator, { type Item = T; fn next(&mut self) -> Option { match self { Self::Left(left) => left.next(), Self::Right(right) => right.next(), } } } impl ToTokens for Either where L: ToTokens, R: ToTokens, { fn to_tokens(&self, tokens: &mut TokenStream) { match self { Self::Left(l) => l.to_tokens(tokens), Self::Right(r) => r.to_tokens(tokens), } } } } #[cfg(any( feature = "as_ref", feature = "debug", feature = "display", feature = "from", feature = "into", feature = "try_from", ))] mod spanning { use std::ops::{Deref, DerefMut}; use proc_macro2::Span; /// Wrapper for non-[`Spanned`] types to hold their [`Span`]. /// /// [`Spanned`]: syn::spanned::Spanned #[derive(Clone, Copy, Debug)] pub(crate) struct Spanning { /// [`Span`] of the `item`. pub(crate) span: Span, /// Item the [`Span`] is held for. pub(crate) item: T, } impl Spanning { /// Creates a new [`Spanning`] `item`, attaching the provided [`Span`] to it. pub(crate) const fn new(item: T, span: Span) -> Self where T: Sized, { Self { span, item } } /// Destructures this [`Spanning`] wrapper returning the underlying `item`. pub fn into_inner(self) -> T where T: Sized, { self.item } /// Returns the [`Span`] contained in this [`Spanning`] wrapper. pub(crate) const fn span(&self) -> Span { self.span } /// Converts this `&`[`Spanning`]`` into [`Spanning`]`<&T>` (moves the reference inside). pub(crate) const fn as_ref(&self) -> Spanning<&T> { Spanning { span: self.span, item: &self.item, } } /// Maps the wrapped `item` with the provided `f`unction, preserving the current [`Span`]. pub(crate) fn map(self, f: impl FnOnce(T) -> U) -> Spanning where T: Sized, { Spanning { span: self.span, item: f(self.item), } } } #[cfg(feature = "into")] impl Spanning> { pub(crate) fn transpose(self) -> Option> { match self.item { Some(item) => Some(Spanning { item, span: self.span, }), None => None, } } } impl Deref for Spanning { type Target = T; fn deref(&self) -> &Self::Target { &self.item } } impl DerefMut for Spanning { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.item } } } #[cfg(any( feature = "as_ref", feature = "debug", feature = "display", feature = "from", feature = "into", feature = "try_from", ))] pub(crate) mod attr { use std::any::Any; use syn::{ parse::{Parse, ParseStream}, spanned::Spanned as _, }; use super::{Either, Spanning}; #[cfg(any( feature = "as_ref", feature = "from", feature = "into", feature = "try_from" ))] pub(crate) use self::empty::Empty; #[cfg(any( feature = "as_ref", feature = "debug", feature = "from", feature = "into", ))] pub(crate) use self::skip::Skip; #[cfg(any(feature = "as_ref", feature = "from", feature = "try_from"))] pub(crate) use self::types::Types; #[cfg(any(feature = "as_ref", feature = "from"))] pub(crate) use self::{ conversion::Conversion, field_conversion::FieldConversion, forward::Forward, }; #[cfg(feature = "try_from")] pub(crate) use self::{repr_conversion::ReprConversion, repr_int::ReprInt}; /// [`Parse`]ing with additional state or metadata. pub(crate) trait Parser { /// [`Parse`]s an item, using additional state or metadata. /// /// Default implementation just calls [`Parse::parse()`] directly. fn parse(&self, input: ParseStream<'_>) -> syn::Result { T::parse(input) } } impl Parser for () {} /// Parsing of a typed attribute from multiple [`syn::Attribute`]s. pub(crate) trait ParseMultiple: Parse + Sized + 'static { /// Parses this attribute from the provided single [`syn::Attribute`] with the provided /// [`Parser`]. /// /// Required, because with [`Parse`] we only able to parse inner attribute tokens, which /// doesn't work for attributes with empty arguments, like `#[attr]`. /// /// Override this method if the default [`syn::Attribute::parse_args_with()`] is not enough. fn parse_attr_with( attr: &syn::Attribute, parser: &P, ) -> syn::Result { attr.parse_args_with(|ps: ParseStream<'_>| parser.parse(ps)) } /// Merges multiple values of this attribute into a single one. /// /// Default implementation only errors, disallowing multiple values of the same attribute. fn merge_attrs( _prev: Spanning, new: Spanning, name: &syn::Ident, ) -> syn::Result> { Err(syn::Error::new( new.span, format!("only single `#[{name}(...)]` attribute is allowed here"), )) } /// Merges multiple [`Option`]al values of this attribute into a single one. /// /// Default implementation uses [`ParseMultiple::merge_attrs()`] when both `prev` and `new` /// are [`Some`]. fn merge_opt_attrs( prev: Option>, new: Option>, name: &syn::Ident, ) -> syn::Result>> { Ok(match (prev, new) { (Some(p), Some(n)) => Some(Self::merge_attrs(p, n, name)?), (Some(p), None) => Some(p), (None, Some(n)) => Some(n), (None, None) => None, }) } /// Parses this attribute from the provided multiple [`syn::Attribute`]s with the provided /// [`Parser`], merging them, and preserving their [`Span`]. /// /// [`Span`]: proc_macro2::Span fn parse_attrs_with( attrs: impl AsRef<[syn::Attribute]>, name: &syn::Ident, parser: &P, ) -> syn::Result>> { attrs .as_ref() .iter() .filter(|attr| attr.path().is_ident(name)) .try_fold(None, |merged, attr| { let parsed = Spanning::new( Self::parse_attr_with(attr, parser)?, attr.span(), ); if let Some(prev) = merged { Self::merge_attrs(prev, parsed, name).map(Some) } else { Ok(Some(parsed)) } }) } /// Parses this attribute from the provided multiple [`syn::Attribute`]s with the default /// [`Parse`], merging them, and preserving their [`Span`]. /// /// [`Span`]: proc_macro2::Span fn parse_attrs( attrs: impl AsRef<[syn::Attribute]>, name: &syn::Ident, ) -> syn::Result>> { Self::parse_attrs_with(attrs, name, &()) } } impl ParseMultiple for Either { fn parse_attr_with( attr: &syn::Attribute, parser: &P, ) -> syn::Result { L::parse_attr_with(attr, parser) .map(Self::Left) .or_else(|_| R::parse_attr_with(attr, parser).map(Self::Right)) } fn merge_attrs( prev: Spanning, new: Spanning, name: &syn::Ident, ) -> syn::Result> { Ok(match (prev.item, new.item) { (Self::Left(p), Self::Left(n)) => { L::merge_attrs(Spanning::new(p, prev.span), Spanning::new(n, new.span), name)? .map(Self::Left) }, (Self::Right(p), Self::Right(n)) => { R::merge_attrs(Spanning::new(p, prev.span), Spanning::new(n, new.span), name)? .map(Self::Right) }, _ => return Err(syn::Error::new( new.span, format!("only single kind of `#[{name}(...)]` attribute is allowed here"), )) }) } } #[cfg(any( feature = "as_ref", feature = "from", feature = "into", feature = "try_from" ))] mod empty { use syn::{ parse::{Parse, ParseStream}, spanned::Spanned as _, }; use super::{ParseMultiple, Parser, Spanning}; /// Representation of an empty attribute, containing no arguments. /// /// ```rust,ignore /// #[] /// ``` #[derive(Clone, Copy, Debug)] pub(crate) struct Empty; impl Parse for Empty { fn parse(input: ParseStream<'_>) -> syn::Result { if input.is_empty() { Ok(Self) } else { Err(syn::Error::new( input.span(), "no attribute arguments allowed here", )) } } } impl ParseMultiple for Empty { fn parse_attr_with( attr: &syn::Attribute, _: &P, ) -> syn::Result { if matches!(attr.meta, syn::Meta::Path(_)) { Ok(Self) } else { Err(syn::Error::new( attr.span(), "no attribute arguments allowed here", )) } } fn merge_attrs( _prev: Spanning, new: Spanning, name: &syn::Ident, ) -> syn::Result> { Err(syn::Error::new( new.span, format!("only single `#[{name}]` attribute is allowed here"), )) } } } #[cfg(any(feature = "as_ref", feature = "from"))] mod forward { use syn::{ parse::{Parse, ParseStream}, spanned::Spanned as _, }; use super::ParseMultiple; /// Representation of a `forward` attribute. /// /// ```rust,ignore /// #[(forward)] /// ``` #[derive(Clone, Copy, Debug)] pub(crate) struct Forward; impl Parse for Forward { fn parse(input: ParseStream<'_>) -> syn::Result { match input.parse::()? { p if p.is_ident("forward") => Ok(Self), p => Err(syn::Error::new(p.span(), "only `forward` allowed here")), } } } impl ParseMultiple for Forward {} } #[cfg(feature = "try_from")] mod repr_int { use proc_macro2::Span; use syn::parse::{Parse, ParseStream}; use super::{ParseMultiple, Parser, Spanning}; /// Representation of a [`#[repr(u/i*)]` Rust attribute][0]. /// /// **NOTE**: Disregards any non-integer representation `#[repr]`s. /// /// ```rust,ignore /// #[repr()] /// ``` /// /// [0]: https://doc.rust-lang.org/reference/type-layout.html#primitive-representations #[derive(Default)] pub(crate) struct ReprInt(Option); impl ReprInt { /// Returns [`syn::Ident`] of the primitive integer type behind this [`ReprInt`] /// attribute. /// /// If there is no explicitly specified primitive integer type, then returns a /// [default `isize` discriminant][0]. /// /// [`syn::Ident`]: struct@syn::Ident /// [0]: https://doc.rust-lang.org/reference/items/enumerations.html#discriminants pub(crate) fn ty(&self) -> syn::Ident { self.0 .as_ref() .cloned() .unwrap_or_else(|| syn::Ident::new("isize", Span::call_site())) } } impl Parse for ReprInt { fn parse(_: ParseStream<'_>) -> syn::Result { unreachable!("call `attr::ParseMultiple::parse_attr_with()` instead") } } impl ParseMultiple for ReprInt { fn parse_attr_with( attr: &syn::Attribute, _: &P, ) -> syn::Result { let mut repr = None; attr.parse_nested_meta(|meta| { if let Some(ident) = meta.path.get_ident() { if matches!( ident.to_string().as_str(), "u8" | "u16" | "u32" | "u64" | "u128" | "usize" | "i8" | "i16" | "i32" | "i64" | "i128" | "isize" ) { repr = Some(ident.clone()); return Ok(()); } } // Ignore all other attributes that could have a body, e.g. `align`. _ = meta.input.parse::(); Ok(()) })?; Ok(Self(repr)) } fn merge_attrs( prev: Spanning, new: Spanning, name: &syn::Ident, ) -> syn::Result> { match (&prev.item.0, &new.item.0) { (Some(_), None) | (None, None) => Ok(prev), (None, Some(_)) => Ok(new), (Some(_), Some(_)) => Err(syn::Error::new( new.span, format!( "only single `#[{name}(u/i*)]` attribute is expected here", ), )), } } } } #[cfg(any( feature = "as_ref", feature = "debug", feature = "display", feature = "from", feature = "into", ))] mod skip { use syn::{ parse::{Parse, ParseStream}, spanned::Spanned as _, }; use super::{ParseMultiple, Spanning}; /// Representation of a `skip`/`ignore` attribute. /// /// ```rust,ignore /// #[(skip)] /// #[(ignore)] /// ``` #[derive(Clone, Copy, Debug)] pub(crate) struct Skip(&'static str); impl Parse for Skip { fn parse(content: ParseStream<'_>) -> syn::Result { match content.parse::()? { p if p.is_ident("skip") => Ok(Self("skip")), p if p.is_ident("ignore") => Ok(Self("ignore")), p => Err(syn::Error::new( p.span(), "only `skip`/`ignore` allowed here", )), } } } impl Skip { /// Returns the concrete name of this attribute (`skip` or `ignore`). pub(crate) const fn name(&self) -> &'static str { self.0 } } impl ParseMultiple for Skip { fn merge_attrs( _: Spanning, new: Spanning, name: &syn::Ident, ) -> syn::Result> { Err(syn::Error::new( new.span, format!( "only single `#[{name}(skip)]`/`#[{name}(ignore)]` attribute is allowed \ here", ), )) } } } #[cfg(any(feature = "as_ref", feature = "from", feature = "try_from"))] mod types { use syn::{ parse::{Parse, ParseStream}, punctuated::Punctuated, Token, }; use super::{ParseMultiple, Spanning}; /// Representation of an attribute, containing a comma-separated list of types. /// /// ```rust,ignore /// #[()] /// ``` pub(crate) struct Types(pub(crate) Punctuated); impl Parse for Types { fn parse(input: ParseStream<'_>) -> syn::Result { input .parse_terminated(syn::Type::parse, Token![,]) .map(Self) } } impl ParseMultiple for Types { fn merge_attrs( mut prev: Spanning, new: Spanning, _: &syn::Ident, ) -> syn::Result> { prev.item.0.extend(new.item.0); Ok(Spanning::new( prev.item, prev.span.join(new.span).unwrap_or(prev.span), )) } } } #[cfg(any(feature = "as_ref", feature = "from"))] mod conversion { use syn::parse::{Parse, ParseStream}; use crate::utils::attr; use super::{Either, ParseMultiple, Spanning}; /// Untyped analogue of a [`Conversion`], recreating its type structure via [`Either`]. /// /// Used to piggyback [`Parse`] and [`ParseMultiple`] impls to [`Either`]. type Untyped = Either; /// Representation of an attribute, specifying which conversions should be generated: /// either forwarded via a blanket impl, or direct for concrete specified types. /// /// ```rust,ignore /// #[(forward)] /// #[()] /// ``` pub(crate) enum Conversion { Forward(attr::Forward), Types(attr::Types), } impl From for Conversion { fn from(v: Untyped) -> Self { match v { Untyped::Left(f) => Self::Forward(f), Untyped::Right(t) => Self::Types(t), } } } impl From for Untyped { fn from(v: Conversion) -> Self { match v { Conversion::Forward(f) => Self::Left(f), Conversion::Types(t) => Self::Right(t), } } } impl Parse for Conversion { fn parse(input: ParseStream<'_>) -> syn::Result { Untyped::parse(input).map(Self::from) } } impl ParseMultiple for Conversion { fn parse_attr_with( attr: &syn::Attribute, parser: &P, ) -> syn::Result { Untyped::parse_attr_with(attr, parser).map(Self::from) } fn merge_attrs( prev: Spanning, new: Spanning, name: &syn::Ident, ) -> syn::Result> { Untyped::merge_attrs(prev.map(Into::into), new.map(Into::into), name) .map(|v| v.map(Self::from)) } } } #[cfg(any(feature = "as_ref", feature = "from"))] mod field_conversion { use syn::parse::{Parse, ParseStream}; use crate::utils::attr; use super::{Either, ParseMultiple, Spanning}; /// Untyped analogue of a [`FieldConversion`], recreating its type structure via [`Either`]. /// /// Used to piggyback [`Parse`] and [`ParseMultiple`] impls to [`Either`]. type Untyped = Either>>; /// Representation of an attribute, specifying which conversions should be generated: /// either forwarded via a blanket impl, or direct for concrete specified types. /// /// ```rust,ignore /// #[] /// #[(skip)] #[(ignore)] /// #[(forward)] /// #[()] /// ``` pub(crate) enum FieldConversion { Empty(attr::Empty), Skip(attr::Skip), Forward(attr::Forward), Types(attr::Types), } impl From for FieldConversion { fn from(v: Untyped) -> Self { match v { Untyped::Left(e) => Self::Empty(e), Untyped::Right(Either::Left(s)) => Self::Skip(s), Untyped::Right(Either::Right(Either::Left(f))) => Self::Forward(f), Untyped::Right(Either::Right(Either::Right(t))) => Self::Types(t), } } } impl From for Untyped { fn from(v: FieldConversion) -> Self { match v { FieldConversion::Empty(e) => Self::Left(e), FieldConversion::Skip(s) => Self::Right(Either::Left(s)), FieldConversion::Forward(f) => { Self::Right(Either::Right(Either::Left(f))) } FieldConversion::Types(t) => { Self::Right(Either::Right(Either::Right(t))) } } } } impl From for FieldConversion { fn from(v: attr::Conversion) -> Self { match v { attr::Conversion::Forward(f) => Self::Forward(f), attr::Conversion::Types(t) => Self::Types(t), } } } impl From for Option { fn from(v: FieldConversion) -> Self { match v { FieldConversion::Forward(f) => Some(attr::Conversion::Forward(f)), FieldConversion::Types(t) => Some(attr::Conversion::Types(t)), FieldConversion::Empty(_) | FieldConversion::Skip(_) => None, } } } impl Parse for FieldConversion { fn parse(input: ParseStream<'_>) -> syn::Result { Untyped::parse(input).map(Self::from) } } impl ParseMultiple for FieldConversion { fn parse_attr_with( attr: &syn::Attribute, parser: &P, ) -> syn::Result { Untyped::parse_attr_with(attr, parser).map(Self::from) } fn merge_attrs( prev: Spanning, new: Spanning, name: &syn::Ident, ) -> syn::Result> { Untyped::merge_attrs(prev.map(Into::into), new.map(Into::into), name) .map(|v| v.map(Self::from)) } } } #[cfg(feature = "try_from")] mod repr_conversion { use syn::parse::{Parse, ParseStream}; use crate::utils::attr; use super::{ParseMultiple, Spanning}; /// Representation of an attribute, specifying which `repr`-conversions should be generated: /// either direct into a discriminant, or for concrete specified types forwarding from a /// discriminant. /// /// ```rust,ignore /// #[(repr)] /// #[(repr())] /// ``` pub(crate) enum ReprConversion { Discriminant(attr::Empty), Types(attr::Types), } impl Parse for ReprConversion { fn parse(input: ParseStream<'_>) -> syn::Result { let prefix = syn::Ident::parse(input)?; if prefix != "repr" { return Err(syn::Error::new( prefix.span(), "expected `repr` argument here", )); } if input.is_empty() { Ok(Self::Discriminant(attr::Empty)) } else { let inner; syn::parenthesized!(inner in input); Ok(Self::Types(attr::Types::parse(&inner)?)) } } } impl ParseMultiple for ReprConversion { fn merge_attrs( prev: Spanning, new: Spanning, name: &syn::Ident, ) -> syn::Result> { Ok(match (prev.item, new.item) { (Self::Discriminant(_), Self::Discriminant(_)) => { return Err(syn::Error::new( new.span, format!("only single `#[{name}(repr)]` attribute is allowed here"), )) }, (Self::Types(p), Self::Types(n)) => { attr::Types::merge_attrs( Spanning::new(p, prev.span), Spanning::new(n, new.span), name, )?.map(Self::Types) }, _ => return Err(syn::Error::new( new.span, format!( "only single kind of `#[{name}(repr(...))]` attribute is allowed here", ), )) }) } } } } #[cfg(any(feature = "from", feature = "into"))] mod fields_ext { use std::{cmp, iter}; use quote::ToTokens as _; use syn::{punctuated, spanned::Spanned as _}; use super::Either; /// Abstraction over `.len()` method to use it on type parameters. pub(crate) trait Len { /// Returns number of fields. fn len(&self) -> usize; } impl Len for syn::Fields { fn len(&self) -> usize { self.len() } } impl Len for [T] { fn len(&self) -> usize { self.len() } } /// [`syn::Fields`] extension. pub(crate) trait FieldsExt: Len { /// Validates the provided [`syn::Type`] against these [`syn::Fields`]. fn validate_type<'t>( &self, ty: &'t syn::Type, ) -> syn::Result< Either, iter::Once<&'t syn::Type>>, > { match ty { syn::Type::Tuple(syn::TypeTuple { elems, .. }) if self.len() > 1 => { match self.len().cmp(&elems.len()) { cmp::Ordering::Greater => { return Err(syn::Error::new( ty.span(), format!( "wrong tuple length: expected {}, found {}. \ Consider adding {} more type{}: `({})`", self.len(), elems.len(), self.len() - elems.len(), if self.len() - elems.len() > 1 { "s" } else { "" }, elems .iter() .map(|ty| ty.into_token_stream().to_string()) .chain( (0..(self.len() - elems.len())) .map(|_| "_".to_string()) ) .collect::>() .join(", "), ), )); } cmp::Ordering::Less => { return Err(syn::Error::new( ty.span(), format!( "wrong tuple length: expected {}, found {}. \ Consider removing last {} type{}: `({})`", self.len(), elems.len(), elems.len() - self.len(), if elems.len() - self.len() > 1 { "s" } else { "" }, elems .iter() .take(self.len()) .map(|ty| ty.into_token_stream().to_string()) .collect::>() .join(", "), ), )); } cmp::Ordering::Equal => {} } } other if self.len() > 1 => { return Err(syn::Error::new( other.span(), format!( "expected tuple: `({}, {})`", other.into_token_stream(), (0..(self.len() - 1)) .map(|_| "_") .collect::>() .join(", "), ), )); } _ => {} } Ok(match ty { syn::Type::Tuple(syn::TypeTuple { elems, .. }) => { Either::Left(elems.iter()) } other => Either::Right(iter::once(other)), }) } } impl FieldsExt for T {} } #[cfg(feature = "as_ref")] mod generics_search { use syn::visit::Visit; use super::HashSet; /// Search of whether some generics (type parameters, lifetime parameters or const parameters) /// are present in some [`syn::Type`]. pub(crate) struct GenericsSearch<'s> { /// Type parameters to look for. pub(crate) types: HashSet<&'s syn::Ident>, /// Lifetime parameters to look for. pub(crate) lifetimes: HashSet<&'s syn::Ident>, /// Const parameters to look for. pub(crate) consts: HashSet<&'s syn::Ident>, } impl<'s> GenericsSearch<'s> { /// Checks the provided [`syn::Type`] to contain anything from this [`GenericsSearch`]. pub(crate) fn any_in(&self, ty: &syn::Type) -> bool { let mut visitor = Visitor { search: self, found: false, }; visitor.visit_type(ty); visitor.found } } /// [`Visit`]or performing a [`GenericsSearch`]. struct Visitor<'s> { /// [`GenericsSearch`] parameters. search: &'s GenericsSearch<'s>, /// Indication whether anything was found for the [`GenericsSearch`] parameters. found: bool, } impl<'s, 'ast> Visit<'ast> for Visitor<'s> { fn visit_type_path(&mut self, tp: &'ast syn::TypePath) { self.found |= tp.path.get_ident().map_or(false, |ident| { self.search.types.contains(ident) || self.search.consts.contains(ident) }); syn::visit::visit_type_path(self, tp) } fn visit_lifetime(&mut self, lf: &'ast syn::Lifetime) { self.found |= self.search.lifetimes.contains(&lf.ident); syn::visit::visit_lifetime(self, lf) } fn visit_expr_path(&mut self, ep: &'ast syn::ExprPath) { self.found |= ep .path .get_ident() .map_or(false, |ident| self.search.consts.contains(ident)); syn::visit::visit_expr_path(self, ep) } } }