Squashed 'third_party/autocxx/' content from commit 629e8fa53
git-subtree-dir: third_party/autocxx
git-subtree-split: 629e8fa531a633164c0b52e2a3cab536d4cd0849
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
Change-Id: I62a03b0049f49adf029e0204639cdb5468dde1a1
diff --git a/engine/src/conversion/parse/bindgen_semantic_attributes.rs b/engine/src/conversion/parse/bindgen_semantic_attributes.rs
new file mode 100644
index 0000000..8b789ae
--- /dev/null
+++ b/engine/src/conversion/parse/bindgen_semantic_attributes.rs
@@ -0,0 +1,190 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use proc_macro2::{Ident, TokenStream};
+use syn::{
+ parenthesized,
+ parse::{Parse, Parser},
+ Attribute, LitStr,
+};
+
+use crate::conversion::{
+ api::{CppVisibility, Layout, References, SpecialMemberKind, Virtualness},
+ convert_error::{ConvertErrorWithContext, ErrorContext},
+ ConvertError,
+};
+
+/// The set of all annotations that autocxx_bindgen has added
+/// for our benefit.
+#[derive(Debug)]
+pub(crate) struct BindgenSemanticAttributes(Vec<BindgenSemanticAttribute>);
+
+impl BindgenSemanticAttributes {
+ // Remove `bindgen_` attributes. They don't have a corresponding macro defined anywhere,
+ // so they will cause compilation errors if we leave them in.
+ // We may return an error if one of the bindgen attributes shows that the
+ // item can't be processed.
+ pub(crate) fn new_retaining_others(attrs: &mut Vec<Attribute>) -> Self {
+ let metadata = Self::new(attrs);
+ attrs.retain(|a| a.path.segments.last().unwrap().ident != "cpp_semantics");
+ metadata
+ }
+
+ pub(crate) fn new(attrs: &[Attribute]) -> Self {
+ Self(
+ attrs
+ .iter()
+ .filter_map(|attr| {
+ if attr.path.segments.last().unwrap().ident == "cpp_semantics" {
+ let r: Result<BindgenSemanticAttribute, syn::Error> = attr.parse_args();
+ r.ok()
+ } else {
+ None
+ }
+ })
+ .collect(),
+ )
+ }
+
+ /// Some attributes indicate we can never handle a given item. Check for those.
+ pub(crate) fn check_for_fatal_attrs(
+ &self,
+ id_for_context: &Ident,
+ ) -> Result<(), ConvertErrorWithContext> {
+ if self.has_attr("unused_template_param") {
+ Err(ConvertErrorWithContext(
+ ConvertError::UnusedTemplateParam,
+ Some(ErrorContext::new_for_item(id_for_context.clone())),
+ ))
+ } else {
+ Ok(())
+ }
+ }
+
+ /// Whether the given attribute is present.
+ pub(super) fn has_attr(&self, attr_name: &str) -> bool {
+ self.0.iter().any(|a| a.is_ident(attr_name))
+ }
+
+ /// The C++ visibility of the item.
+ pub(super) fn get_cpp_visibility(&self) -> CppVisibility {
+ if self.has_attr("visibility_private") {
+ CppVisibility::Private
+ } else if self.has_attr("visibility_protected") {
+ CppVisibility::Protected
+ } else {
+ CppVisibility::Public
+ }
+ }
+
+ /// Whether the item is virtual.
+ pub(super) fn get_virtualness(&self) -> Virtualness {
+ if self.has_attr("pure_virtual") {
+ Virtualness::PureVirtual
+ } else if self.has_attr("bindgen_virtual") {
+ Virtualness::Virtual
+ } else {
+ Virtualness::None
+ }
+ }
+
+ fn parse_if_present<T: Parse>(&self, annotation: &str) -> Option<T> {
+ self.0
+ .iter()
+ .find(|a| a.is_ident(annotation))
+ .map(|a| a.parse_args().unwrap())
+ }
+
+ fn string_if_present(&self, annotation: &str) -> Option<String> {
+ let ls: Option<LitStr> = self.parse_if_present(annotation);
+ ls.map(|ls| ls.value())
+ }
+
+ /// The in-memory layout of the item.
+ pub(super) fn get_layout(&self) -> Option<Layout> {
+ self.parse_if_present("layout")
+ }
+
+ /// The original C++ name, which bindgen may have changed.
+ pub(super) fn get_original_name(&self) -> Option<String> {
+ self.string_if_present("original_name")
+ }
+
+ /// Whether this is a move constructor or other special member.
+ pub(super) fn special_member_kind(&self) -> Option<SpecialMemberKind> {
+ self.string_if_present("special_member")
+ .map(|kind| match kind.as_str() {
+ "default_ctor" => SpecialMemberKind::DefaultConstructor,
+ "copy_ctor" => SpecialMemberKind::CopyConstructor,
+ "move_ctor" => SpecialMemberKind::MoveConstructor,
+ "dtor" => SpecialMemberKind::Destructor,
+ "assignment_operator" => SpecialMemberKind::AssignmentOperator,
+ _ => panic!("unexpected special_member_kind"),
+ })
+ }
+
+ /// Any reference parameters or return values.
+ pub(super) fn get_reference_parameters_and_return(&self) -> References {
+ let mut results = References::default();
+ for a in &self.0 {
+ if a.is_ident("ret_type_reference") {
+ results.ref_return = true;
+ } else if a.is_ident("ret_type_rvalue_reference") {
+ results.rvalue_ref_return = true;
+ } else if a.is_ident("arg_type_reference") {
+ let r: Result<Ident, syn::Error> = a.parse_args();
+ if let Ok(ls) = r {
+ results.ref_params.insert(ls);
+ }
+ } else if a.is_ident("arg_type_rvalue_reference") {
+ let r: Result<Ident, syn::Error> = a.parse_args();
+ if let Ok(ls) = r {
+ results.rvalue_ref_params.insert(ls);
+ }
+ }
+ }
+ results
+ }
+}
+
+#[derive(Debug)]
+struct BindgenSemanticAttribute {
+ annotation_name: Ident,
+ body: Option<TokenStream>,
+}
+
+impl BindgenSemanticAttribute {
+ fn is_ident(&self, name: &str) -> bool {
+ self.annotation_name == name
+ }
+
+ fn parse_args<T: Parse>(&self) -> Result<T, syn::Error> {
+ T::parse.parse2(self.body.as_ref().unwrap().clone())
+ }
+}
+
+impl Parse for BindgenSemanticAttribute {
+ fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
+ let annotation_name: Ident = input.parse()?;
+ if input.peek(syn::token::Paren) {
+ let body_contents;
+ parenthesized!(body_contents in input);
+ Ok(Self {
+ annotation_name,
+ body: Some(body_contents.parse()?),
+ })
+ } else if !input.is_empty() {
+ Err(input.error("expected nothing"))
+ } else {
+ Ok(Self {
+ annotation_name,
+ body: None,
+ })
+ }
+ }
+}
diff --git a/engine/src/conversion/parse/mod.rs b/engine/src/conversion/parse/mod.rs
new file mode 100644
index 0000000..3f42ce4
--- /dev/null
+++ b/engine/src/conversion/parse/mod.rs
@@ -0,0 +1,14 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+mod bindgen_semantic_attributes;
+mod parse_bindgen;
+mod parse_foreign_mod;
+
+pub(crate) use bindgen_semantic_attributes::BindgenSemanticAttributes;
+pub(crate) use parse_bindgen::ParseBindgen;
diff --git a/engine/src/conversion/parse/parse_bindgen.rs b/engine/src/conversion/parse/parse_bindgen.rs
new file mode 100644
index 0000000..0818aa5
--- /dev/null
+++ b/engine/src/conversion/parse/parse_bindgen.rs
@@ -0,0 +1,388 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use indexmap::map::IndexMap as HashMap;
+use indexmap::set::IndexSet as HashSet;
+
+use crate::{
+ conversion::{
+ api::{Api, ApiName, NullPhase, StructDetails, SubclassName, TypedefKind, UnanalyzedApi},
+ apivec::ApiVec,
+ 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, RustPath};
+use syn::{parse_quote, Fields, Ident, Item, Type, TypePath, UseTree};
+
+use super::{
+ super::utilities::generate_utilities, bindgen_semantic_attributes::BindgenSemanticAttributes,
+};
+
+use super::parse_foreign_mod::ParseForeignMod;
+
+/// Parses a bindgen mod in order to understand the APIs within it.
+pub(crate) struct ParseBindgen<'a> {
+ config: &'a IncludeCppConfig,
+ apis: ApiVec<NullPhase>,
+}
+
+fn api_name(ns: &Namespace, id: Ident, attrs: &BindgenSemanticAttributes) -> ApiName {
+ ApiName::new_with_cpp_name(ns, id, attrs.get_original_name())
+}
+
+pub(crate) fn api_name_qualified(
+ ns: &Namespace,
+ id: Ident,
+ attrs: &BindgenSemanticAttributes,
+) -> Result<ApiName, ConvertErrorWithContext> {
+ match validate_ident_ok_for_cxx(&id.to_string()) {
+ Err(e) => {
+ let ctx = ErrorContext::new_for_item(id);
+ Err(ConvertErrorWithContext(e, Some(ctx)))
+ }
+ Ok(..) => Ok(api_name(ns, id, attrs)),
+ }
+}
+
+impl<'a> ParseBindgen<'a> {
+ pub(crate) fn new(config: &'a IncludeCppConfig) -> Self {
+ ParseBindgen {
+ config,
+ apis: ApiVec::new(),
+ }
+ }
+
+ /// Parses items found in the `bindgen` output and returns a set of
+ /// `Api`s together with some other data.
+ pub(crate) fn parse_items(
+ mut self,
+ items: Vec<Item>,
+ ) -> Result<ApiVec<NullPhase>, ConvertError> {
+ let items = Self::find_items_in_root(items)?;
+ if !self.config.exclude_utilities() {
+ generate_utilities(&mut self.apis, self.config);
+ }
+ self.add_apis_from_config();
+ let root_ns = Namespace::new();
+ self.parse_mod_items(items, root_ns);
+ self.confirm_all_generate_directives_obeyed()?;
+ self.replace_extern_cpp_types();
+ Ok(self.apis)
+ }
+
+ /// Some API items are not populated from bindgen output, but instead
+ /// directly from items in the config.
+ fn add_apis_from_config(&mut self) {
+ self.apis
+ .extend(self.config.subclasses.iter().map(|sc| Api::Subclass {
+ name: SubclassName::new(sc.subclass.clone()),
+ superclass: QualifiedName::new_from_cpp_name(&sc.superclass),
+ }));
+ self.apis
+ .extend(self.config.extern_rust_funs.iter().map(|fun| {
+ let id = fun.sig.ident.clone();
+ Api::RustFn {
+ name: ApiName::new_in_root_namespace(id),
+ details: fun.clone(),
+ receiver: fun.receiver.as_ref().map(|receiver_id| {
+ QualifiedName::new(&Namespace::new(), receiver_id.clone())
+ }),
+ }
+ }));
+ let unique_rust_types: HashSet<&RustPath> = self.config.rust_types.iter().collect();
+ self.apis.extend(unique_rust_types.into_iter().map(|path| {
+ let id = path.get_final_ident();
+ Api::RustType {
+ name: ApiName::new_in_root_namespace(id.clone()),
+ path: path.clone(),
+ }
+ }));
+ self.apis.extend(
+ self.config
+ .concretes
+ .0
+ .iter()
+ .map(|(cpp_definition, rust_id)| {
+ let name = ApiName::new_in_root_namespace(rust_id.clone());
+ Api::ConcreteType {
+ name,
+ cpp_definition: cpp_definition.clone(),
+ rs_definition: None,
+ }
+ }),
+ );
+ }
+
+ /// We do this last, _after_ we've parsed all the APIs, because we might want to actually
+ /// replace some of the existing APIs (structs/enums/etc.) with replacements.
+ fn replace_extern_cpp_types(&mut self) {
+ let pod_requests: HashSet<_> = self.config.get_pod_requests().iter().collect();
+ let replacements: HashMap<_, _> = self
+ .config
+ .externs
+ .0
+ .iter()
+ .map(|(cpp_definition, details)| {
+ let qn = QualifiedName::new_from_cpp_name(cpp_definition);
+ let pod = pod_requests.contains(&qn.to_cpp_name());
+ (
+ qn.clone(),
+ Api::ExternCppType {
+ name: ApiName::new_from_qualified_name(qn),
+ details: details.clone(),
+ pod,
+ },
+ )
+ })
+ .collect();
+ self.apis
+ .retain(|api| !replacements.contains_key(api.name()));
+ self.apis.extend(replacements.into_iter().map(|(_, v)| v));
+ }
+
+ fn find_items_in_root(items: Vec<Item>) -> Result<Vec<Item>, ConvertError> {
+ for item in items {
+ match item {
+ Item::Mod(root_mod) => {
+ // With namespaces enabled, bindgen always puts everything
+ // in a mod called 'root'. We don't want to pass that
+ // onto cxx, so jump right into it.
+ assert!(root_mod.ident == "root");
+ if let Some((_, items)) = root_mod.content {
+ return Ok(items);
+ }
+ }
+ _ => return Err(ConvertError::UnexpectedOuterItem),
+ }
+ }
+ Ok(Vec::new())
+ }
+
+ /// Interpret the bindgen-generated .rs for a particular
+ /// mod, which corresponds to a C++ namespace.
+ fn parse_mod_items(&mut self, items: Vec<Item>, ns: Namespace) {
+ // This object maintains some state specific to this namespace, i.e.
+ // this particular mod.
+ let mut mod_converter = ParseForeignMod::new(ns.clone());
+ let mut more_apis = ApiVec::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);
+ Ok(())
+ }
+ Item::Struct(s) => {
+ if s.ident.to_string().ends_with("__bindgen_vtable") {
+ return Ok(());
+ }
+ let annotations = BindgenSemanticAttributes::new(&s.attrs);
+ // cxx::bridge can't cope with type aliases to generic
+ // types at the moment.
+ let name = api_name_qualified(ns, s.ident.clone(), &annotations)?;
+ let err = annotations.check_for_fatal_attrs(&s.ident).err();
+ let api = if ns.is_empty() && self.config.is_rust_type(&s.ident) {
+ None
+ } else if Self::spot_forward_declaration(&s.fields)
+ || (Self::spot_zero_length_struct(&s.fields) && err.is_some())
+ {
+ // Forward declarations are recorded especially because we can't
+ // store them in UniquePtr or similar.
+ // Templated forward declarations don't appear with an _unused field (which is what
+ // we spot in the previous clause) but instead with an _address field.
+ // So, solely in the case where we're storing up an error about such
+ // a templated type, we'll also treat such cases as forward declarations.
+ Some(UnanalyzedApi::ForwardDeclaration { name, err })
+ } else {
+ let has_rvalue_reference_fields = s.fields.iter().any(|f| {
+ BindgenSemanticAttributes::new(&f.attrs).has_attr("rvalue_reference")
+ });
+ Some(UnanalyzedApi::Struct {
+ name,
+ details: Box::new(StructDetails {
+ vis: annotations.get_cpp_visibility(),
+ layout: annotations.get_layout(),
+ item: s,
+ has_rvalue_reference_fields,
+ }),
+ analysis: (),
+ })
+ };
+ if let Some(api) = api {
+ if !self.config.is_on_blocklist(&api.name().to_cpp_name()) {
+ self.apis.push(api);
+ }
+ }
+ Ok(())
+ }
+ Item::Enum(e) => {
+ let annotations = BindgenSemanticAttributes::new(&e.attrs);
+ let api = UnanalyzedApi::Enum {
+ name: api_name_qualified(ns, e.ident.clone(), &annotations)?,
+ item: e,
+ };
+ if !self.config.is_on_blocklist(&api.name().to_cpp_name()) {
+ self.apis.push(api);
+ }
+ Ok(())
+ }
+ Item::Impl(imp) => {
+ // We *mostly* ignore all impl blocks generated by bindgen.
+ // Methods also appear in 'extern "C"' blocks which
+ // we will convert instead. At that time we'll also construct
+ // synthetic impl blocks.
+ // We do however record which methods were spotted, since
+ // we have no other way of working out which functions are
+ // static methods vs plain functions.
+ 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, // we do not add this to any API since we generate equivalent
+ // use statements in our codegen phase.
+ UseTree::Rename(urn) => {
+ let old_id = &urn.ident;
+ let new_id = &urn.rename;
+ let new_tyname = QualifiedName::new(ns, new_id.clone());
+ assert!(segs.remove(0) == "self", "Path didn't start with self");
+ assert!(
+ segs.remove(0) == "super",
+ "Path didn't start with self::super"
+ );
+ // This is similar to the path encountered within 'tree'
+ // but without the self::super prefix which is unhelpful
+ // in our output mod, because we prefer relative paths
+ // (we're nested in another mod)
+ 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::new_for_item(new_id.clone())),
+ ));
+ }
+ let annotations = BindgenSemanticAttributes::new(&use_item.attrs);
+ self.apis.push(UnanalyzedApi::Typedef {
+ name: api_name(ns, new_id.clone(), &annotations),
+ item: TypedefKind::Use(
+ parse_quote! {
+ pub use #old_path as #new_id;
+ },
+ Box::new(Type::Path(old_path)),
+ ),
+ old_tyname: Some(old_tyname),
+ analysis: (),
+ });
+ break;
+ }
+ _ => {
+ return Err(ConvertErrorWithContext(
+ ConvertError::UnexpectedUseStatement(
+ segs.into_iter().last().map(|i| i.to_string()),
+ ),
+ None,
+ ))
+ }
+ }
+ }
+ Ok(())
+ }
+ Item::Const(const_item) => {
+ let annotations = BindgenSemanticAttributes::new(&const_item.attrs);
+ self.apis.push(UnanalyzedApi::Const {
+ name: api_name(ns, const_item.ident.clone(), &annotations),
+ const_item,
+ });
+ Ok(())
+ }
+ Item::Type(ity) => {
+ let annotations = BindgenSemanticAttributes::new(&ity.attrs);
+ // It's known that sometimes bindgen will give us duplicate typedefs with the
+ // same name - see test_issue_264.
+ self.apis.push(UnanalyzedApi::Typedef {
+ name: api_name(ns, ity.ident.clone(), &annotations),
+ item: TypedefKind::Type(ity),
+ old_tyname: None,
+ analysis: (),
+ });
+ Ok(())
+ }
+ _ => Err(ConvertErrorWithContext(
+ ConvertError::UnexpectedItemInMod,
+ None,
+ )),
+ }
+ }
+
+ fn spot_forward_declaration(s: &Fields) -> bool {
+ Self::spot_field(s, "_unused")
+ }
+
+ fn spot_zero_length_struct(s: &Fields) -> bool {
+ Self::spot_field(s, "_address")
+ }
+
+ fn spot_field(s: &Fields, desired_id: &str) -> bool {
+ s.iter()
+ .filter_map(|f| f.ident.as_ref())
+ .any(|id| id == desired_id)
+ }
+
+ 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(())
+ }
+}
diff --git a/engine/src/conversion/parse/parse_foreign_mod.rs b/engine/src/conversion/parse/parse_foreign_mod.rs
new file mode 100644
index 0000000..08c1fd8
--- /dev/null
+++ b/engine/src/conversion/parse/parse_foreign_mod.rs
@@ -0,0 +1,177 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use crate::conversion::api::{ApiName, NullPhase, Provenance};
+use crate::conversion::apivec::ApiVec;
+use crate::conversion::doc_attr::get_doc_attrs;
+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;
+use syn::{Block, Expr, ExprCall, ForeignItem, Ident, ImplItem, ItemImpl, Stmt, Type};
+
+use super::bindgen_semantic_attributes::BindgenSemanticAttributes;
+
+/// Parses a given bindgen-generated 'mod' into suitable
+/// [Api]s. In bindgen output, a given mod concerns
+/// a specific C++ namespace.
+pub(crate) struct ParseForeignMod {
+ ns: Namespace,
+ // We mostly act upon the functions we see within the 'extern "C"'
+ // block of bindgen output, but we can't actually do this until
+ // we've seen the (possibly subsequent) 'impl' blocks so we can
+ // deduce which functions are actually static methods. Hence
+ // store them.
+ funcs_to_convert: Vec<FuncToConvert>,
+ // Evidence from 'impl' blocks about which of these items
+ // may actually be methods (static or otherwise). Mapping from
+ // function name to type name.
+ method_receivers: HashMap<Ident, QualifiedName>,
+ ignored_apis: ApiVec<NullPhase>,
+}
+
+impl ParseForeignMod {
+ pub(crate) fn new(ns: Namespace) -> Self {
+ Self {
+ ns,
+ funcs_to_convert: Vec::new(),
+ method_receivers: HashMap::new(),
+ ignored_apis: ApiVec::new(),
+ }
+ }
+
+ /// Record information from foreign mod items encountered
+ /// in bindgen output.
+ pub(crate) fn convert_foreign_mod_items(&mut self, foreign_mod_items: Vec<ForeignItem>) {
+ let mut extra_apis = ApiVec::new();
+ for i in foreign_mod_items {
+ report_any_error(&self.ns.clone(), &mut extra_apis, || {
+ self.parse_foreign_item(i)
+ });
+ }
+ self.ignored_apis.append(&mut extra_apis);
+ }
+
+ fn parse_foreign_item(&mut self, i: ForeignItem) -> Result<(), ConvertErrorWithContext> {
+ match i {
+ ForeignItem::Fn(item) => {
+ let annotations = BindgenSemanticAttributes::new(&item.attrs);
+ let doc_attrs = get_doc_attrs(&item.attrs);
+ self.funcs_to_convert.push(FuncToConvert {
+ provenance: Provenance::Bindgen,
+ self_ty: None,
+ ident: item.sig.ident,
+ doc_attrs,
+ inputs: item.sig.inputs,
+ output: item.sig.output,
+ vis: item.vis,
+ virtualness: annotations.get_virtualness(),
+ cpp_vis: annotations.get_cpp_visibility(),
+ special_member: annotations.special_member_kind(),
+ unused_template_param: annotations
+ .has_attr("incomprehensible_param_in_arg_or_return"),
+ references: annotations.get_reference_parameters_and_return(),
+ original_name: annotations.get_original_name(),
+ synthesized_this_type: None,
+ add_to_trait: None,
+ is_deleted: annotations.has_attr("deleted"),
+ synthetic_cpp: None,
+ variadic: item.sig.variadic.is_some(),
+ });
+ Ok(())
+ }
+ ForeignItem::Static(item) => Err(ConvertErrorWithContext(
+ ConvertError::StaticData(item.ident.to_string()),
+ Some(ErrorContext::new_for_item(item.ident)),
+ )),
+ _ => Err(ConvertErrorWithContext(
+ ConvertError::UnexpectedForeignItem,
+ None,
+ )),
+ }
+ }
+
+ /// Record information from impl blocks encountered in bindgen
+ /// output.
+ 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 = 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()),
+ );
+ }
+ }
+ }
+
+ /// Indicate that all foreign mods and all impl blocks have been
+ /// fed into us, and we should process that information to generate
+ /// the resulting APIs.
+ pub(crate) fn finished(mut self, apis: &mut ApiVec<NullPhase>) {
+ 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: (),
+ })
+ }
+ }
+}
+
+/// bindgen sometimes generates an impl fn called a which calls
+/// a function called a1(), if it's dealing with conflicting names.
+/// We actually care about the name a1, so we have to parse the
+/// name of the actual function call inside the block's body.
+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");
+ }
+}