Bug 1429816 - Part 1: Bump syn and quote in xpcom. r=froydnj,mystor
authorNika Layzell <nika@thelayzells.com>
Tue, 10 Apr 2018 01:48:33 +0200
changeset 412693 5fa8c1abc6b6ae913af02e9c5e00e5ab54c85fc7
parent 412692 60de60fe27339fc2dcf5f47a31a68c7e6bdd58b0
child 412694 8ef553153fc05f7e72bac8df0cc71d909c1578d6
push id101981
push useraiakab@mozilla.com
push dateTue, 10 Apr 2018 22:18:59 +0000
treeherdermozilla-inbound@9ad2b8aabfae [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj, mystor
bugs1429816
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1429816 - Part 1: Bump syn and quote in xpcom. r=froydnj,mystor
xpcom/rust/xpcom/xpcom_macros/Cargo.toml
xpcom/rust/xpcom/xpcom_macros/src/lib.rs
--- a/xpcom/rust/xpcom/xpcom_macros/Cargo.toml
+++ b/xpcom/rust/xpcom/xpcom_macros/Cargo.toml
@@ -2,11 +2,11 @@
 name = "xpcom_macros"
 version = "0.1.0"
 authors = ["Michael Layzell <michael@thelayzells.com>"]
 
 [lib]
 proc-macro = true
 
 [dependencies]
-syn = "0.11"
-quote = "0.3"
+syn = "0.13"
+quote = "0.5"
 lazy_static = "1.0"
--- a/xpcom/rust/xpcom/xpcom_macros/src/lib.rs
+++ b/xpcom/rust/xpcom/xpcom_macros/src/lib.rs
@@ -126,31 +126,33 @@
 
 // NOTE: We use some really big quote! invocations, so we need a high recursion
 // limit.
 #![recursion_limit="256"]
 
 #[macro_use]
 extern crate quote;
 
+#[macro_use]
 extern crate syn;
 
 extern crate proc_macro;
 
 #[macro_use]
 extern crate lazy_static;
 
 use proc_macro::TokenStream;
 
 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 */
 
 /// A single parameter to an XPCOM method.
 #[derive(Debug)]
 struct Param {
     name: &'static str,
@@ -169,16 +171,28 @@ struct Method {
 /// cannot be implemented in rust code.
 #[derive(Debug)]
 struct Interface {
     name: &'static str,
     base: Option<&'static str>,
     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.
     static ref IFACES: HashMap<&'static str, &'static Interface> = {
         let lists: &[&[Interface]] =
             include!(concat!(env!("MOZ_TOPOBJDIR"), "/dist/xpcrs/bt/all.rs"));
 
         let mut hm = HashMap::new();
@@ -206,170 +220,136 @@ impl ToTokens for RefcntKind {
             RefcntKind::Atomic => quote!(xpcom::AtomicRefcnt).to_tokens(tokens),
         }
     }
 }
 
 /// 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" {
                 Ok(RefcntKind::Atomic)
             } else {
                 Err("Unexpected value in #[refcnt]. \
                      Expected `nonatomic`, or `atomic`")?
             };
         }
     }
 
-    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(..)]")?
                 }
             }
         }
     }
     Ok(inherits)
 }
 
 /// 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,
-                })
-            ),
-        });
-    }
-
-    fields.push(Field {
-        ident: Some("__refcnt".into()),
-        vis: Visibility::Inherited,
-        attrs: vec![],
-        ty: syn::parse_type(quote!(#refcnt_ty).as_ref())?,
-    });
+    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)
+     });
 
-    // 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
 /// VTable for the given interface.
 ///
 /// 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!{}
     };
 
     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,});
         }
 
         method_defs.push(quote!{
             unsafe extern "system" fn #name (this: *const #base_ty, #(#params)*) -> #ret {
                 let lt = ();
@@ -381,28 +361,25 @@ fn gen_vtable_methods(base: &str) -> Res
     Ok(quote!{
         #base_methods
         #(#method_defs)*
     })
 }
 
 /// 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 {
         quote!{}
     };
 
     // Include each of the method definitions for this interface.
     let vtable_init = methods.into_iter().map(|method| {
@@ -411,19 +388,19 @@ fn gen_inner_vtable(base: &str) -> Resul
     }).collect::<Vec<_>>();
 
     Ok(quote!(#vtable_ty {
         #base_vtable
         #(#vtable_init)*
     }))
 }
 
-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)?;
 
     // Define the `recover_self` method. This performs an offset calculation to
     // recover a pointer to the original struct from a pointer to the given
     // VTable field.
     Ok(quote!{#field: {
         // NOTE: The &'a () dummy lifetime parameter is useful as it easily
@@ -453,41 +430,41 @@ fn gen_root_vtable(name: &Ident, base: &
     },})
 }
 
 /// Generate the cast implementations. This generates the implementation details
 /// for the `Coerce` trait, and the `QueryInterface` method. The first return
 /// 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,
             name,
             coerce_name,
             vtable_field,
         )?
     } else {
         (quote!{}, quote!{})
     };
 
     // 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
         if *uuid == #base_name::IID {
             // Implement QueryInterface in terms of coersions.
             self.addref();
             *result = self.coerce::<#base_name>()
                 as *const #base_name
@@ -514,19 +491,18 @@ fn gen_casts(
             }
         }
     };
 
     Ok((qi, coerce))
 }
 
 /// 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 \
                     generic types cannot have their VTables instantiated \
                     correctly.".into());
     }
 
     let bases = get_bases(&init.attrs)?;
@@ -565,17 +541,17 @@ fn xpcom(input: &str) -> Result<Tokens, 
     let mut qi_impl = Vec::new();
     let mut coerce_impl = Vec::new();
     for base in &bases {
         let (qi, coerce) = gen_casts(
             &mut seen,
             base,
             name,
             &coerce_name,
-            &Ident::from(format!("__base_{}", base)),
+            &Ident::from(format!("__base_{}", base.name)),
         )?;
         qi_impl.push(qi);
         coerce_impl.push(coerce);
     }
 
     Ok(quote! {
         #real
 
@@ -684,12 +660,11 @@ fn xpcom(input: &str) -> Result<Tokens, 
                 self.Release();
             }
         }
     })
 }
 
 #[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()
 }