use crate::conversion::api::ApiName;
use crate::conversion::doc_attr::get_doc_attr;
use crate::conversion::error_reporter::report_any_error;
use crate::conversion::{
api::{FuncToConvert, UnanalyzedApi},
convert_error::ConvertErrorWithContext,
convert_error::ErrorContext,
};
use crate::{
conversion::ConvertError,
types::{Namespace, QualifiedName},
};
use std::collections::{HashMap, HashSet};
use syn::{
Block, Expr, ExprCall, ForeignItem, ForeignItemFn, Ident, ImplItem, ItemImpl, LitStr, Stmt,
Type,
};
use super::parse_bindgen::get_bindgen_original_name_annotation;
pub(crate) struct ParseForeignMod {
ns: Namespace,
funcs_to_convert: Vec<FuncToConvert>,
method_receivers: HashMap<Ident, QualifiedName>,
ignored_apis: Vec<UnanalyzedApi>,
}
impl ParseForeignMod {
pub(crate) fn new(ns: Namespace) -> Self {
Self {
ns,
funcs_to_convert: Vec::new(),
method_receivers: HashMap::new(),
ignored_apis: Vec::new(),
}
}
pub(crate) fn convert_foreign_mod_items(
&mut self,
foreign_mod_items: Vec<ForeignItem>,
virtual_this_type: Option<QualifiedName>,
) {
let mut extra_apis = Vec::new();
for i in foreign_mod_items {
report_any_error(&self.ns.clone(), &mut extra_apis, || {
self.parse_foreign_item(i, &virtual_this_type)
});
}
self.ignored_apis.append(&mut extra_apis);
}
fn parse_foreign_item(
&mut self,
i: ForeignItem,
virtual_this_type: &Option<QualifiedName>,
) -> Result<(), ConvertErrorWithContext> {
match i {
ForeignItem::Fn(item) => {
let is_private = Self::has_attr(&item, "bindgen_visibility_private");
let is_pure_virtual = Self::has_attr(&item, "bindgen_pure_virtual");
let unused_template_param =
Self::has_attr(&item, "bindgen_unused_template_param_in_arg_or_return");
let is_move_constructor = Self::is_move_constructor(&item);
let (reference_args, return_type_is_reference) =
Self::get_reference_parameters_and_return(&item);
let original_name = get_bindgen_original_name_annotation(&item.attrs);
let doc_attr = get_doc_attr(&item.attrs);
self.funcs_to_convert.push(FuncToConvert {
virtual_this_type: virtual_this_type.clone(),
self_ty: None,
ident: item.sig.ident,
doc_attr,
inputs: item.sig.inputs,
output: item.sig.output,
vis: item.vis,
is_pure_virtual,
is_private,
is_move_constructor,
unused_template_param,
return_type_is_reference,
reference_args,
original_name,
});
Ok(())
}
ForeignItem::Static(item) => Err(ConvertErrorWithContext(
ConvertError::StaticData(item.ident.to_string()),
Some(ErrorContext::Item(item.ident)),
)),
_ => Err(ConvertErrorWithContext(
ConvertError::UnexpectedForeignItem,
None,
)),
}
}
fn has_attr(fun: &ForeignItemFn, attr_name: &str) -> bool {
fun.attrs.iter().any(|a| a.path.is_ident(attr_name))
}
fn get_bindgen_special_member_annotation(fun: &ForeignItemFn) -> Option<String> {
fun.attrs
.iter()
.filter_map(|a| {
if a.path.is_ident("bindgen_special_member") {
let r: Result<LitStr, syn::Error> = a.parse_args();
match r {
Ok(ls) => Some(ls.value()),
Err(_) => None,
}
} else {
None
}
})
.next()
}
fn is_move_constructor(fun: &ForeignItemFn) -> bool {
Self::get_bindgen_special_member_annotation(fun).map_or(false, |val| val == "move_ctor")
}
fn get_reference_parameters_and_return(fun: &ForeignItemFn) -> (HashSet<Ident>, bool) {
let mut ref_params = HashSet::new();
let mut ref_return = false;
for a in &fun.attrs {
if a.path.is_ident("bindgen_ret_type_reference") {
ref_return = true;
} else if a.path.is_ident("bindgen_arg_type_reference") {
let r: Result<Ident, syn::Error> = a.parse_args();
if let Ok(ls) = r {
ref_params.insert(ls);
}
}
}
(ref_params, ref_return)
}
pub(crate) fn convert_impl_items(&mut self, imp: ItemImpl) {
let ty_id = match *imp.self_ty {
Type::Path(typ) => typ.path.segments.last().unwrap().ident.clone(),
_ => return,
};
for i in imp.items {
if let ImplItem::Method(itm) = i {
let effective_fun_name = if itm.sig.ident == "new" {
ty_id.clone()
} else {
match get_called_function(&itm.block) {
Some(id) => id.clone(),
None => itm.sig.ident,
}
};
self.method_receivers.insert(
effective_fun_name,
QualifiedName::new(&self.ns, ty_id.clone()),
);
}
}
}
pub(crate) fn finished(mut self, apis: &mut Vec<UnanalyzedApi>) {
apis.append(&mut self.ignored_apis);
while !self.funcs_to_convert.is_empty() {
let mut fun = self.funcs_to_convert.remove(0);
fun.self_ty = self.method_receivers.get(&fun.ident).cloned();
apis.push(UnanalyzedApi::Function {
name: ApiName::new_with_cpp_name(
&self.ns,
fun.ident.clone(),
fun.original_name.clone(),
),
fun: Box::new(fun),
analysis: (),
name_for_gc: None,
})
}
}
}
fn get_called_function(block: &Block) -> Option<&Ident> {
match block.stmts.first() {
Some(Stmt::Expr(Expr::Call(ExprCall { func, .. }))) => match **func {
Expr::Path(ref exp) => exp.path.segments.first().map(|ps| &ps.ident),
_ => None,
},
_ => None,
}
}
#[cfg(test)]
mod test {
use super::get_called_function;
use syn::parse_quote;
use syn::Block;
#[test]
fn test_get_called_function() {
let b: Block = parse_quote! {
{
call_foo()
}
};
assert_eq!(get_called_function(&b).unwrap().to_string(), "call_foo");
}
}