Bug 1888590 - Mark some subtests on trusted-types-event-handlers.html as failing...
[gecko.git] / third_party / rust / zerovec-derive / src / utils.rs
blobe4155076f722928eb4543e864f0a90e392f48253
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};
7 use proc_macro2::Span;
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>()
18             .ok()
19             .and_then(|s| s.idents.iter().find(|s| predicate(s)).map(|_| ()))
20             .is_some()
21     })
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![,])?,
33         })
34     }
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 {
40     match *fields {
41         Fields::Named(_) => quote!( { #(#streams),* } ),
42         Fields::Unnamed(_) => quote!( ( #(#streams),* ) ),
43         Fields::Unit => {
44             unreachable!("#[make_(var)ule] should have already checked that there are fields")
45         }
46     }
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 {
52         quote!(;)
53     } else {
54         quote!()
55     }
58 /// Returns the repr attribute to be applied to the resultant ULE or VarULE type
59 pub fn repr_for(f: &Fields) -> TokenStream2 {
60     if f.len() == 1 {
61         quote!(transparent)
62     } else {
63         quote!(packed)
64     }
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;
84     );
86     for (i, field_info) in fields.iter().enumerate() {
87         let field = &field_info.field;
88         let ty = &field.ty;
89         let ty = if fields_are_asule {
90             quote!(<#ty as zerovec::ule::AsULE>::ULE)
91         } else {
92             quote!(#ty)
93         };
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);
97         code = quote! {
98             #code;
99             const #size_ident: usize = ::core::mem::size_of::<#ty>();
100             const #new_offset_ident: usize = #prev_offset_ident + #size_ident;
101             #pf_code;
102         };
104         prev_offset_ident = new_offset_ident;
105     }
107     (code, prev_offset_ident)
110 #[derive(Clone, Debug)]
111 pub(crate) struct FieldInfo<'a> {
112     pub accessor: TokenStream2,
113     pub field: &'a Field,
114     pub index: usize,
117 impl<'a> FieldInfo<'a> {
118     pub fn make_list(iter: impl Iterator<Item = &'a Field>) -> Vec<Self> {
119         iter.enumerate()
120             .map(|(i, field)| Self::new_for_field(field, i))
121             .collect()
122     }
124     pub fn new_for_field(f: &'a Field, index: usize) -> Self {
125         if let Some(ref i) = f.ident {
126             FieldInfo {
127                 accessor: quote!(#i),
128                 field: f,
129                 index,
130             }
131         } else {
132             let idx = Index::from(index);
133             FieldInfo {
134                 accessor: quote!(#idx),
135                 field: f,
136                 index,
137             }
138         }
139     }
141     /// Get the code for setting this field in struct decl/brace syntax
142     ///
143     /// Use self.accessor for dot-notation accesses
144     pub fn setter(&self) -> TokenStream2 {
145         if let Some(ref i) = self.field.ident {
146             quote!(#i: )
147         } else {
148             quote!()
149         }
150     }
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 {
155             quote!(#i)
156         } else {
157             suffixed_ident("field", self.index, self.field.span()).into_token_stream()
158         }
159     }
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")
165         } else {
166             format!("tuple struct field #{}", self.index)
167         }
168     }
171 /// Extracts all `zerovec::name(..)` attribute
172 pub fn extract_parenthetical_zerovec_attrs(
173     attrs: &mut Vec<Attribute>,
174     name: &str,
175 ) -> Result<Vec<Ident>> {
176     let mut ret = vec![];
177     let mut error = None;
178     attrs.retain(|a| {
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>() {
185                     Ok(l) => l,
186                     Err(_) => {
187                         error = Some(Error::new(
188                             a.span(),
189                             format!("#[zerovec::{name}(..)] takes in a comma separated list of identifiers"),
190                         ));
191                         return false;
192                     }
193                 };
194                 ret.extend(list.idents.iter().cloned());
195                 return false;
196             }
197         }
199         true
200     });
202     if let Some(error) = error {
203         return Err(error);
204     }
205     Ok(ret)
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![];
211     attrs.retain(|a| {
212         if a.path().segments.len() == 2 && a.path().segments[0].ident == "zerovec" {
213             ret.push(a.clone());
214             return false;
215         }
216         true
217     });
218     ret
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(
230             varule[1].span(),
231             "Found multiple #[zerovec::varule()] on one field",
232         ));
233     }
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",
239         ));
240     }
242     Ok(varule.get(0).cloned())
245 #[derive(Default, Copy, Clone)]
246 pub struct ZeroVecAttrs {
247     pub skip_kv: bool,
248     pub skip_ord: bool,
249     pub serialize: bool,
250     pub deserialize: bool,
251     pub debug: bool,
252     pub hash: bool,
255 /// Removes all known zerovec:: attributes from struct attrs and validates them
256 pub fn extract_attributes_common(
257     attrs: &mut Vec<Attribute>,
258     span: Span,
259     is_var: bool,
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(
270             attr.span(),
271             format!("Found unknown or duplicate attribute for #[{name}]"),
272         ));
273     }
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" {
283             attrs.debug = true;
284         } else if ident == "Hash" {
285             attrs.hash = true;
286         } else {
287             return Err(Error::new(
288                 ident.span(),
289                 format!(
290                     "Found unknown derive attribute for #[{name}]: #[zerovec::derive({ident})]"
291                 ),
292             ));
293         }
294     }
296     for ident in skip {
297         if ident == "ZeroMapKV" {
298             attrs.skip_kv = true;
299         } else if ident == "Ord" {
300             attrs.skip_ord = true;
301         } else {
302             return Err(Error::new(
303                 ident.span(),
304                 format!("Found unknown derive attribute for #[{name}]: #[zerovec::skip_derive({ident})]"),
305             ));
306         }
307     }
309     if (attrs.serialize || attrs.deserialize) && !is_var {
310         return Err(Error::new(
311             span,
312             "#[make_ule] does not support #[zerovec::derive(Serialize, Deserialize)]",
313         ));
314     }
316     Ok(attrs)