mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
style: Factor out some of style_derive.
Differential Revision: https://phabricator.services.mozilla.com/D17188
This commit is contained in:
parent
f581d2afb2
commit
d8a758272e
13 changed files with 40 additions and 9 deletions
350
components/derive_common/cg.rs
Normal file
350
components/derive_common/cg.rs
Normal file
|
@ -0,0 +1,350 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use darling::{FromDeriveInput, FromField, FromVariant};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::TokenStreamExt;
|
||||
use syn::{self, AngleBracketedGenericArguments, Binding, DeriveInput, Field};
|
||||
use syn::{GenericArgument, GenericParam, Ident, Path};
|
||||
use syn::{PathArguments, PathSegment, QSelf, Type, TypeArray};
|
||||
use syn::{TypeParam, TypeParen, TypePath, TypeSlice, TypeTuple};
|
||||
use syn::{Variant, WherePredicate};
|
||||
use synstructure::{self, BindStyle, BindingInfo, VariantAst, VariantInfo};
|
||||
|
||||
/// Given an input type which has some where clauses already, like:
|
||||
///
|
||||
/// struct InputType<T>
|
||||
/// where
|
||||
/// T: Zero,
|
||||
/// {
|
||||
/// ...
|
||||
/// }
|
||||
///
|
||||
/// Add the necessary `where` clauses so that the output type of a trait
|
||||
/// fulfils them.
|
||||
///
|
||||
/// For example:
|
||||
///
|
||||
/// <T as ToComputedValue>::ComputedValue: Zero,
|
||||
///
|
||||
/// This needs to run before adding other bounds to the type parameters.
|
||||
pub fn propagate_clauses_to_output_type(
|
||||
where_clause: &mut Option<syn::WhereClause>,
|
||||
generics: &syn::Generics,
|
||||
trait_path: Path,
|
||||
trait_output: Ident,
|
||||
) {
|
||||
let where_clause = match *where_clause {
|
||||
Some(ref mut clause) => clause,
|
||||
None => return,
|
||||
};
|
||||
let mut extra_bounds = vec![];
|
||||
for pred in &where_clause.predicates {
|
||||
let ty = match *pred {
|
||||
syn::WherePredicate::Type(ref ty) => ty,
|
||||
ref predicate => panic!("Unhanded complex where predicate: {:?}", predicate),
|
||||
};
|
||||
|
||||
let path = match ty.bounded_ty {
|
||||
syn::Type::Path(ref p) => &p.path,
|
||||
ref ty => panic!("Unhanded complex where type: {:?}", ty),
|
||||
};
|
||||
|
||||
assert!(
|
||||
ty.lifetimes.is_none(),
|
||||
"Unhanded complex lifetime bound: {:?}",
|
||||
ty,
|
||||
);
|
||||
|
||||
let ident = match path_to_ident(path) {
|
||||
Some(i) => i,
|
||||
None => panic!("Unhanded complex where type path: {:?}", path),
|
||||
};
|
||||
|
||||
if generics.type_params().any(|param| param.ident == *ident) {
|
||||
extra_bounds.push(ty.clone());
|
||||
}
|
||||
}
|
||||
|
||||
for bound in extra_bounds {
|
||||
let ty = bound.bounded_ty;
|
||||
let bounds = bound.bounds;
|
||||
where_clause
|
||||
.predicates
|
||||
.push(parse_quote!(<#ty as #trait_path>::#trait_output: #bounds))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_predicate(where_clause: &mut Option<syn::WhereClause>, pred: WherePredicate) {
|
||||
where_clause
|
||||
.get_or_insert(parse_quote!(where))
|
||||
.predicates
|
||||
.push(pred);
|
||||
}
|
||||
|
||||
pub fn fmap_match<F>(input: &DeriveInput, bind_style: BindStyle, mut f: F) -> TokenStream
|
||||
where
|
||||
F: FnMut(BindingInfo) -> TokenStream,
|
||||
{
|
||||
let mut s = synstructure::Structure::new(input);
|
||||
s.variants_mut().iter_mut().for_each(|v| {
|
||||
v.bind_with(|_| bind_style);
|
||||
});
|
||||
s.each_variant(|variant| {
|
||||
let (mapped, mapped_fields) = value(variant, "mapped");
|
||||
let fields_pairs = variant.bindings().iter().zip(mapped_fields);
|
||||
let mut computations = quote!();
|
||||
computations.append_all(fields_pairs.map(|(field, mapped_field)| {
|
||||
let expr = f(field.clone());
|
||||
quote! { let #mapped_field = #expr; }
|
||||
}));
|
||||
computations.append_all(mapped);
|
||||
Some(computations)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn fmap_trait_output(input: &DeriveInput, trait_path: &Path, trait_output: Ident) -> Path {
|
||||
let segment = PathSegment {
|
||||
ident: input.ident.clone(),
|
||||
arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments {
|
||||
args: input
|
||||
.generics
|
||||
.params
|
||||
.iter()
|
||||
.map(|arg| match arg {
|
||||
&GenericParam::Lifetime(ref data) => {
|
||||
GenericArgument::Lifetime(data.lifetime.clone())
|
||||
},
|
||||
&GenericParam::Type(ref data) => {
|
||||
let ident = &data.ident;
|
||||
GenericArgument::Type(parse_quote!(<#ident as #trait_path>::#trait_output))
|
||||
},
|
||||
ref arg => panic!("arguments {:?} cannot be mapped yet", arg),
|
||||
})
|
||||
.collect(),
|
||||
colon2_token: Default::default(),
|
||||
gt_token: Default::default(),
|
||||
lt_token: Default::default(),
|
||||
}),
|
||||
};
|
||||
segment.into()
|
||||
}
|
||||
|
||||
pub fn map_type_params<F>(ty: &Type, params: &[&TypeParam], f: &mut F) -> Type
|
||||
where
|
||||
F: FnMut(&Ident) -> Type,
|
||||
{
|
||||
match *ty {
|
||||
Type::Slice(ref inner) => Type::from(TypeSlice {
|
||||
elem: Box::new(map_type_params(&inner.elem, params, f)),
|
||||
..inner.clone()
|
||||
}),
|
||||
Type::Array(ref inner) => {
|
||||
//ref ty, ref expr) => {
|
||||
Type::from(TypeArray {
|
||||
elem: Box::new(map_type_params(&inner.elem, params, f)),
|
||||
..inner.clone()
|
||||
})
|
||||
},
|
||||
ref ty @ Type::Never(_) => ty.clone(),
|
||||
Type::Tuple(ref inner) => Type::from(TypeTuple {
|
||||
elems: inner
|
||||
.elems
|
||||
.iter()
|
||||
.map(|ty| map_type_params(&ty, params, f))
|
||||
.collect(),
|
||||
..inner.clone()
|
||||
}),
|
||||
Type::Path(TypePath {
|
||||
qself: None,
|
||||
ref path,
|
||||
}) => {
|
||||
if let Some(ident) = path_to_ident(path) {
|
||||
if params.iter().any(|ref param| ¶m.ident == ident) {
|
||||
return f(ident);
|
||||
}
|
||||
}
|
||||
Type::from(TypePath {
|
||||
qself: None,
|
||||
path: map_type_params_in_path(path, params, f),
|
||||
})
|
||||
},
|
||||
Type::Path(TypePath {
|
||||
ref qself,
|
||||
ref path,
|
||||
}) => Type::from(TypePath {
|
||||
qself: qself.as_ref().map(|qself| QSelf {
|
||||
ty: Box::new(map_type_params(&qself.ty, params, f)),
|
||||
position: qself.position,
|
||||
..qself.clone()
|
||||
}),
|
||||
path: map_type_params_in_path(path, params, f),
|
||||
}),
|
||||
Type::Paren(ref inner) => Type::from(TypeParen {
|
||||
elem: Box::new(map_type_params(&inner.elem, params, f)),
|
||||
..inner.clone()
|
||||
}),
|
||||
ref ty => panic!("type {:?} cannot be mapped yet", ty),
|
||||
}
|
||||
}
|
||||
|
||||
fn map_type_params_in_path<F>(path: &Path, params: &[&TypeParam], f: &mut F) -> Path
|
||||
where
|
||||
F: FnMut(&Ident) -> Type,
|
||||
{
|
||||
Path {
|
||||
leading_colon: path.leading_colon,
|
||||
segments: path
|
||||
.segments
|
||||
.iter()
|
||||
.map(|segment| PathSegment {
|
||||
ident: segment.ident.clone(),
|
||||
arguments: match segment.arguments {
|
||||
PathArguments::AngleBracketed(ref data) => {
|
||||
PathArguments::AngleBracketed(AngleBracketedGenericArguments {
|
||||
args: data
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| match arg {
|
||||
ty @ &GenericArgument::Lifetime(_) => ty.clone(),
|
||||
&GenericArgument::Type(ref data) => {
|
||||
GenericArgument::Type(map_type_params(data, params, f))
|
||||
},
|
||||
&GenericArgument::Binding(ref data) => {
|
||||
GenericArgument::Binding(Binding {
|
||||
ty: map_type_params(&data.ty, params, f),
|
||||
..data.clone()
|
||||
})
|
||||
},
|
||||
ref arg => panic!("arguments {:?} cannot be mapped yet", arg),
|
||||
})
|
||||
.collect(),
|
||||
..data.clone()
|
||||
})
|
||||
},
|
||||
ref arg @ PathArguments::None => arg.clone(),
|
||||
ref parameters => panic!("parameters {:?} cannot be mapped yet", parameters),
|
||||
},
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn path_to_ident(path: &Path) -> Option<&Ident> {
|
||||
match *path {
|
||||
Path {
|
||||
leading_colon: None,
|
||||
ref segments,
|
||||
} if segments.len() == 1 => {
|
||||
if segments[0].arguments.is_empty() {
|
||||
Some(&segments[0].ident)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_field_attrs<A>(field: &Field) -> A
|
||||
where
|
||||
A: FromField,
|
||||
{
|
||||
match A::from_field(field) {
|
||||
Ok(attrs) => attrs,
|
||||
Err(e) => panic!("failed to parse field attributes: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_input_attrs<A>(input: &DeriveInput) -> A
|
||||
where
|
||||
A: FromDeriveInput,
|
||||
{
|
||||
match A::from_derive_input(input) {
|
||||
Ok(attrs) => attrs,
|
||||
Err(e) => panic!("failed to parse input attributes: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_variant_attrs_from_ast<A>(variant: &VariantAst) -> A
|
||||
where
|
||||
A: FromVariant,
|
||||
{
|
||||
let v = Variant {
|
||||
ident: variant.ident.clone(),
|
||||
attrs: variant.attrs.to_vec(),
|
||||
fields: variant.fields.clone(),
|
||||
discriminant: variant.discriminant.clone(),
|
||||
};
|
||||
parse_variant_attrs(&v)
|
||||
}
|
||||
|
||||
pub fn parse_variant_attrs<A>(variant: &Variant) -> A
|
||||
where
|
||||
A: FromVariant,
|
||||
{
|
||||
match A::from_variant(variant) {
|
||||
Ok(attrs) => attrs,
|
||||
Err(e) => panic!("failed to parse variant attributes: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ref_pattern<'a>(
|
||||
variant: &'a VariantInfo,
|
||||
prefix: &str,
|
||||
) -> (TokenStream, Vec<BindingInfo<'a>>) {
|
||||
let mut v = variant.clone();
|
||||
v.bind_with(|_| BindStyle::Ref);
|
||||
v.bindings_mut().iter_mut().for_each(|b| {
|
||||
b.binding = Ident::new(&format!("{}_{}", b.binding, prefix), Span::call_site())
|
||||
});
|
||||
(v.pat(), v.bindings().to_vec())
|
||||
}
|
||||
|
||||
pub fn value<'a>(variant: &'a VariantInfo, prefix: &str) -> (TokenStream, Vec<BindingInfo<'a>>) {
|
||||
let mut v = variant.clone();
|
||||
v.bindings_mut().iter_mut().for_each(|b| {
|
||||
b.binding = Ident::new(&format!("{}_{}", b.binding, prefix), Span::call_site())
|
||||
});
|
||||
v.bind_with(|_| BindStyle::Move);
|
||||
(v.pat(), v.bindings().to_vec())
|
||||
}
|
||||
|
||||
/// Transforms "FooBar" to "foo-bar".
|
||||
///
|
||||
/// If the first Camel segment is "Moz", "Webkit", or "Servo", the result string
|
||||
/// is prepended with "-".
|
||||
pub fn to_css_identifier(mut camel_case: &str) -> String {
|
||||
camel_case = camel_case.trim_end_matches('_');
|
||||
let mut first = true;
|
||||
let mut result = String::with_capacity(camel_case.len());
|
||||
while let Some(segment) = split_camel_segment(&mut camel_case) {
|
||||
if first {
|
||||
match segment {
|
||||
"Moz" | "Webkit" | "Servo" => first = false,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
if !first {
|
||||
result.push_str("-");
|
||||
}
|
||||
first = false;
|
||||
result.push_str(&segment.to_lowercase());
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Given "FooBar", returns "Foo" and sets `camel_case` to "Bar".
|
||||
fn split_camel_segment<'input>(camel_case: &mut &'input str) -> Option<&'input str> {
|
||||
let index = match camel_case.chars().next() {
|
||||
None => return None,
|
||||
Some(ch) => ch.len_utf8(),
|
||||
};
|
||||
let end_position = camel_case[index..]
|
||||
.find(char::is_uppercase)
|
||||
.map_or(camel_case.len(), |pos| index + pos);
|
||||
let result = &camel_case[..end_position];
|
||||
*camel_case = &camel_case[end_position..];
|
||||
Some(result)
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue