1 // This file is part of ICU4X. For terms of use, please see the file
2 // called LICENSE at the top level of the ICU4X source tree
3 // (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
5 use quote::{quote, ToTokens};
8 use proc_macro2::TokenStream as TokenStream2;
9 use syn::parse::{Parse, ParseStream};
10 use syn::punctuated::Punctuated;
11 use syn::spanned::Spanned;
12 use syn::{Attribute, Error, Field, Fields, Ident, Index, Result, Token};
14 // Check that there are repr attributes satisfying the given predicate
15 pub fn has_valid_repr(attrs: &[Attribute], predicate: impl Fn(&Ident) -> bool + Copy) -> bool {
16 attrs.iter().filter(|a| a.path().is_ident("repr")).any(|a| {
17 a.parse_args::<IdentListAttribute>()
19 .and_then(|s| s.idents.iter().find(|s| predicate(s)).map(|_| ()))
24 // An attribute that is a list of idents
25 struct IdentListAttribute {
26 idents: Punctuated<Ident, Token![,]>,
29 impl Parse for IdentListAttribute {
30 fn parse(input: ParseStream) -> Result<Self> {
31 Ok(IdentListAttribute {
32 idents: input.parse_terminated(Ident::parse, Token![,])?,
37 /// Given a set of entries for struct field definitions to go inside a `struct {}` definition,
38 /// wrap in a () or {} based on the type of field
39 pub fn wrap_field_inits(streams: &[TokenStream2], fields: &Fields) -> TokenStream2 {
41 Fields::Named(_) => quote!( { #(#streams),* } ),
42 Fields::Unnamed(_) => quote!( ( #(#streams),* ) ),
44 unreachable!("#[make_(var)ule] should have already checked that there are fields")
49 /// Return a semicolon token if necessary after the struct definition
50 pub fn semi_for(f: &Fields) -> TokenStream2 {
51 if let Fields::Unnamed(..) = *f {
58 /// Returns the repr attribute to be applied to the resultant ULE or VarULE type
59 pub fn repr_for(f: &Fields) -> TokenStream2 {
67 fn suffixed_ident(name: &str, suffix: usize, s: Span) -> Ident {
68 Ident::new(&format!("{name}_{suffix}"), s)
71 /// Given an iterator over ULE or AsULE struct fields, returns code that calculates field sizes and generates a line
72 /// of code per field based on the per_field_code function (whose parameters are the field, the identifier of the const
73 /// for the previous offset, the identifier for the const for the next offset, and the field index)
74 pub(crate) fn generate_per_field_offsets<'a>(
75 fields: &[FieldInfo<'a>],
76 // Whether the fields are ULE types or AsULE (and need conversion)
77 fields_are_asule: bool,
78 // (field, prev_offset_ident, size_ident)
79 mut per_field_code: impl FnMut(&FieldInfo<'a>, &Ident, &Ident) -> TokenStream2, /* (code, remaining_offset) */
80 ) -> (TokenStream2, syn::Ident) {
81 let mut prev_offset_ident = Ident::new("ZERO", Span::call_site());
82 let mut code = quote!(
83 const ZERO: usize = 0;
86 for (i, field_info) in fields.iter().enumerate() {
87 let field = &field_info.field;
89 let ty = if fields_are_asule {
90 quote!(<#ty as zerovec::ule::AsULE>::ULE)
94 let new_offset_ident = suffixed_ident("OFFSET", i, field.span());
95 let size_ident = suffixed_ident("SIZE", i, field.span());
96 let pf_code = per_field_code(field_info, &prev_offset_ident, &size_ident);
99 const #size_ident: usize = ::core::mem::size_of::<#ty>();
100 const #new_offset_ident: usize = #prev_offset_ident + #size_ident;
104 prev_offset_ident = new_offset_ident;
107 (code, prev_offset_ident)
110 #[derive(Clone, Debug)]
111 pub(crate) struct FieldInfo<'a> {
112 pub accessor: TokenStream2,
113 pub field: &'a Field,
117 impl<'a> FieldInfo<'a> {
118 pub fn make_list(iter: impl Iterator<Item = &'a Field>) -> Vec<Self> {
120 .map(|(i, field)| Self::new_for_field(field, i))
124 pub fn new_for_field(f: &'a Field, index: usize) -> Self {
125 if let Some(ref i) = f.ident {
127 accessor: quote!(#i),
132 let idx = Index::from(index);
134 accessor: quote!(#idx),
141 /// Get the code for setting this field in struct decl/brace syntax
143 /// Use self.accessor for dot-notation accesses
144 pub fn setter(&self) -> TokenStream2 {
145 if let Some(ref i) = self.field.ident {
152 /// Produce a name for a getter for the field
153 pub fn getter(&self) -> TokenStream2 {
154 if let Some(ref i) = self.field.ident {
157 suffixed_ident("field", self.index, self.field.span()).into_token_stream()
161 /// Produce a prose name for the field for use in docs
162 pub fn getter_doc_name(&self) -> String {
163 if let Some(ref i) = self.field.ident {
164 format!("the unsized `{i}` field")
166 format!("tuple struct field #{}", self.index)
171 /// Extracts all `zerovec::name(..)` attribute
172 pub fn extract_parenthetical_zerovec_attrs(
173 attrs: &mut Vec<Attribute>,
175 ) -> Result<Vec<Ident>> {
176 let mut ret = vec![];
177 let mut error = None;
179 // skip the "zerovec" part
180 let second_segment = a.path().segments.iter().nth(1);
182 if let Some(second) = second_segment {
183 if second.ident == name {
184 let list = match a.parse_args::<IdentListAttribute>() {
187 error = Some(Error::new(
189 format!("#[zerovec::{name}(..)] takes in a comma separated list of identifiers"),
194 ret.extend(list.idents.iter().cloned());
202 if let Some(error) = error {
208 /// Removes all attributes with `zerovec` in the name and places them in a separate vector
209 pub fn extract_zerovec_attributes(attrs: &mut Vec<Attribute>) -> Vec<Attribute> {
210 let mut ret = vec![];
212 if a.path().segments.len() == 2 && a.path().segments[0].ident == "zerovec" {
221 /// Extract attributes from field, and return them
223 /// Only current field attribute is `zerovec::varule(VarUleType)`
224 pub fn extract_field_attributes(attrs: &mut Vec<Attribute>) -> Result<Option<Ident>> {
225 let mut zerovec_attrs = extract_zerovec_attributes(attrs);
226 let varule = extract_parenthetical_zerovec_attrs(&mut zerovec_attrs, "varule")?;
228 if varule.len() > 1 {
229 return Err(Error::new(
231 "Found multiple #[zerovec::varule()] on one field",
235 if !zerovec_attrs.is_empty() {
236 return Err(Error::new(
237 zerovec_attrs[1].span(),
238 "Found unusable #[zerovec::] attrs on field, only #[zerovec::varule()] supported",
242 Ok(varule.get(0).cloned())
245 #[derive(Default, Copy, Clone)]
246 pub struct ZeroVecAttrs {
250 pub deserialize: bool,
255 /// Removes all known zerovec:: attributes from struct attrs and validates them
256 pub fn extract_attributes_common(
257 attrs: &mut Vec<Attribute>,
260 ) -> Result<ZeroVecAttrs> {
261 let mut zerovec_attrs = extract_zerovec_attributes(attrs);
263 let derive = extract_parenthetical_zerovec_attrs(&mut zerovec_attrs, "derive")?;
264 let skip = extract_parenthetical_zerovec_attrs(&mut zerovec_attrs, "skip_derive")?;
266 let name = if is_var { "make_varule" } else { "make_ule" };
268 if let Some(attr) = zerovec_attrs.get(0) {
269 return Err(Error::new(
271 format!("Found unknown or duplicate attribute for #[{name}]"),
275 let mut attrs = ZeroVecAttrs::default();
277 for ident in derive {
278 if ident == "Serialize" {
279 attrs.serialize = true;
280 } else if ident == "Deserialize" {
281 attrs.deserialize = true;
282 } else if ident == "Debug" {
284 } else if ident == "Hash" {
287 return Err(Error::new(
290 "Found unknown derive attribute for #[{name}]: #[zerovec::derive({ident})]"
297 if ident == "ZeroMapKV" {
298 attrs.skip_kv = true;
299 } else if ident == "Ord" {
300 attrs.skip_ord = true;
302 return Err(Error::new(
304 format!("Found unknown derive attribute for #[{name}]: #[zerovec::skip_derive({ident})]"),
309 if (attrs.serialize || attrs.deserialize) && !is_var {
310 return Err(Error::new(
312 "#[make_ule] does not support #[zerovec::derive(Serialize, Deserialize)]",