Bug 1429816 - Part 1: Bump syn and quote in xpcom. r?mystor,froydnj draft
authorNika Layzell <nika@thelayzells.com>
Tue, 10 Apr 2018 01:48:33 +0200
changeset 779868 82229ed6642ef13ba86a634b49a6c0dabdf5119a
parent 779456 1b91e11c60912e9d1e944645dbff0d522cd6ac8c
child 779869 df3ca2484842769824e9c16360648d8505e37c01
push id105898
push userbmo:eijebong@bananium.fr
push dateTue, 10 Apr 2018 18:47:58 +0000
reviewersmystor, froydnj
bugs1429816
milestone61.0a1
Bug 1429816 - Part 1: Bump syn and quote in xpcom. r?mystor,froydnj
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()
 }