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