cg.rs (13188B)
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::{quote, TokenStreamExt}; 8 use syn::parse_quote; 9 use syn::{self, AngleBracketedGenericArguments, AssocType, DeriveInput, Field}; 10 use syn::{GenericArgument, GenericParam, Ident, Path}; 11 use syn::{PathArguments, PathSegment, QSelf, Type, TypeArray, TypeGroup}; 12 use syn::{TypeParam, TypeParen, TypePath, TypeSlice, TypeTuple}; 13 use syn::{Variant, WherePredicate}; 14 use 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. 35 pub(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 82 pub(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 89 pub(crate) fn fmap_match<F>(input: &DeriveInput, bind_style: BindStyle, f: F) -> TokenStream 90 where 91 F: FnMut(&BindingInfo) -> TokenStream, 92 { 93 fmap2_match(input, bind_style, f, |_| None) 94 } 95 96 pub(crate) fn fmap2_match<F, G>( 97 input: &DeriveInput, 98 bind_style: BindStyle, 99 mut f: F, 100 mut g: G, 101 ) -> TokenStream 102 where 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 131 pub(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 165 pub(crate) fn map_type_params<F>( 166 ty: &Type, 167 params: &[&TypeParam], 168 self_type: &Path, 169 f: &mut F, 170 ) -> Type 171 where 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| ¶m.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 238 fn map_type_params_in_path<F>( 239 path: &Path, 240 params: &[&TypeParam], 241 self_type: &Path, 242 f: &mut F, 243 ) -> Path 244 where 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 285 fn 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 301 pub(crate) fn parse_field_attrs<A>(field: &Field) -> A 302 where 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 311 pub(crate) fn parse_input_attrs<A>(input: &DeriveInput) -> A 312 where 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 321 pub(crate) fn parse_variant_attrs_from_ast<A>(variant: &VariantAst) -> A 322 where 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 334 pub(crate) fn parse_variant_attrs<A>(variant: &Variant) -> A 335 where 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 344 pub(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 356 pub(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 "-". 372 pub(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. 393 pub(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". 398 fn 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 }