Bug 1429816 - Part 1: Bump syn and quote in xpcom. r=froydnj,mystor

--HG--
extra : rebase_source : 46d9431193b07658534bd5489477aadce7398e1a
This commit is contained in:
Nika Layzell 2018-04-10 01:48:33 +02:00
Родитель 4f61ba5899
Коммит 06d6bb6d34
2 изменённых файлов: 83 добавлений и 108 удалений

Просмотреть файл

@ -7,6 +7,6 @@ authors = ["Michael Layzell <michael@thelayzells.com>"]
proc-macro = true
[dependencies]
syn = "0.11"
quote = "0.3"
syn = "0.13"
quote = "0.5"
lazy_static = "1.0"

Просмотреть файл

@ -131,6 +131,7 @@
#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;
extern crate proc_macro;
@ -144,8 +145,9 @@ use quote::{ToTokens, Tokens};
use syn::*;
use syn::punctuated::Punctuated;
use std::collections::{HashMap, HashSet};
use std::default::Default;
use std::error::Error;
/* These are the structs generated by the rust_macros.py script */
@ -174,6 +176,18 @@ struct Interface {
methods: Result<&'static [Method], &'static str>,
}
impl Interface {
fn base(&self) -> Result<Option<&'static Interface>, Box<Error>> {
Ok(if let Some(base) = self.base {
Some(*IFACES.get(base).ok_or_else(
|| format!("Base interface {} does not exist", base)
)?)
} else {
None
})
}
}
lazy_static! {
/// This item contains the information generated by the procedural macro in
/// the form of a `HashMap` from interface names to their descriptions.
@ -211,11 +225,17 @@ impl ToTokens for RefcntKind {
/// Scans through the attributes on a struct, and extracts the type of the refcount to use.
fn get_refcnt_kind(attrs: &[Attribute]) -> Result<RefcntKind, Box<Error>> {
for attr in attrs {
if let MetaItem::NameValue(ref name, Lit::Str(ref value, _)) = attr.value {
if name != "refcnt" {
if let Some(Meta::NameValue(ref attr)) = attr.interpret_meta() {
if attr.ident.as_ref() != "refcnt" {
continue;
}
let value = if let Lit::Str(ref s) = attr.lit {
s.value()
} else {
Err("Unexpected non-string value in #[refcnt]")?
};
return if value == "nonatomic" {
Ok(RefcntKind::NonAtomic)
} else if value == "atomic" {
@ -227,26 +247,30 @@ fn get_refcnt_kind(attrs: &[Attribute]) -> Result<RefcntKind, Box<Error>> {
}
}
Err("Expected #[refcnt] attribute".into())
Err("Expected #[refcnt] attribute")?
}
/// Scan the attributes looking for an #[xpimplements] attribute. The identifier
/// arguments passed to this attribute are the interfaces which the type wants to
/// directly implement.
fn get_bases(attrs: &[Attribute]) -> Result<Vec<&str>, Box<Error>> {
fn get_bases(attrs: &[Attribute]) -> Result<Vec<&'static Interface>, Box<Error>> {
let mut inherits = Vec::new();
for attr in attrs {
if let MetaItem::List(ref name, ref items) = attr.value {
if name != "xpimplements" {
if let Some(Meta::List(ref attr)) = attr.interpret_meta() {
if attr.ident.as_ref() != "xpimplements" {
continue;
}
for item in items {
if let NestedMetaItem::MetaItem(MetaItem::Word(ref iface)) = *item {
inherits.push(iface.as_ref());
for item in &attr.nested {
if let NestedMeta::Meta(Meta::Word(ref iface)) = *item {
if let Some(&iface) = IFACES.get(iface.as_ref()) {
inherits.push(iface);
} else {
Err(format!("Unexpected invalid base interface `{}` in \
#[xpimplements(..)]", iface))?
}
} else {
return Err("Unexpected non-identifier in xpimplements \
attribute list".into());
Err("Unexpected non-identifier in #[xpimplements(..)]")?
}
}
}
@ -255,82 +279,41 @@ fn get_bases(attrs: &[Attribute]) -> Result<Vec<&str>, Box<Error>> {
}
/// Extract the fields list from the input struct.
fn get_fields(di: &DeriveInput) -> Result<&[Field], Box<Error>> {
match di.body {
Body::Struct(VariantData::Struct(ref fields)) => Ok(fields),
fn get_fields(di: &DeriveInput) -> Result<&Punctuated<Field, Token![,]>, Box<Error>> {
match di.data {
Data::Struct(DataStruct {
fields: Fields::Named(ref named), ..
}) => Ok(&named.named),
_ => Err("The initializer struct must be a standard \
named value struct definition".into())
}
}
/// Helper function for building a `syn::Ty` from a list of path segment.s
fn mk_path_ty(segments: &[&str]) -> Ty {
Ty::Path(None, Path {
global: true,
segments: segments.iter().map(|&seg| {
PathSegment {
ident: seg.into(),
parameters: PathParameters::none(),
}
}).collect()
})
}
/// Takes the `Init*` struct in, and generates a `DeriveInput` for the "real" struct.
fn gen_real_struct(init: &DeriveInput, bases: &[&str], refcnt_ty: RefcntKind) -> Result<DeriveInput, Box<Error>> {
fn gen_real_struct(init: &DeriveInput, bases: &[&Interface], refcnt_ty: RefcntKind) -> Result<DeriveInput, Box<Error>> {
// Determine the name for the real struct based on the name of the
// initializer struct's name.
if !init.ident.as_ref().starts_with("Init") {
return Err("The target struct's name must begin with Init".into());
Err("The target struct's name must begin with Init")?
}
let name: Ident = init.ident.as_ref()[4..].into();
let vis = &init.vis;
// Add the vtable and refcnt fields to the struct declaration.
let mut fields = vec![];
for base in bases {
fields.push(Field {
ident: Some(format!("__base_{}", base).into()),
vis: Visibility::Inherited,
attrs: vec![],
ty: Ty::Ptr(
Box::new(MutTy {
ty: mk_path_ty(&["xpcom", "interfaces", &format!("{}VTable", base)]),
mutability: Mutability::Immutable,
})
),
});
}
let bases = bases.iter().map(|base| {
let ident = Ident::from(format!("__base_{}", base.name));
let vtable = Ident::from(format!("{}VTable", base.name));
quote!(#ident : *const xpcom::interfaces::#vtable)
});
fields.push(Field {
ident: Some("__refcnt".into()),
vis: Visibility::Inherited,
attrs: vec![],
ty: syn::parse_type(quote!(#refcnt_ty).as_ref())?,
});
// Add the data fields from the initializer to the struct declaration.
fields.extend(get_fields(init)?.iter().cloned());
// Create the real struct definition
Ok(DeriveInput {
ident: name,
vis: init.vis.clone(),
attrs: vec![
// #[repr(C)]
Attribute {
style: AttrStyle::Outer,
value: MetaItem::List(
"repr".into(),
vec![NestedMetaItem::MetaItem(
MetaItem::Word("C".into())
)],
),
is_sugared_doc: false,
}
],
generics: Generics::default(),
body: Body::Struct(VariantData::Struct(fields)),
})
let fields = get_fields(init)?;
Ok(parse_quote! {
#[repr(C)]
#vis struct #name {
#(#bases,)*
__refcnt: #refcnt_ty,
#fields
}
})
}
/// Generates the `extern "system"` methods which are actually included in the
@ -339,13 +322,10 @@ fn gen_real_struct(init: &DeriveInput, bases: &[&str], refcnt_ty: RefcntKind) ->
/// These methods attempt to invoke the `recover_self` method to translate from
/// the passed-in raw pointer to the actual `&self` value, and it is expected to
/// be in scope.
fn gen_vtable_methods(base: &str) -> Result<Tokens, Box<Error>> {
let base_ty = Ident::from(base);
fn gen_vtable_methods(iface: &Interface) -> Result<Tokens, Box<Error>> {
let base_ty = Ident::from(iface.name);
let iface = IFACES.get(base)
.ok_or(format!("Interface {} does not exist", base))?;
let base_methods = if let Some(base) = iface.base {
let base_methods = if let Some(base) = iface.base()? {
gen_vtable_methods(base)?
} else {
quote!{}
@ -353,18 +333,18 @@ fn gen_vtable_methods(base: &str) -> Result<Tokens, Box<Error>> {
let methods = iface.methods
.map_err(|reason| format!("Interface {} cannot be implemented in rust \
because {} is not supported yet", base, reason))?;
because {} is not supported yet", iface.name, reason))?;
let mut method_defs = Vec::new();
for method in methods {
let name = Ident::from(method.name);
let ret = Ident::from(method.ret);
let ret = syn::parse_str::<Type>(method.ret)?;
let mut params = Vec::new();
let mut args = Vec::new();
for param in method.params {
let name = Ident::from(param.name);
let ty = Ident::from(param.ty);
let ty = syn::parse_str::<Type>(param.ty)?;
params.push(quote!{#name : #ty,});
args.push(quote!{#name,});
@ -386,18 +366,15 @@ fn gen_vtable_methods(base: &str) -> Result<Tokens, Box<Error>> {
/// Generates the VTable for a given base interface. This assumes that the
/// implementations of each of the `extern "system"` methods are in scope.
fn gen_inner_vtable(base: &str) -> Result<Tokens, Box<Error>> {
let vtable_ty = Ident::from(format!("{}VTable", base));
let iface = IFACES.get(base)
.ok_or(format!("Interface {} does not exist", base))?;
fn gen_inner_vtable(iface: &Interface) -> Result<Tokens, Box<Error>> {
let vtable_ty = Ident::from(format!("{}VTable", iface.name));
let methods = iface.methods
.map_err(|reason| format!("Interface {} cannot be implemented in rust \
because {} is not supported yet", base, reason))?;
because {} is not supported yet", iface.name, reason))?;
// Generate the vtable for the base interface.
let base_vtable = if let Some(base) = iface.base {
let base_vtable = if let Some(base) = iface.base()? {
let vt = gen_inner_vtable(base)?;
quote!{__base: #vt,}
} else {
@ -416,9 +393,9 @@ fn gen_inner_vtable(base: &str) -> Result<Tokens, Box<Error>> {
}))
}
fn gen_root_vtable(name: &Ident, base: &str) -> Result<Tokens, Box<Error>> {
let field = Ident::from(format!("__base_{}", base));
let vtable_ty = Ident::from(format!("{}VTable", base));
fn gen_root_vtable(name: &Ident, base: &Interface) -> Result<Tokens, Box<Error>> {
let field = Ident::from(format!("__base_{}", base.name));
let vtable_ty = Ident::from(format!("{}VTable", base.name));
let methods = gen_vtable_methods(base)?;
let value = gen_inner_vtable(base)?;
@ -458,18 +435,18 @@ fn gen_root_vtable(name: &Ident, base: &str) -> Result<Tokens, Box<Error>> {
/// value is the `QueryInterface` implementation, and the second is the `Coerce`
/// implementation.
fn gen_casts(
seen: &mut HashSet<String>,
base: &str,
seen: &mut HashSet<&'static str>,
iface: &Interface,
name: &Ident,
coerce_name: &Ident,
vtable_field: &Ident,
) -> Result<(Tokens, Tokens), Box<Error>> {
if !seen.insert(base.to_owned()) {
if !seen.insert(iface.name) {
return Ok((quote!{}, quote!{}));
}
// Generate the cast implementations for the base interfaces.
let (base_qi, base_coerce) = if let Some(base) = IFACES[base].base {
let (base_qi, base_coerce) = if let Some(base) = iface.base()? {
gen_casts(
seen,
base,
@ -482,7 +459,7 @@ fn gen_casts(
};
// Add the if statment to QueryInterface for the base class.
let base_name = Ident::from(base);
let base_name = Ident::from(iface.name);
let qi = quote! {
#base_qi
@ -519,9 +496,8 @@ fn gen_casts(
}
/// The root xpcom procedural macro definition.
fn xpcom(input: &str) -> Result<Tokens, Box<Error>> {
let init = syn::parse_derive_input(input)?;
if init.generics != Generics::default() {
fn xpcom(init: DeriveInput) -> Result<Tokens, Box<Error>> {
if !init.generics.params.is_empty() || !init.generics.where_clause.is_none() {
return Err("Cannot #[derive(xpcom)] on a generic type, due to \
rust limitations. It is not possible to instantiate \
a static with a generic type parameter, meaning that \
@ -570,7 +546,7 @@ fn xpcom(input: &str) -> Result<Tokens, Box<Error>> {
base,
name,
&coerce_name,
&Ident::from(format!("__base_{}", base)),
&Ident::from(format!("__base_{}", base.name)),
)?;
qi_impl.push(qi);
coerce_impl.push(coerce);
@ -689,7 +665,6 @@ fn xpcom(input: &str) -> Result<Tokens, Box<Error>> {
#[proc_macro_derive(xpcom, attributes(xpimplements, refcnt))]
pub fn xpcom_internal(input: TokenStream) -> TokenStream {
let source = input.to_string();
let out_src = xpcom(&source).unwrap().to_string();
out_src.parse().unwrap()
xpcom(parse(input).expect("Invalid derive input"))
.expect("#[derive(xpcom)] failed").into()
}