stylo_derive/
cg.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use darling::{FromDeriveInput, FromField, FromVariant};
6use proc_macro2::{Span, TokenStream};
7use quote::{quote, TokenStreamExt};
8use syn::parse_quote;
9use syn::{self, AngleBracketedGenericArguments, AssocType, DeriveInput, Field};
10use syn::{GenericArgument, GenericParam, Ident, Path};
11use syn::{PathArguments, PathSegment, QSelf, Type, TypeArray, TypeGroup};
12use syn::{TypeParam, TypeParen, TypePath, TypeSlice, TypeTuple};
13use syn::{Variant, WherePredicate};
14use synstructure::{self, BindStyle, BindingInfo, VariantAst, VariantInfo};
15
16/// Given an input type which has some where clauses already, like:
17///
18/// struct InputType<T>
19/// where
20///     T: Zero,
21/// {
22///     ...
23/// }
24///
25/// Add the necessary `where` clauses so that the output type of a trait
26/// fulfils them.
27///
28/// For example:
29///
30/// ```ignore
31///     <T as ToComputedValue>::ComputedValue: Zero,
32/// ```
33///
34/// This needs to run before adding other bounds to the type parameters.
35pub(crate) fn propagate_clauses_to_output_type(
36    where_clause: &mut Option<syn::WhereClause>,
37    generics: &syn::Generics,
38    trait_path: &Path,
39    trait_output: &Ident,
40) {
41    let where_clause = match *where_clause {
42        Some(ref mut clause) => clause,
43        None => return,
44    };
45    let mut extra_bounds = vec![];
46    for pred in &where_clause.predicates {
47        let ty = match *pred {
48            syn::WherePredicate::Type(ref ty) => ty,
49            ref predicate => panic!("Unhanded complex where predicate: {:?}", predicate),
50        };
51
52        let path = match ty.bounded_ty {
53            syn::Type::Path(ref p) => &p.path,
54            ref ty => panic!("Unhanded complex where type: {:?}", ty),
55        };
56
57        assert!(
58            ty.lifetimes.is_none(),
59            "Unhanded complex lifetime bound: {:?}",
60            ty,
61        );
62
63        let ident = match path_to_ident(path) {
64            Some(i) => i,
65            None => panic!("Unhanded complex where type path: {:?}", path),
66        };
67
68        if generics.type_params().any(|param| param.ident == *ident) {
69            extra_bounds.push(ty.clone());
70        }
71    }
72
73    for bound in extra_bounds {
74        let ty = bound.bounded_ty;
75        let bounds = bound.bounds;
76        where_clause
77            .predicates
78            .push(parse_quote!(<#ty as #trait_path>::#trait_output: #bounds))
79    }
80}
81
82pub(crate) fn add_predicate(where_clause: &mut Option<syn::WhereClause>, pred: WherePredicate) {
83    where_clause
84        .get_or_insert(parse_quote!(where))
85        .predicates
86        .push(pred);
87}
88
89pub(crate) fn fmap_match<F>(input: &DeriveInput, bind_style: BindStyle, f: F) -> TokenStream
90where
91    F: FnMut(&BindingInfo) -> TokenStream,
92{
93    fmap2_match(input, bind_style, f, |_| None)
94}
95
96pub(crate) fn fmap2_match<F, G>(
97    input: &DeriveInput,
98    bind_style: BindStyle,
99    mut f: F,
100    mut g: G,
101) -> TokenStream
102where
103    F: FnMut(&BindingInfo) -> TokenStream,
104    G: FnMut(&BindingInfo) -> Option<TokenStream>,
105{
106    let mut s = synstructure::Structure::new(input);
107    s.variants_mut().iter_mut().for_each(|v| {
108        v.bind_with(|_| bind_style);
109    });
110    s.each_variant(|variant| {
111        let (mapped, mapped_fields) = value(variant, "mapped");
112        let fields_pairs = variant.bindings().iter().zip(mapped_fields.iter());
113        let mut computations = quote!();
114        computations.append_all(fields_pairs.map(|(field, mapped_field)| {
115            let expr = f(field);
116            quote! { let #mapped_field = #expr; }
117        }));
118        computations.append_all(
119            mapped_fields
120                .iter()
121                .map(|mapped_field| match g(mapped_field) {
122                    Some(expr) => quote! { let #mapped_field = #expr; },
123                    None => quote!(),
124                }),
125        );
126        computations.append_all(mapped);
127        Some(computations)
128    })
129}
130
131pub(crate) fn fmap_trait_output(
132    input: &DeriveInput,
133    trait_path: &Path,
134    trait_output: &Ident,
135) -> Path {
136    let segment = PathSegment {
137        ident: input.ident.clone(),
138        arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
139            args: input
140                .generics
141                .params
142                .iter()
143                .map(|arg| match arg {
144                    &GenericParam::Lifetime(ref data) => {
145                        GenericArgument::Lifetime(data.lifetime.clone())
146                    },
147                    &GenericParam::Type(ref data) => {
148                        let ident = &data.ident;
149                        GenericArgument::Type(parse_quote!(<#ident as #trait_path>::#trait_output))
150                    },
151                    &GenericParam::Const(ref inner) => {
152                        let ident = &inner.ident;
153                        GenericArgument::Const(parse_quote!(#ident))
154                    },
155                })
156                .collect(),
157            colon2_token: Default::default(),
158            gt_token: Default::default(),
159            lt_token: Default::default(),
160        }),
161    };
162    segment.into()
163}
164
165pub(crate) fn map_type_params<F>(
166    ty: &Type,
167    params: &[&TypeParam],
168    self_type: &Path,
169    f: &mut F,
170) -> Type
171where
172    F: FnMut(&Ident) -> Type,
173{
174    match *ty {
175        Type::Slice(ref inner) => Type::from(TypeSlice {
176            elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),
177            ..inner.clone()
178        }),
179        Type::Array(ref inner) => {
180            //ref ty, ref expr) => {
181            Type::from(TypeArray {
182                elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),
183                ..inner.clone()
184            })
185        },
186        ref ty @ Type::Never(_) => ty.clone(),
187        Type::Tuple(ref inner) => Type::from(TypeTuple {
188            elems: inner
189                .elems
190                .iter()
191                .map(|ty| map_type_params(&ty, params, self_type, f))
192                .collect(),
193            ..inner.clone()
194        }),
195        Type::Path(TypePath {
196            qself: None,
197            ref path,
198        }) => {
199            if let Some(ident) = path_to_ident(path) {
200                if params.iter().any(|ref param| &param.ident == ident) {
201                    return f(ident);
202                }
203                if ident == "Self" {
204                    return Type::from(TypePath {
205                        qself: None,
206                        path: self_type.clone(),
207                    });
208                }
209            }
210            Type::from(TypePath {
211                qself: None,
212                path: map_type_params_in_path(path, params, self_type, f),
213            })
214        },
215        Type::Path(TypePath {
216            ref qself,
217            ref path,
218        }) => Type::from(TypePath {
219            qself: qself.as_ref().map(|qself| QSelf {
220                ty: Box::new(map_type_params(&qself.ty, params, self_type, f)),
221                position: qself.position,
222                ..qself.clone()
223            }),
224            path: map_type_params_in_path(path, params, self_type, f),
225        }),
226        Type::Paren(ref inner) => Type::from(TypeParen {
227            elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),
228            ..inner.clone()
229        }),
230        Type::Group(ref inner) => Type::from(TypeGroup {
231            elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),
232            ..inner.clone()
233        }),
234        ref ty => panic!("type {:?} cannot be mapped yet", ty),
235    }
236}
237
238fn map_type_params_in_path<F>(
239    path: &Path,
240    params: &[&TypeParam],
241    self_type: &Path,
242    f: &mut F,
243) -> Path
244where
245    F: FnMut(&Ident) -> Type,
246{
247    Path {
248        leading_colon: path.leading_colon,
249        segments: path
250            .segments
251            .iter()
252            .map(|segment| PathSegment {
253                ident: segment.ident.clone(),
254                arguments: match segment.arguments {
255                    PathArguments::AngleBracketed(ref data) => {
256                        PathArguments::AngleBracketed(AngleBracketedGenericArguments {
257                            args: data
258                                .args
259                                .iter()
260                                .map(|arg| match arg {
261                                    ty @ &GenericArgument::Lifetime(_) => ty.clone(),
262                                    &GenericArgument::Type(ref data) => GenericArgument::Type(
263                                        map_type_params(data, params, self_type, f),
264                                    ),
265                                    &GenericArgument::AssocType(ref data) => {
266                                        GenericArgument::AssocType(AssocType {
267                                            ty: map_type_params(&data.ty, params, self_type, f),
268                                            ..data.clone()
269                                        })
270                                    },
271                                    ref arg => panic!("arguments {:?} cannot be mapped yet", arg),
272                                })
273                                .collect(),
274                            ..data.clone()
275                        })
276                    },
277                    ref arg @ PathArguments::None => arg.clone(),
278                    ref parameters => panic!("parameters {:?} cannot be mapped yet", parameters),
279                },
280            })
281            .collect(),
282    }
283}
284
285fn path_to_ident(path: &Path) -> Option<&Ident> {
286    match *path {
287        Path {
288            leading_colon: None,
289            ref segments,
290        } if segments.len() == 1 => {
291            if segments[0].arguments.is_empty() {
292                Some(&segments[0].ident)
293            } else {
294                None
295            }
296        },
297        _ => None,
298    }
299}
300
301pub(crate) fn parse_field_attrs<A>(field: &Field) -> A
302where
303    A: FromField,
304{
305    match A::from_field(field) {
306        Ok(attrs) => attrs,
307        Err(e) => panic!("failed to parse field attributes: {}", e),
308    }
309}
310
311pub(crate) fn parse_input_attrs<A>(input: &DeriveInput) -> A
312where
313    A: FromDeriveInput,
314{
315    match A::from_derive_input(input) {
316        Ok(attrs) => attrs,
317        Err(e) => panic!("failed to parse input attributes: {}", e),
318    }
319}
320
321pub(crate) fn parse_variant_attrs_from_ast<A>(variant: &VariantAst) -> A
322where
323    A: FromVariant,
324{
325    let v = Variant {
326        ident: variant.ident.clone(),
327        attrs: variant.attrs.to_vec(),
328        fields: variant.fields.clone(),
329        discriminant: variant.discriminant.clone(),
330    };
331    parse_variant_attrs(&v)
332}
333
334pub(crate) fn parse_variant_attrs<A>(variant: &Variant) -> A
335where
336    A: FromVariant,
337{
338    match A::from_variant(variant) {
339        Ok(attrs) => attrs,
340        Err(e) => panic!("failed to parse variant attributes: {}", e),
341    }
342}
343
344pub(crate) fn ref_pattern<'a>(
345    variant: &'a VariantInfo,
346    prefix: &str,
347) -> (TokenStream, Vec<BindingInfo<'a>>) {
348    let mut v = variant.clone();
349    v.bind_with(|_| BindStyle::Ref);
350    v.bindings_mut().iter_mut().for_each(|b| {
351        b.binding = Ident::new(&format!("{}_{}", b.binding, prefix), Span::call_site())
352    });
353    (v.pat(), v.bindings().to_vec())
354}
355
356pub(crate) fn value<'a>(
357    variant: &'a VariantInfo,
358    prefix: &str,
359) -> (TokenStream, Vec<BindingInfo<'a>>) {
360    let mut v = variant.clone();
361    v.bindings_mut().iter_mut().for_each(|b| {
362        b.binding = Ident::new(&format!("{}_{}", b.binding, prefix), Span::call_site())
363    });
364    v.bind_with(|_| BindStyle::Move);
365    (v.pat(), v.bindings().to_vec())
366}
367
368/// Transforms "FooBar" to "foo-bar".
369///
370/// If the first Camel segment is "Moz", "Webkit", or "Servo", the result string
371/// is prepended with "-".
372pub(crate) fn to_css_identifier(mut camel_case: &str) -> String {
373    camel_case = camel_case.trim_end_matches('_');
374    let mut first = true;
375    let mut result = String::with_capacity(camel_case.len());
376    while let Some(segment) = split_camel_segment(&mut camel_case) {
377        if first {
378            match segment {
379                "Moz" | "Webkit" | "Servo" => first = false,
380                _ => {},
381            }
382        }
383        if !first {
384            result.push('-');
385        }
386        first = false;
387        result.push_str(&segment.to_lowercase());
388    }
389    result
390}
391
392/// Transforms foo-bar to FOO_BAR.
393pub(crate) fn to_scream_case(css_case: &str) -> String {
394    css_case.to_uppercase().replace('-', "_")
395}
396
397/// Given "FooBar", returns "Foo" and sets `camel_case` to "Bar".
398fn split_camel_segment<'input>(camel_case: &mut &'input str) -> Option<&'input str> {
399    let index = match camel_case.chars().next() {
400        None => return None,
401        Some(ch) => ch.len_utf8(),
402    };
403    let end_position = camel_case[index..]
404        .find(char::is_uppercase)
405        .map_or(camel_case.len(), |pos| index + pos);
406    let result = &camel_case[..end_position];
407    *camel_case = &camel_case[end_position..];
408    Some(result)
409}