294 lines
8.5 KiB
Rust
294 lines
8.5 KiB
Rust
/* 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 http://mozilla.org/MPL/2.0/. */
|
|
|
|
#![allow(clippy::redundant_closure_call)]
|
|
|
|
pub trait IterHelpers: Iterator {
|
|
fn try_skip_map<F, T, E>(&mut self, f: F) -> Result<Vec<T>, E>
|
|
where
|
|
F: FnMut(&Self::Item) -> Result<Option<T>, E>;
|
|
}
|
|
|
|
impl<I> IterHelpers for I
|
|
where
|
|
I: Iterator,
|
|
{
|
|
fn try_skip_map<F, T, E>(&mut self, mut f: F) -> Result<Vec<T>, E>
|
|
where
|
|
F: FnMut(&Self::Item) -> Result<Option<T>, E>,
|
|
{
|
|
let mut out = Vec::new();
|
|
for item in self {
|
|
if let Some(x) = f(&item)? {
|
|
out.push(x);
|
|
}
|
|
}
|
|
Ok(out)
|
|
}
|
|
}
|
|
|
|
pub trait SynItemFnHelpers: SynAttributeHelpers {
|
|
fn exported_name(&self) -> Option<String>;
|
|
}
|
|
|
|
impl SynItemFnHelpers for syn::ItemFn {
|
|
fn exported_name(&self) -> Option<String> {
|
|
self.attrs
|
|
.attr_name_value_lookup("export_name")
|
|
.or_else(|| {
|
|
if self.is_no_mangle() {
|
|
Some(self.sig.ident.to_string())
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
impl SynItemFnHelpers for syn::ImplItemMethod {
|
|
fn exported_name(&self) -> Option<String> {
|
|
self.attrs
|
|
.attr_name_value_lookup("export_name")
|
|
.or_else(|| {
|
|
if self.is_no_mangle() {
|
|
Some(self.sig.ident.to_string())
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
/// Returns whether this attribute causes us to skip at item. This basically
|
|
/// checks for `#[cfg(test)]`, `#[test]`, `/// cbindgen::ignore` and
|
|
/// variations thereof.
|
|
fn is_skip_item_attr(attr: &syn::Meta) -> bool {
|
|
match *attr {
|
|
syn::Meta::Path(ref path) => {
|
|
// TODO(emilio): It'd be great if rustc allowed us to use a syntax
|
|
// like `#[cbindgen::ignore]` or such.
|
|
path.is_ident("test")
|
|
}
|
|
syn::Meta::List(ref list) => {
|
|
if !list.path.is_ident("cfg") {
|
|
return false;
|
|
}
|
|
list.nested.iter().any(|nested| match *nested {
|
|
syn::NestedMeta::Meta(ref meta) => is_skip_item_attr(meta),
|
|
syn::NestedMeta::Lit(..) => false,
|
|
})
|
|
}
|
|
syn::Meta::NameValue(ref name_value) => {
|
|
if name_value.path.is_ident("doc") {
|
|
if let syn::Lit::Str(ref content) = name_value.lit {
|
|
// FIXME(emilio): Maybe should use the general annotation
|
|
// mechanism, but it seems overkill for this.
|
|
if content.value().trim() == "cbindgen:ignore" {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
false
|
|
}
|
|
}
|
|
}
|
|
|
|
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 {
|
|
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
|
|
}
|
|
})
|
|
}
|
|
|
|
fn is_no_mangle(&self) -> bool {
|
|
self.has_attr_word("no_mangle")
|
|
}
|
|
|
|
/// Sees whether we should skip parsing a given item.
|
|
fn should_skip_parsing(&self) -> bool {
|
|
for attr in self.attrs() {
|
|
let meta = match attr.parse_meta() {
|
|
Ok(attr) => attr,
|
|
Err(..) => return false,
|
|
};
|
|
if is_skip_item_attr(&meta) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
fn attr_name_value_lookup(&self, name: &str) -> Option<String> {
|
|
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<String> {
|
|
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 $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 SynAttributeHelpers for syn::Item {
|
|
fn attrs(&self) -> &[syn::Attribute] {
|
|
syn_item_match_helper!(self =>
|
|
has_attrs: |item| { &item.attrs },
|
|
otherwise: || { &[] }
|
|
)
|
|
}
|
|
}
|
|
|
|
macro_rules! impl_syn_item_helper {
|
|
($t:ty) => {
|
|
impl SynAttributeHelpers for $t {
|
|
fn attrs(&self) -> &[syn::Attribute] {
|
|
&self.attrs
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
impl_syn_item_helper!(syn::ItemExternCrate);
|
|
impl_syn_item_helper!(syn::ItemUse);
|
|
impl_syn_item_helper!(syn::ItemStatic);
|
|
impl_syn_item_helper!(syn::ItemConst);
|
|
impl_syn_item_helper!(syn::ItemFn);
|
|
impl_syn_item_helper!(syn::ImplItemMethod);
|
|
impl_syn_item_helper!(syn::ItemMod);
|
|
impl_syn_item_helper!(syn::ItemForeignMod);
|
|
impl_syn_item_helper!(syn::ItemType);
|
|
impl_syn_item_helper!(syn::ItemStruct);
|
|
impl_syn_item_helper!(syn::ItemEnum);
|
|
impl_syn_item_helper!(syn::ItemUnion);
|
|
impl_syn_item_helper!(syn::ItemTrait);
|
|
impl_syn_item_helper!(syn::ItemImpl);
|
|
impl_syn_item_helper!(syn::ItemMacro);
|
|
impl_syn_item_helper!(syn::ItemMacro2);
|
|
impl_syn_item_helper!(syn::ItemTraitAlias);
|
|
|
|
/// Helper function for accessing Abi information
|
|
pub trait SynAbiHelpers {
|
|
fn is_c(&self) -> bool;
|
|
fn is_omitted(&self) -> bool;
|
|
}
|
|
|
|
impl SynAbiHelpers for Option<syn::Abi> {
|
|
fn is_c(&self) -> bool {
|
|
if let Some(ref abi) = *self {
|
|
if let Some(ref lit_string) = abi.name {
|
|
return lit_string.value() == "C";
|
|
}
|
|
}
|
|
false
|
|
}
|
|
fn is_omitted(&self) -> bool {
|
|
if let Some(ref abi) = *self {
|
|
abi.name.is_none()
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SynAbiHelpers for syn::Abi {
|
|
fn is_c(&self) -> bool {
|
|
if let Some(ref lit_string) = self.name {
|
|
lit_string.value() == "C"
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
fn is_omitted(&self) -> bool {
|
|
self.name.is_none()
|
|
}
|
|
}
|
|
|
|
impl SynAttributeHelpers for [syn::Attribute] {
|
|
fn attrs(&self) -> &[syn::Attribute] {
|
|
self
|
|
}
|
|
}
|
|
|
|
fn split_doc_attr(input: &str) -> Vec<String> {
|
|
input
|
|
// Convert two newline (indicate "new paragraph") into two line break.
|
|
.replace("\n\n", " \n \n")
|
|
// Convert newline after two spaces (indicate "line break") into line break.
|
|
.split(" \n")
|
|
// Convert single newline (indicate hard-wrapped) into space.
|
|
.map(|s| s.replace('\n', " "))
|
|
.map(|s| s.trim_end().to_string())
|
|
.collect()
|
|
}
|