Parse function pointer types
diff --git a/syntax/check.rs b/syntax/check.rs
index be61226..1fe3daf 100644
--- a/syntax/check.rs
+++ b/syntax/check.rs
@@ -45,6 +45,7 @@
errors.push(unsupported_reference_type(ty));
}
}
+ Type::Fn(_) => errors.push(unimplemented_fn_type(ty)),
_ => {}
}
}
@@ -213,3 +214,7 @@
let message = format!("returning {} by value is not supported", desc);
Error::new_spanned(ty, message)
}
+
+fn unimplemented_fn_type(ty: &Type) -> Error {
+ Error::new_spanned(ty, "function pointer support is not implemented yet")
+}
diff --git a/syntax/parse.rs b/syntax/parse.rs
index 58237f3..3d35f78 100644
--- a/syntax/parse.rs
+++ b/syntax/parse.rs
@@ -3,11 +3,11 @@
Ty1, Type, Var,
};
use proc_macro2::Ident;
-use quote::quote;
+use quote::{format_ident, quote};
use syn::{
Abi, Error, Fields, FnArg, ForeignItem, ForeignItemFn, ForeignItemType, GenericArgument, Item,
- ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Type as RustType, TypePath,
- TypeReference,
+ ItemForeignMod, ItemStruct, Pat, PathArguments, Result, ReturnType, Type as RustType,
+ TypeBareFn, TypePath, TypeReference,
};
pub fn parse_items(items: Vec<Item>) -> Result<Vec<Api>> {
@@ -176,32 +176,7 @@
}
let mut throws = false;
- let ret = match &foreign_fn.sig.output {
- ReturnType::Default => None,
- ReturnType::Type(_, ret) => {
- let mut ret = ret.as_ref();
- if let RustType::Path(ty) = ret {
- let path = &ty.path;
- if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
- let segment = &path.segments[0];
- let ident = segment.ident.clone();
- if let PathArguments::AngleBracketed(generic) = &segment.arguments {
- if ident == "Result" && generic.args.len() == 1 {
- if let GenericArgument::Type(arg) = &generic.args[0] {
- ret = arg;
- throws = true;
- }
- }
- }
- }
- }
- match parse_type(ret)? {
- Type::Void(_) => None,
- ty => Some(ty),
- }
- }
- };
-
+ let ret = parse_return_type(&foreign_fn.sig.output, &mut throws)?;
let doc = attrs::parse_doc(&foreign_fn.attrs)?;
let fn_token = foreign_fn.sig.fn_token;
let ident = foreign_fn.sig.ident.clone();
@@ -230,6 +205,7 @@
match ty {
RustType::Reference(ty) => parse_type_reference(ty),
RustType::Path(ty) => parse_type_path(ty),
+ RustType::BareFn(ty) => parse_type_fn(ty),
RustType::Tuple(ty) if ty.elems.is_empty() => Ok(Type::Void(ty.paren_token.span)),
_ => Err(Error::new_spanned(ty, "unsupported type")),
}
@@ -290,6 +266,71 @@
Err(Error::new_spanned(ty, "unsupported type"))
}
+fn parse_type_fn(ty: &TypeBareFn) -> Result<Type> {
+ if ty.lifetimes.is_some() {
+ return Err(Error::new_spanned(
+ ty,
+ "function pointer with lifetime parameters is not supported yet",
+ ));
+ }
+ if ty.variadic.is_some() {
+ return Err(Error::new_spanned(
+ ty,
+ "variadic function pointer is not supported yet",
+ ));
+ }
+ let args = ty
+ .inputs
+ .iter()
+ .enumerate()
+ .map(|(i, arg)| {
+ let ty = parse_type(&arg.ty)?;
+ let ident = match &arg.name {
+ Some(ident) => ident.0.clone(),
+ None => format_ident!("_{}", i),
+ };
+ Ok(Var { ident, ty })
+ })
+ .collect::<Result<_>>()?;
+ let mut throws = false;
+ let ret = parse_return_type(&ty.output, &mut throws)?;
+ let tokens = quote!(#ty);
+ Ok(Type::Fn(Box::new(Signature {
+ fn_token: ty.fn_token,
+ receiver: None,
+ args,
+ ret,
+ throws,
+ tokens,
+ })))
+}
+
+fn parse_return_type(ty: &ReturnType, throws: &mut bool) -> Result<Option<Type>> {
+ let mut ret = match ty {
+ ReturnType::Default => return Ok(None),
+ ReturnType::Type(_, ret) => ret.as_ref(),
+ };
+ if let RustType::Path(ty) = ret {
+ let path = &ty.path;
+ if ty.qself.is_none() && path.leading_colon.is_none() && path.segments.len() == 1 {
+ let segment = &path.segments[0];
+ let ident = segment.ident.clone();
+ if let PathArguments::AngleBracketed(generic) = &segment.arguments {
+ if ident == "Result" && generic.args.len() == 1 {
+ if let GenericArgument::Type(arg) = &generic.args[0] {
+ ret = arg;
+ *throws = true;
+ }
+ }
+ }
+ }
+ }
+ match parse_type(ret)? {
+ Type::Void(_) => Ok(None),
+ ty => Ok(Some(ty)),
+ }
+}
+
fn check_reserved_name(ident: &Ident) -> Result<()> {
if ident == "Box" || ident == "UniquePtr" || Atom::from(ident).is_some() {
Err(Error::new(ident.span(), "reserved name"))
diff --git a/syntax/tokens.rs b/syntax/tokens.rs
index ed5b315..59bb0de 100644
--- a/syntax/tokens.rs
+++ b/syntax/tokens.rs
@@ -1,7 +1,7 @@
use crate::syntax::atom::Atom::*;
-use crate::syntax::{Derive, ExternFn, Ref, Ty1, Type, Var};
+use crate::syntax::{Derive, ExternFn, Ref, Signature, Ty1, Type, Var};
use proc_macro2::{Ident, Span, TokenStream};
-use quote::{quote, quote_spanned, ToTokens};
+use quote::{quote_spanned, ToTokens};
use syn::Token;
impl ToTokens for Type {
@@ -16,21 +16,7 @@
}
Type::RustBox(ty) | Type::UniquePtr(ty) => ty.to_tokens(tokens),
Type::Ref(r) | Type::Str(r) => r.to_tokens(tokens),
- Type::Fn(f) => {
- let fn_token = f.fn_token;
- let args = &f.args;
- tokens.extend(quote!(#fn_token(#(#args),*)));
- let mut ret = match &f.ret {
- Some(ret) => quote!(#ret),
- None => quote!(()),
- };
- if f.throws {
- ret = quote!(::std::result::Result<#ret, _>);
- }
- if f.ret.is_some() || f.throws {
- tokens.extend(quote!(-> #ret));
- }
- }
+ Type::Fn(f) => f.to_tokens(tokens),
Type::Void(span) => tokens.extend(quote_spanned!(*span=> ())),
}
}
@@ -80,3 +66,9 @@
self.sig.tokens.to_tokens(tokens);
}
}
+
+impl ToTokens for Signature {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ self.tokens.to_tokens(tokens);
+ }
+}