Parse function pointer types

This commit is contained in:
David Tolnay 2020-03-18 16:59:53 -07:00
parent 265f6a079d
commit c071b89c2c
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
3 changed files with 84 additions and 46 deletions

View File

@ -45,6 +45,7 @@ pub(crate) fn typecheck(apis: &[Api], types: &Types) -> Result<()> {
errors.push(unsupported_reference_type(ty));
}
}
Type::Fn(_) => errors.push(unimplemented_fn_type(ty)),
_ => {}
}
}
@ -213,3 +214,7 @@ fn return_by_value(ty: &Type, types: &Types) -> Error {
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")
}

View File

@ -3,11 +3,11 @@ use crate::syntax::{
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 @@ fn parse_extern_fn(foreign_fn: &ForeignItemFn, lang: Lang) -> Result<ExternFn> {
}
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 @@ fn parse_type(ty: &RustType) -> Result<Type> {
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 @@ fn parse_type_path(ty: &TypePath) -> Result<Type> {
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"))

View File

@ -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 @@ impl ToTokens for Type {
}
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 @@ impl ToTokens for ExternFn {
self.sig.tokens.to_tokens(tokens);
}
}
impl ToTokens for Signature {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.tokens.to_tokens(tokens);
}
}