use std::collections::HashSet;
use crate::{
conversion::{
api::{ApiDetail, NullAnalysis, TypedefKind, UnanalyzedApi},
ConvertError,
},
types::Namespace,
types::QualifiedName,
};
use crate::{
conversion::{
convert_error::{ConvertErrorWithContext, ErrorContext},
error_reporter::report_any_error,
},
types::validate_ident_ok_for_cxx,
};
use autocxx_parser::IncludeCppConfig;
use syn::{parse_quote, Attribute, Fields, Ident, Item, LitStr, TypePath, UseTree};
use super::super::utilities::generate_utilities;
use super::parse_foreign_mod::ParseForeignMod;
pub(crate) struct ParseBindgen<'a> {
config: &'a IncludeCppConfig,
apis: Vec<UnanalyzedApi>,
latest_virtual_this_type: Option<QualifiedName>,
}
pub(crate) fn get_bindgen_original_name_annotation(attrs: &[Attribute]) -> Option<String> {
attrs
.iter()
.filter_map(|a| {
if a.path.is_ident("bindgen_original_name") {
let r: Result<LitStr, syn::Error> = a.parse_args();
match r {
Ok(ls) => Some(ls.value()),
Err(_) => None,
}
} else {
None
}
})
.next()
}
impl<'a> ParseBindgen<'a> {
pub(crate) fn new(config: &'a IncludeCppConfig) -> Self {
ParseBindgen {
config,
apis: Vec::new(),
latest_virtual_this_type: None,
}
}
pub(crate) fn parse_items(
mut self,
items: Vec<Item>,
) -> Result<Vec<UnanalyzedApi>, ConvertError> {
let items = Self::find_items_in_root(items)?;
if !self.config.exclude_utilities() {
generate_utilities(&mut self.apis, &self.config);
}
let root_ns = Namespace::new();
self.parse_mod_items(items, root_ns);
self.confirm_all_generate_directives_obeyed()?;
Ok(self.apis)
}
fn find_items_in_root(items: Vec<Item>) -> Result<Vec<Item>, ConvertError> {
for item in items {
match item {
Item::Mod(root_mod) => {
assert!(root_mod.ident == "root");
if let Some((_, items)) = root_mod.content {
return Ok(items);
}
}
_ => return Err(ConvertError::UnexpectedOuterItem),
}
}
Ok(Vec::new())
}
fn parse_mod_items(&mut self, items: Vec<Item>, ns: Namespace) {
let mut mod_converter = ParseForeignMod::new(ns.clone());
let mut more_apis = Vec::new();
for item in items {
report_any_error(&ns, &mut more_apis, || {
self.parse_item(item, &mut mod_converter, &ns)
});
}
self.apis.append(&mut more_apis);
mod_converter.finished(&mut self.apis);
}
fn parse_item(
&mut self,
item: Item,
mod_converter: &mut ParseForeignMod,
ns: &Namespace,
) -> Result<(), ConvertErrorWithContext> {
match item {
Item::ForeignMod(fm) => {
mod_converter
.convert_foreign_mod_items(fm.items, self.latest_virtual_this_type.clone());
Ok(())
}
Item::Struct(s) => {
if s.ident.to_string().ends_with("__bindgen_vtable") {
return Ok(());
}
let tyname = Self::qualify_name(ns, s.ident.clone())?;
let is_forward_declaration = Self::spot_forward_declaration(&s.fields);
let cpp_name = get_bindgen_original_name_annotation(&s.attrs);
self.parse_type(tyname.clone(), is_forward_declaration, s, cpp_name, |s| {
ApiDetail::Struct {
item: s,
analysis: (),
}
});
self.latest_virtual_this_type = Some(tyname);
Ok(())
}
Item::Enum(e) => {
let tyname = Self::qualify_name(ns, e.ident.clone())?;
let cpp_name = get_bindgen_original_name_annotation(&e.attrs);
self.parse_type(tyname, false, e, cpp_name, |e| ApiDetail::Enum { item: e });
Ok(())
}
Item::Impl(imp) => {
mod_converter.convert_impl_items(imp);
Ok(())
}
Item::Mod(itm) => {
if let Some((_, items)) = itm.content {
let new_ns = ns.push(itm.ident.to_string());
self.parse_mod_items(items, new_ns);
}
Ok(())
}
Item::Use(use_item) => {
let mut segs = Vec::new();
let mut tree = &use_item.tree;
loop {
match tree {
UseTree::Path(up) => {
segs.push(up.ident.clone());
tree = &up.tree;
}
UseTree::Name(un) if un.ident == "root" => break, UseTree::Rename(urn) => {
let old_id = &urn.ident;
let new_id = &urn.rename;
let new_tyname = QualifiedName::new(ns, new_id.clone());
if segs.remove(0) != "self" {
panic!("Path didn't start with self");
}
if segs.remove(0) != "super" {
panic!("Path didn't start with self::super");
}
let old_path: TypePath = parse_quote! {
#(#segs)::* :: #old_id
};
let old_tyname = QualifiedName::from_type_path(&old_path);
if new_tyname == old_tyname {
return Err(ConvertErrorWithContext(
ConvertError::InfinitelyRecursiveTypedef(new_tyname),
Some(ErrorContext::Item(new_id.clone())),
));
}
let mut deps = HashSet::new();
deps.insert(old_tyname);
self.apis.push(UnanalyzedApi {
name: QualifiedName::new(ns, new_id.clone()),
cpp_name: get_bindgen_original_name_annotation(&use_item.attrs),
deps,
detail: ApiDetail::Typedef {
item: TypedefKind::Use(parse_quote! {
pub use #old_path as #new_id;
}),
analysis: (),
},
});
break;
}
_ => {
return Err(ConvertErrorWithContext(
ConvertError::UnexpectedUseStatement(segs.into_iter().last()),
None,
))
}
}
}
Ok(())
}
Item::Const(const_item) => {
self.apis.push(UnanalyzedApi {
name: QualifiedName::new(ns, const_item.ident.clone()),
cpp_name: get_bindgen_original_name_annotation(&const_item.attrs),
deps: HashSet::new(),
detail: ApiDetail::Const { const_item },
});
Ok(())
}
Item::Type(ity) => {
self.apis.push(UnanalyzedApi {
name: QualifiedName::new(ns, ity.ident.clone()),
cpp_name: get_bindgen_original_name_annotation(&ity.attrs),
deps: HashSet::new(),
detail: ApiDetail::Typedef {
item: TypedefKind::Type(ity),
analysis: (),
},
});
Ok(())
}
_ => Err(ConvertErrorWithContext(
ConvertError::UnexpectedItemInMod,
None,
)),
}
}
fn qualify_name(ns: &Namespace, id: Ident) -> Result<QualifiedName, ConvertErrorWithContext> {
match validate_ident_ok_for_cxx(&id.to_string()) {
Err(e) => {
let ctx = ErrorContext::Item(id);
Err(ConvertErrorWithContext(e, Some(ctx)))
}
Ok(..) => Ok(QualifiedName::new(ns, id)),
}
}
fn spot_forward_declaration(s: &Fields) -> bool {
s.iter()
.filter_map(|f| f.ident.as_ref())
.any(|id| id == "_unused")
}
fn parse_type<T, F>(
&mut self,
name: QualifiedName,
is_forward_declaration: bool,
bindgen_mod_item: T,
cpp_name: Option<String>,
api_make: F,
) where
F: FnOnce(T) -> ApiDetail<NullAnalysis>,
{
if self.config.is_on_blocklist(&name.to_cpp_name()) {
return;
}
let api = UnanalyzedApi {
name,
cpp_name,
deps: HashSet::new(),
detail: if is_forward_declaration {
ApiDetail::ForwardDeclaration
} else {
api_make(bindgen_mod_item)
},
};
self.apis.push(api);
}
fn confirm_all_generate_directives_obeyed(&self) -> Result<(), ConvertError> {
let api_names: HashSet<_> = self
.apis
.iter()
.map(|api| api.name().to_cpp_name())
.collect();
for generate_directive in self.config.must_generate_list() {
if !api_names.contains(&generate_directive) {
return Err(ConvertError::DidNotGenerateAnything(generate_directive));
}
}
Ok(())
}
}