diff --git a/src/bindgen/parser.rs b/src/bindgen/parser.rs index 891580e..4118f10 100644 --- a/src/bindgen/parser.rs +++ b/src/bindgen/parser.rs @@ -18,7 +18,7 @@ use crate::bindgen::ir::{ AnnotationSet, Cfg, Constant, Documentation, Enum, Function, GenericParams, ItemMap, OpaqueItem, Path, Static, Struct, Type, Typedef, Union, }; -use crate::bindgen::utilities::{SynAbiHelpers, SynItemFnHelpers, SynItemHelpers}; +use crate::bindgen::utilities::{SynAbiHelpers, SynAttributeHelpers, SynItemFnHelpers}; const STD_CRATES: &[&str] = &[ "std", diff --git a/src/bindgen/utilities.rs b/src/bindgen/utilities.rs index c29afa9..f2bf116 100644 --- a/src/bindgen/utilities.rs +++ b/src/bindgen/utilities.rs @@ -39,7 +39,7 @@ pub fn find_first_some(slice: &[Option]) -> Option<&T> { None } -pub trait SynItemFnHelpers: SynItemHelpers { +pub trait SynItemFnHelpers: SynAttributeHelpers { fn exported_name(&self) -> Option; } @@ -71,16 +71,52 @@ impl SynItemFnHelpers for syn::ImplItemMethod { } } -pub trait SynItemHelpers { +pub trait SynAttributeHelpers { + /// Returns the list of attributes for an item. + fn attrs(&self) -> &[syn::Attribute]; + /// Searches for attributes like `#[test]`. /// Example: /// - `item.has_attr_word("test")` => `#[test]` - fn has_attr_word(&self, name: &str) -> bool; + fn has_attr_word(&self, name: &str) -> bool { + self.attrs() + .iter() + .filter_map(|x| x.parse_meta().ok()) + .any(|attr| { + if let syn::Meta::Path(ref path) = attr { + path.is_ident(name) + } else { + false + } + }) + } /// Searches for attributes like `#[cfg(test)]`. /// Example: /// - `item.has_attr_list("cfg", &["test"])` => `#[cfg(test)]` - fn has_attr_list(&self, name: &str, args: &[&str]) -> bool; + fn has_attr_list(&self, name: &str, args: &[&str]) -> bool { + self.attrs() + .iter() + .filter_map(|x| x.parse_meta().ok()) + .any(|attr| { + if let syn::Meta::List(syn::MetaList { path, nested, .. }) = attr { + if !path.is_ident(name) { + return false; + } + args.iter().all(|arg| { + nested.iter().any(|nested_meta| { + if let syn::NestedMeta::Meta(syn::Meta::Path(path)) = nested_meta { + path.is_ident(arg) + } else { + false + } + }) + }) + } else { + false + } + }) + } fn is_no_mangle(&self) -> bool { self.has_attr_word("no_mangle") @@ -90,58 +126,88 @@ pub trait SynItemHelpers { fn has_test_attr(&self) -> bool { self.has_attr_list("cfg", &["test"]) || self.has_attr_word("test") } + + fn attr_name_value_lookup(&self, name: &str) -> Option { + self.attrs() + .iter() + .filter_map(|attr| { + let attr = attr.parse_meta().ok()?; + if let syn::Meta::NameValue(syn::MetaNameValue { + path, + lit: syn::Lit::Str(lit), + .. + }) = attr + { + if path.is_ident(name) { + return Some(lit.value()); + } + } + None + }) + .next() + } + + fn get_comment_lines(&self) -> Vec { + let mut comment = Vec::new(); + + for attr in self.attrs() { + if attr.style == syn::AttrStyle::Outer { + if let Ok(syn::Meta::NameValue(syn::MetaNameValue { + path, + lit: syn::Lit::Str(content), + .. + })) = attr.parse_meta() + { + if path.is_ident("doc") { + comment.extend(split_doc_attr(&content.value())); + } + } + } + } + + comment + } } macro_rules! syn_item_match_helper { ($s:ident => has_attrs: |$i:ident| $a:block, otherwise: || $b:block) => { match *$s { - syn::Item::Const(ref item) => (|$i: &syn::ItemConst| $a)(item), - syn::Item::Enum(ref item) => (|$i: &syn::ItemEnum| $a)(item), - syn::Item::ExternCrate(ref item) => (|$i: &syn::ItemExternCrate| $a)(item), - syn::Item::Fn(ref item) => (|$i: &syn::ItemFn| $a)(item), - syn::Item::ForeignMod(ref item) => (|$i: &syn::ItemForeignMod| $a)(item), - syn::Item::Impl(ref item) => (|$i: &syn::ItemImpl| $a)(item), - syn::Item::Macro(ref item) => (|$i: &syn::ItemMacro| $a)(item), - syn::Item::Macro2(ref item) => (|$i: &syn::ItemMacro2| $a)(item), - syn::Item::Mod(ref item) => (|$i: &syn::ItemMod| $a)(item), - syn::Item::Static(ref item) => (|$i: &syn::ItemStatic| $a)(item), - syn::Item::Struct(ref item) => (|$i: &syn::ItemStruct| $a)(item), - syn::Item::Trait(ref item) => (|$i: &syn::ItemTrait| $a)(item), - syn::Item::Type(ref item) => (|$i: &syn::ItemType| $a)(item), - syn::Item::Union(ref item) => (|$i: &syn::ItemUnion| $a)(item), - syn::Item::Use(ref item) => (|$i: &syn::ItemUse| $a)(item), - syn::Item::TraitAlias(ref item) => (|$i: &syn::ItemTraitAlias| $a)(item), - syn::Item::Verbatim(_) => (|| $b)(), + syn::Item::Const(ref $i) => $a, + syn::Item::Enum(ref $i) => $a, + syn::Item::ExternCrate(ref $i) => $a, + syn::Item::Fn(ref $i) => $a, + syn::Item::ForeignMod(ref $i) => $a, + syn::Item::Impl(ref $i) => $a, + syn::Item::Macro(ref $i) => $a, + syn::Item::Macro2(ref $i) => $a, + syn::Item::Mod(ref $i) => $a, + syn::Item::Static(ref $i) => $a, + syn::Item::Struct(ref $i) => $a, + syn::Item::Trait(ref $i) => $a, + syn::Item::Type(ref $i) => $a, + syn::Item::Union(ref $i) => $a, + syn::Item::Use(ref $i) => $a, + syn::Item::TraitAlias(ref $i) => $a, + syn::Item::Verbatim(_) => $b, _ => panic!("Unhandled syn::Item: {:?}", $s), } }; } -impl SynItemHelpers for syn::Item { - fn has_attr_word(&self, name: &str) -> bool { +impl SynAttributeHelpers for syn::Item { + fn attrs(&self) -> &[syn::Attribute] { syn_item_match_helper!(self => - has_attrs: |item| { item.has_attr_word(name) }, - otherwise: || { false } - ) - } - - fn has_attr_list(&self, name: &str, args: &[&str]) -> bool { - syn_item_match_helper!(self => - has_attrs: |item| { item.has_attr_list(name, args) }, - otherwise: || { false } + has_attrs: |item| { &item.attrs }, + otherwise: || { &[] } ) } } macro_rules! impl_syn_item_helper { ($t:ty) => { - impl SynItemHelpers for $t { - fn has_attr_word(&self, name: &str) -> bool { - self.attrs.has_attr_word(name) - } - - fn has_attr_list(&self, name: &str, args: &[&str]) -> bool { - self.attrs.has_attr_list(name, args) + impl SynAttributeHelpers for $t { + fn attrs(&self) -> &[syn::Attribute] { + &self.attrs } } }; @@ -202,83 +268,9 @@ impl SynAbiHelpers for syn::Abi { } } -pub trait SynAttributeHelpers { - fn get_comment_lines(&self) -> Vec; - fn has_attr_word(&self, name: &str) -> bool; - fn has_attr_list(&self, name: &str, args: &[&str]) -> bool; - fn attr_name_value_lookup(&self, name: &str) -> Option; -} - impl SynAttributeHelpers for [syn::Attribute] { - fn has_attr_word(&self, name: &str) -> bool { - self.iter().filter_map(|x| x.parse_meta().ok()).any(|attr| { - if let syn::Meta::Path(ref path) = attr { - path.is_ident(name) - } else { - false - } - }) - } - - fn has_attr_list(&self, name: &str, args: &[&str]) -> bool { - self.iter().filter_map(|x| x.parse_meta().ok()).any(|attr| { - if let syn::Meta::List(syn::MetaList { path, nested, .. }) = attr { - if !path.is_ident(name) { - return false; - } - args.iter().all(|arg| { - nested.iter().any(|nested_meta| { - if let syn::NestedMeta::Meta(syn::Meta::Path(path)) = nested_meta { - path.is_ident(arg) - } else { - false - } - }) - }) - } else { - false - } - }) - } - - fn attr_name_value_lookup(&self, name: &str) -> Option { - self.iter() - .filter_map(|attr| { - let attr = attr.parse_meta().ok()?; - if let syn::Meta::NameValue(syn::MetaNameValue { - path, - lit: syn::Lit::Str(lit), - .. - }) = attr - { - if path.is_ident(name) { - return Some(lit.value()); - } - } - None - }) - .next() - } - - fn get_comment_lines(&self) -> Vec { - let mut comment = Vec::new(); - - for attr in self { - if attr.style == syn::AttrStyle::Outer { - if let Ok(syn::Meta::NameValue(syn::MetaNameValue { - path, - lit: syn::Lit::Str(content), - .. - })) = attr.parse_meta() - { - if path.is_ident("doc") { - comment.extend(split_doc_attr(&content.value())); - } - } - } - } - - comment + fn attrs(&self) -> &[syn::Attribute] { + self } }