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/analysis/fun/mod.rs b/engine/src/conversion/analysis/fun/mod.rs
new file mode 100644
index 0000000..19340f9
--- /dev/null
+++ b/engine/src/conversion/analysis/fun/mod.rs
@@ -0,0 +1,2140 @@
+// 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 bridge_name_tracker;
+pub(crate) mod function_wrapper;
+mod implicit_constructors;
+mod overload_tracker;
+mod subclass;
+
+use crate::{
+ conversion::{
+ analysis::{
+ fun::function_wrapper::{CppConversionType, CppFunctionKind},
+ type_converter::{self, add_analysis, TypeConversionContext, TypeConverter},
+ },
+ api::{
+ ApiName, CastMutability, CppVisibility, FuncToConvert, NullPhase, Provenance,
+ References, SpecialMemberKind, SubclassName, TraitImplSignature, TraitSynthesis,
+ UnsafetyNeeded, Virtualness,
+ },
+ apivec::ApiVec,
+ convert_error::ErrorContext,
+ convert_error::{ConvertErrorWithContext, ErrorContextType},
+ error_reporter::{convert_apis, report_any_error},
+ },
+ known_types::known_types,
+ types::validate_ident_ok_for_rust,
+};
+use indexmap::map::IndexMap as HashMap;
+use indexmap::set::IndexSet as HashSet;
+
+use autocxx_parser::{ExternCppType, IncludeCppConfig, UnsafePolicy};
+use function_wrapper::{CppFunction, CppFunctionBody, TypeConversionPolicy};
+use itertools::Itertools;
+use proc_macro2::Span;
+use quote::quote;
+use syn::{
+ parse_quote, punctuated::Punctuated, token::Comma, FnArg, Ident, Pat, ReturnType, Type,
+ TypePtr, Visibility,
+};
+
+use crate::{
+ conversion::{
+ api::{AnalysisPhase, Api, TypeKind},
+ ConvertError,
+ },
+ types::{make_ident, validate_ident_ok_for_cxx, Namespace, QualifiedName},
+};
+
+use self::{
+ bridge_name_tracker::BridgeNameTracker,
+ function_wrapper::RustConversionType,
+ implicit_constructors::{find_constructors_present, ItemsFound},
+ overload_tracker::OverloadTracker,
+ subclass::{
+ create_subclass_constructor, create_subclass_fn_wrapper, create_subclass_function,
+ create_subclass_trait_item,
+ },
+};
+
+use super::{
+ doc_label::make_doc_attrs,
+ pod::{PodAnalysis, PodPhase},
+ tdef::TypedefAnalysis,
+ type_converter::{Annotated, PointerTreatment},
+};
+
+#[derive(Clone, Debug)]
+pub(crate) enum ReceiverMutability {
+ Const,
+ Mutable,
+}
+
+#[derive(Clone, Debug)]
+pub(crate) enum MethodKind {
+ Normal(ReceiverMutability),
+ Constructor { is_default: bool },
+ Static,
+ Virtual(ReceiverMutability),
+ PureVirtual(ReceiverMutability),
+}
+
+#[derive(Clone)]
+pub(crate) enum TraitMethodKind {
+ CopyConstructor,
+ MoveConstructor,
+ Cast,
+ Destructor,
+ Alloc,
+ Dealloc,
+}
+
+#[derive(Clone)]
+pub(crate) struct TraitMethodDetails {
+ pub(crate) trt: TraitImplSignature,
+ pub(crate) avoid_self: bool,
+ pub(crate) method_name: Ident,
+ /// For traits, where we're trying to implement a specific existing
+ /// interface, we may need to reorder the parameters to fit that
+ /// interface.
+ pub(crate) parameter_reordering: Option<Vec<usize>>,
+ /// The function we're calling from the trait requires unsafe even
+ /// though the trait and its function aren't.
+ pub(crate) trait_call_is_unsafe: bool,
+}
+
+#[derive(Clone)]
+pub(crate) enum FnKind {
+ Function,
+ Method {
+ method_kind: MethodKind,
+ impl_for: QualifiedName,
+ },
+ TraitMethod {
+ kind: TraitMethodKind,
+ /// The name of the type T for which we're implementing a trait,
+ /// though we may be actually implementing the trait for &mut T or
+ /// similar, so we store more details of both the type and the
+ /// method in `details`
+ impl_for: QualifiedName,
+ details: Box<TraitMethodDetails>,
+ },
+}
+
+/// Strategy for ensuring that the final, callable, Rust name
+/// is what the user originally expected.
+#[derive(Clone)]
+
+pub(crate) enum RustRenameStrategy {
+ /// cxx::bridge name matches user expectations
+ None,
+ /// Even the #[rust_name] attribute would cause conflicts, and we need
+ /// to use a 'use XYZ as ABC'
+ RenameInOutputMod(Ident),
+ /// This function requires us to generate a Rust function to do
+ /// parameter conversion.
+ RenameUsingWrapperFunction,
+}
+
+#[derive(Clone)]
+pub(crate) struct FnAnalysis {
+ /// Each entry in the cxx::bridge needs to have a unique name, even if
+ /// (from the perspective of Rust and C++) things are in different
+ /// namespaces/mods.
+ pub(crate) cxxbridge_name: Ident,
+ /// ... so record also the name under which we wish to expose it in Rust.
+ pub(crate) rust_name: String,
+ pub(crate) rust_rename_strategy: RustRenameStrategy,
+ pub(crate) params: Punctuated<FnArg, Comma>,
+ pub(crate) kind: FnKind,
+ pub(crate) ret_type: ReturnType,
+ pub(crate) param_details: Vec<ArgumentAnalysis>,
+ pub(crate) ret_conversion: Option<TypeConversionPolicy>,
+ pub(crate) requires_unsafe: UnsafetyNeeded,
+ pub(crate) vis: Visibility,
+ pub(crate) cpp_wrapper: Option<CppFunction>,
+ pub(crate) deps: HashSet<QualifiedName>,
+ /// Some methods still need to be recorded because we want
+ /// to (a) generate the ability to call superclasses, (b) create
+ /// subclass entries for them. But we do not want to have them
+ /// be externally callable.
+ pub(crate) ignore_reason: Result<(), ConvertErrorWithContext>,
+ /// Whether this can be called by external code. Not so for
+ /// protected methods.
+ pub(crate) externally_callable: bool,
+ /// Whether we need to generate a Rust-side calling function
+ pub(crate) rust_wrapper_needed: bool,
+}
+
+#[derive(Clone)]
+pub(crate) struct ArgumentAnalysis {
+ pub(crate) conversion: TypeConversionPolicy,
+ pub(crate) name: Pat,
+ pub(crate) self_type: Option<(QualifiedName, ReceiverMutability)>,
+ pub(crate) has_lifetime: bool,
+ pub(crate) deps: HashSet<QualifiedName>,
+ pub(crate) requires_unsafe: UnsafetyNeeded,
+ pub(crate) is_placement_return_destination: bool,
+}
+
+struct ReturnTypeAnalysis {
+ rt: ReturnType,
+ conversion: Option<TypeConversionPolicy>,
+ was_reference: bool,
+ deps: HashSet<QualifiedName>,
+ placement_param_needed: Option<(FnArg, ArgumentAnalysis)>,
+}
+
+impl Default for ReturnTypeAnalysis {
+ fn default() -> Self {
+ Self {
+ rt: parse_quote! {},
+ conversion: None,
+ was_reference: false,
+ deps: Default::default(),
+ placement_param_needed: None,
+ }
+ }
+}
+
+pub(crate) struct PodAndConstructorAnalysis {
+ pub(crate) pod: PodAnalysis,
+ pub(crate) constructors: PublicConstructors,
+}
+
+/// An analysis phase where we've analyzed each function, but
+/// haven't yet determined which constructors/etc. belong to each type.
+pub(crate) struct FnPrePhase1;
+
+impl AnalysisPhase for FnPrePhase1 {
+ type TypedefAnalysis = TypedefAnalysis;
+ type StructAnalysis = PodAnalysis;
+ type FunAnalysis = FnAnalysis;
+}
+
+/// An analysis phase where we've analyzed each function, and identified
+/// what implicit constructors/destructors are present in each type.
+pub(crate) struct FnPrePhase2;
+
+impl AnalysisPhase for FnPrePhase2 {
+ type TypedefAnalysis = TypedefAnalysis;
+ type StructAnalysis = PodAndConstructorAnalysis;
+ type FunAnalysis = FnAnalysis;
+}
+
+pub(crate) struct PodAndDepAnalysis {
+ pub(crate) pod: PodAnalysis,
+ pub(crate) constructor_and_allocator_deps: Vec<QualifiedName>,
+ pub(crate) constructors: PublicConstructors,
+}
+
+/// Analysis phase after we've finished analyzing functions and determined
+/// which constructors etc. belong to them.
+pub(crate) struct FnPhase;
+
+/// Indicates which kinds of public constructors are known to exist for a type.
+#[derive(Debug, Default, Copy, Clone)]
+pub(crate) struct PublicConstructors {
+ pub(crate) move_constructor: bool,
+ pub(crate) destructor: bool,
+}
+
+impl PublicConstructors {
+ fn from_items_found(items_found: &ItemsFound) -> Self {
+ Self {
+ move_constructor: items_found.move_constructor.callable_any(),
+ destructor: items_found.destructor.callable_any(),
+ }
+ }
+}
+
+impl AnalysisPhase for FnPhase {
+ type TypedefAnalysis = TypedefAnalysis;
+ type StructAnalysis = PodAndDepAnalysis;
+ type FunAnalysis = FnAnalysis;
+}
+
+/// Whether to allow highly optimized calls because this is a simple Rust->C++ call,
+/// or to use a simpler set of policies because this is a subclass call where
+/// we may have C++->Rust->C++ etc.
+#[derive(Copy, Clone)]
+enum TypeConversionSophistication {
+ Regular,
+ SimpleForSubclasses,
+}
+
+pub(crate) struct FnAnalyzer<'a> {
+ unsafe_policy: UnsafePolicy,
+ extra_apis: ApiVec<NullPhase>,
+ type_converter: TypeConverter<'a>,
+ bridge_name_tracker: BridgeNameTracker,
+ pod_safe_types: HashSet<QualifiedName>,
+ moveit_safe_types: HashSet<QualifiedName>,
+ config: &'a IncludeCppConfig,
+ overload_trackers_by_mod: HashMap<Namespace, OverloadTracker>,
+ subclasses_by_superclass: HashMap<QualifiedName, Vec<SubclassName>>,
+ nested_type_name_map: HashMap<QualifiedName, String>,
+ generic_types: HashSet<QualifiedName>,
+ types_in_anonymous_namespace: HashSet<QualifiedName>,
+ existing_superclass_trait_api_names: HashSet<QualifiedName>,
+}
+
+impl<'a> FnAnalyzer<'a> {
+ pub(crate) fn analyze_functions(
+ apis: ApiVec<PodPhase>,
+ unsafe_policy: UnsafePolicy,
+ config: &'a IncludeCppConfig,
+ ) -> ApiVec<FnPrePhase2> {
+ let mut me = Self {
+ unsafe_policy,
+ extra_apis: ApiVec::new(),
+ type_converter: TypeConverter::new(config, &apis),
+ bridge_name_tracker: BridgeNameTracker::new(),
+ config,
+ overload_trackers_by_mod: HashMap::new(),
+ pod_safe_types: Self::build_pod_safe_type_set(&apis),
+ moveit_safe_types: Self::build_correctly_sized_type_set(&apis),
+ subclasses_by_superclass: subclass::subclasses_by_superclass(&apis),
+ nested_type_name_map: Self::build_nested_type_map(&apis),
+ generic_types: Self::build_generic_type_set(&apis),
+ existing_superclass_trait_api_names: HashSet::new(),
+ types_in_anonymous_namespace: Self::build_types_in_anonymous_namespace(&apis),
+ };
+ let mut results = ApiVec::new();
+ convert_apis(
+ apis,
+ &mut results,
+ |name, fun, _| me.analyze_foreign_fn_and_subclasses(name, fun),
+ Api::struct_unchanged,
+ Api::enum_unchanged,
+ Api::typedef_unchanged,
+ );
+ let mut results = me.add_constructors_present(results);
+ me.add_subclass_constructors(&mut results);
+ results.extend(me.extra_apis.into_iter().map(add_analysis));
+ results
+ }
+
+ fn build_pod_safe_type_set(apis: &ApiVec<PodPhase>) -> HashSet<QualifiedName> {
+ apis.iter()
+ .filter_map(|api| match api {
+ Api::Struct {
+ analysis:
+ PodAnalysis {
+ kind: TypeKind::Pod,
+ ..
+ },
+ ..
+ } => Some(api.name().clone()),
+ Api::Enum { .. } => Some(api.name().clone()),
+ Api::ExternCppType { pod: true, .. } => Some(api.name().clone()),
+ _ => None,
+ })
+ .chain(
+ known_types()
+ .get_pod_safe_types()
+ .filter_map(
+ |(tn, is_pod_safe)| {
+ if is_pod_safe {
+ Some(tn)
+ } else {
+ None
+ }
+ },
+ ),
+ )
+ .collect()
+ }
+
+ /// Return the set of 'moveit safe' types. That must include only types where
+ /// the size is known to be correct.
+ fn build_correctly_sized_type_set(apis: &ApiVec<PodPhase>) -> HashSet<QualifiedName> {
+ apis.iter()
+ .filter(|api| {
+ matches!(
+ api,
+ Api::Struct { .. }
+ | Api::Enum { .. }
+ | Api::ExternCppType {
+ details: ExternCppType { opaque: false, .. },
+ ..
+ }
+ )
+ })
+ .map(|api| api.name().clone())
+ .chain(known_types().get_moveit_safe_types())
+ .collect()
+ }
+
+ fn build_generic_type_set(apis: &ApiVec<PodPhase>) -> HashSet<QualifiedName> {
+ apis.iter()
+ .filter_map(|api| match api {
+ Api::Struct {
+ analysis:
+ PodAnalysis {
+ is_generic: true, ..
+ },
+ ..
+ } => Some(api.name().clone()),
+ _ => None,
+ })
+ .collect()
+ }
+
+ fn build_types_in_anonymous_namespace(apis: &ApiVec<PodPhase>) -> HashSet<QualifiedName> {
+ apis.iter()
+ .filter_map(|api| match api {
+ Api::Struct {
+ analysis:
+ PodAnalysis {
+ in_anonymous_namespace: true,
+ ..
+ },
+ ..
+ } => Some(api.name().clone()),
+ _ => None,
+ })
+ .collect()
+ }
+
+ /// Builds a mapping from a qualified type name to the last 'nest'
+ /// of its name, if it has multiple elements.
+ fn build_nested_type_map(apis: &ApiVec<PodPhase>) -> HashMap<QualifiedName, String> {
+ apis.iter()
+ .filter_map(|api| match api {
+ Api::Struct { name, .. } | Api::Enum { name, .. } => {
+ let cpp_name = name
+ .cpp_name_if_present()
+ .cloned()
+ .unwrap_or_else(|| name.name.get_final_item().to_string());
+ cpp_name
+ .rsplit_once("::")
+ .map(|(_, suffix)| (name.name.clone(), suffix.to_string()))
+ }
+ _ => None,
+ })
+ .collect()
+ }
+
+ fn convert_boxed_type(
+ &mut self,
+ ty: Box<Type>,
+ ns: &Namespace,
+ pointer_treatment: PointerTreatment,
+ ) -> Result<Annotated<Box<Type>>, ConvertError> {
+ let ctx = TypeConversionContext::OuterType { pointer_treatment };
+ let mut annotated = self.type_converter.convert_boxed_type(ty, ns, &ctx)?;
+ self.extra_apis.append(&mut annotated.extra_apis);
+ Ok(annotated)
+ }
+
+ fn get_cxx_bridge_name(
+ &mut self,
+ type_name: Option<&str>,
+ found_name: &str,
+ ns: &Namespace,
+ ) -> String {
+ self.bridge_name_tracker
+ .get_unique_cxx_bridge_name(type_name, found_name, ns)
+ }
+
+ fn is_on_allowlist(&self, type_name: &QualifiedName) -> bool {
+ self.config.is_on_allowlist(&type_name.to_cpp_name())
+ }
+
+ fn is_generic_type(&self, type_name: &QualifiedName) -> bool {
+ self.generic_types.contains(type_name)
+ }
+
+ #[allow(clippy::if_same_then_else)] // clippy bug doesn't notice the two
+ // closures below are different.
+ fn should_be_unsafe(
+ &self,
+ param_details: &[ArgumentAnalysis],
+ kind: &FnKind,
+ ) -> UnsafetyNeeded {
+ let unsafest_non_placement_param = UnsafetyNeeded::from_param_details(param_details, true);
+ let unsafest_param = UnsafetyNeeded::from_param_details(param_details, false);
+ match kind {
+ // Trait unsafety must always correspond to the norms for the
+ // trait we're implementing.
+ FnKind::TraitMethod {
+ kind:
+ TraitMethodKind::CopyConstructor
+ | TraitMethodKind::MoveConstructor
+ | TraitMethodKind::Alloc
+ | TraitMethodKind::Dealloc,
+ ..
+ } => UnsafetyNeeded::Always,
+ FnKind::TraitMethod { .. } => match unsafest_param {
+ UnsafetyNeeded::Always => UnsafetyNeeded::JustBridge,
+ _ => unsafest_param,
+ },
+ _ if self.unsafe_policy == UnsafePolicy::AllFunctionsUnsafe => UnsafetyNeeded::Always,
+ _ => match unsafest_non_placement_param {
+ UnsafetyNeeded::Always => UnsafetyNeeded::Always,
+ UnsafetyNeeded::JustBridge => match unsafest_param {
+ UnsafetyNeeded::Always => UnsafetyNeeded::JustBridge,
+ _ => unsafest_non_placement_param,
+ },
+ UnsafetyNeeded::None => match unsafest_param {
+ UnsafetyNeeded::Always => UnsafetyNeeded::JustBridge,
+ _ => unsafest_param,
+ },
+ },
+ }
+ }
+
+ fn add_subclass_constructors(&mut self, apis: &mut ApiVec<FnPrePhase2>) {
+ let mut results = ApiVec::new();
+
+ // Pre-assemble a list of types with known destructors, to avoid having to
+ // do a O(n^2) nested loop.
+ let types_with_destructors: HashSet<_> = apis
+ .iter()
+ .filter_map(|api| match api {
+ Api::Function {
+ fun,
+ analysis:
+ FnAnalysis {
+ kind: FnKind::TraitMethod { impl_for, .. },
+ ..
+ },
+ ..
+ } if matches!(
+ **fun,
+ FuncToConvert {
+ special_member: Some(SpecialMemberKind::Destructor),
+ is_deleted: false,
+ cpp_vis: CppVisibility::Public,
+ ..
+ }
+ ) =>
+ {
+ Some(impl_for)
+ }
+ _ => None,
+ })
+ .cloned()
+ .collect();
+
+ for api in apis.iter() {
+ if let Api::Function {
+ fun,
+ analysis:
+ analysis @ FnAnalysis {
+ kind:
+ FnKind::Method {
+ impl_for: sup,
+ method_kind: MethodKind::Constructor { .. },
+ ..
+ },
+ ..
+ },
+ ..
+ } = api
+ {
+ // If we don't have an accessible destructor, then std::unique_ptr cannot be
+ // instantiated for this C++ type.
+ if !types_with_destructors.contains(sup) {
+ continue;
+ }
+
+ for sub in self.subclasses_by_superclass(sup) {
+ // Create a subclass constructor. This is a synthesized function
+ // which didn't exist in the original C++.
+ let (subclass_constructor_func, subclass_constructor_name) =
+ create_subclass_constructor(sub, analysis, sup, fun);
+ self.analyze_and_add(
+ subclass_constructor_name.clone(),
+ subclass_constructor_func.clone(),
+ &mut results,
+ TypeConversionSophistication::Regular,
+ );
+ }
+ }
+ }
+ apis.extend(results.into_iter());
+ }
+
+ /// Analyze a given function, and any permutations of that function which
+ /// we might additionally generate (e.g. for subclasses.)
+ ///
+ /// Leaves the [`FnKind::Method::type_constructors`] at its default for [`add_constructors_present`]
+ /// to fill out.
+ fn analyze_foreign_fn_and_subclasses(
+ &mut self,
+ name: ApiName,
+ fun: Box<FuncToConvert>,
+ ) -> Result<Box<dyn Iterator<Item = Api<FnPrePhase1>>>, ConvertErrorWithContext> {
+ let (analysis, name) =
+ self.analyze_foreign_fn(name, &fun, TypeConversionSophistication::Regular, None);
+ let mut results = ApiVec::new();
+
+ // Consider whether we need to synthesize subclass items.
+ if let FnKind::Method {
+ impl_for: sup,
+ method_kind:
+ MethodKind::Virtual(receiver_mutability) | MethodKind::PureVirtual(receiver_mutability),
+ ..
+ } = &analysis.kind
+ {
+ let (simpler_analysis, _) = self.analyze_foreign_fn(
+ name.clone(),
+ &fun,
+ TypeConversionSophistication::SimpleForSubclasses,
+ Some(analysis.rust_name.clone()),
+ );
+ for sub in self.subclasses_by_superclass(sup) {
+ // For each subclass, we need to create a plain-C++ method to call its superclass
+ // and a Rust/C++ bridge API to call _that_.
+ // What we're generating here is entirely about the subclass, so the
+ // superclass's namespace is irrelevant. We generate
+ // all subclasses in the root namespace.
+ let is_pure_virtual = matches!(
+ &simpler_analysis.kind,
+ FnKind::Method {
+ method_kind: MethodKind::PureVirtual(..),
+ ..
+ }
+ );
+
+ let super_fn_call_name =
+ SubclassName::get_super_fn_name(&Namespace::new(), &analysis.rust_name);
+ let super_fn_api_name = SubclassName::get_super_fn_name(
+ &Namespace::new(),
+ &analysis.cxxbridge_name.to_string(),
+ );
+ let trait_api_name = SubclassName::get_trait_api_name(sup, &analysis.rust_name);
+
+ let mut subclass_fn_deps = vec![trait_api_name.clone()];
+ if !is_pure_virtual {
+ // Create a C++ API representing the superclass implementation (allowing
+ // calls from Rust->C++)
+ let maybe_wrap = create_subclass_fn_wrapper(&sub, &super_fn_call_name, &fun);
+ let super_fn_name = ApiName::new_from_qualified_name(super_fn_api_name);
+ let super_fn_call_api_name = self.analyze_and_add(
+ super_fn_name,
+ maybe_wrap,
+ &mut results,
+ TypeConversionSophistication::SimpleForSubclasses,
+ );
+ subclass_fn_deps.push(super_fn_call_api_name);
+ }
+
+ // Create the Rust API representing the subclass implementation (allowing calls
+ // from C++ -> Rust)
+ results.push(create_subclass_function(
+ // RustSubclassFn
+ &sub,
+ &simpler_analysis,
+ &name,
+ receiver_mutability,
+ sup,
+ subclass_fn_deps,
+ ));
+
+ // Create the trait item for the <superclass>_methods and <superclass>_supers
+ // traits. This is required per-superclass, not per-subclass, so don't
+ // create it if it already exists.
+ if !self
+ .existing_superclass_trait_api_names
+ .contains(&trait_api_name)
+ {
+ self.existing_superclass_trait_api_names
+ .insert(trait_api_name.clone());
+ results.push(create_subclass_trait_item(
+ ApiName::new_from_qualified_name(trait_api_name),
+ &simpler_analysis,
+ receiver_mutability,
+ sup.clone(),
+ is_pure_virtual,
+ ));
+ }
+ }
+ }
+
+ results.push(Api::Function {
+ fun,
+ analysis,
+ name,
+ });
+
+ Ok(Box::new(results.into_iter()))
+ }
+
+ /// Adds an API, usually a synthesized API. Returns the final calculated API name, which can be used
+ /// for others to depend on this.
+ fn analyze_and_add<P: AnalysisPhase<FunAnalysis = FnAnalysis>>(
+ &mut self,
+ name: ApiName,
+ new_func: Box<FuncToConvert>,
+ results: &mut ApiVec<P>,
+ sophistication: TypeConversionSophistication,
+ ) -> QualifiedName {
+ let (analysis, name) = self.analyze_foreign_fn(name, &new_func, sophistication, None);
+ results.push(Api::Function {
+ fun: new_func,
+ analysis,
+ name: name.clone(),
+ });
+ name.name
+ }
+
+ /// Determine how to materialize a function.
+ ///
+ /// The main job here is to determine whether a function can simply be noted
+ /// in the [cxx::bridge] mod and passed directly to cxx, or if it needs a Rust-side
+ /// wrapper function, or if it needs a C++-side wrapper function, or both.
+ /// We aim for the simplest case but, for example:
+ /// * We'll need a C++ wrapper for static methods
+ /// * We'll need a C++ wrapper for parameters which need to be wrapped and unwrapped
+ /// to [cxx::UniquePtr]
+ /// * We'll need a Rust wrapper if we've got a C++ wrapper and it's a method.
+ /// * We may need wrappers if names conflict.
+ /// etc.
+ /// The other major thing we do here is figure out naming for the function.
+ /// This depends on overloads, and what other functions are floating around.
+ /// The output of this analysis phase is used by both Rust and C++ codegen.
+ fn analyze_foreign_fn(
+ &mut self,
+ name: ApiName,
+ fun: &FuncToConvert,
+ sophistication: TypeConversionSophistication,
+ predetermined_rust_name: Option<String>,
+ ) -> (FnAnalysis, ApiName) {
+ let mut cpp_name = name.cpp_name_if_present().cloned();
+ let ns = name.name.get_namespace();
+
+ // Let's gather some pre-wisdom about the name of the function.
+ // We're shortly going to plunge into analyzing the parameters,
+ // and it would be nice to have some idea of the function name
+ // for diagnostics whilst we do that.
+ let initial_rust_name = fun.ident.to_string();
+ let diagnostic_display_name = cpp_name.as_ref().unwrap_or(&initial_rust_name);
+
+ // Now let's analyze all the parameters.
+ // See if any have annotations which our fork of bindgen has craftily inserted...
+ let (param_details, bads): (Vec<_>, Vec<_>) = fun
+ .inputs
+ .iter()
+ .map(|i| {
+ self.convert_fn_arg(
+ i,
+ ns,
+ diagnostic_display_name,
+ &fun.synthesized_this_type,
+ &fun.references,
+ true,
+ false,
+ None,
+ sophistication,
+ false,
+ )
+ })
+ .partition(Result::is_ok);
+ let (mut params, mut param_details): (Punctuated<_, Comma>, Vec<_>) =
+ param_details.into_iter().map(Result::unwrap).unzip();
+
+ let params_deps: HashSet<_> = param_details
+ .iter()
+ .flat_map(|p| p.deps.iter().cloned())
+ .collect();
+ let self_ty = param_details
+ .iter()
+ .filter_map(|pd| pd.self_type.as_ref())
+ .next()
+ .cloned();
+
+ // End of parameter processing.
+ // Work out naming, part one.
+ // bindgen may have mangled the name either because it's invalid Rust
+ // syntax (e.g. a keyword like 'async') or it's an overload.
+ // If the former, we respect that mangling. If the latter, we don't,
+ // because we'll add our own overload counting mangling later.
+ // Cases:
+ // function, IRN=foo, CN=<none> output: foo case 1
+ // function, IRN=move_, CN=move (keyword problem) output: move_ case 2
+ // function, IRN=foo1, CN=foo (overload) output: foo case 3
+ // method, IRN=A_foo, CN=foo output: foo case 4
+ // method, IRN=A_move, CN=move (keyword problem) output: move_ case 5
+ // method, IRN=A_foo1, CN=foo (overload) output: foo case 6
+ let ideal_rust_name = match &cpp_name {
+ None => initial_rust_name, // case 1
+ Some(cpp_name) => {
+ if initial_rust_name.ends_with('_') {
+ initial_rust_name // case 2
+ } else if validate_ident_ok_for_rust(cpp_name).is_err() {
+ format!("{}_", cpp_name) // case 5
+ } else {
+ cpp_name.to_string() // cases 3, 4, 6
+ }
+ }
+ };
+
+ // Let's spend some time figuring out the kind of this function (i.e. method,
+ // virtual function, etc.)
+ // Part one, work out if this is a static method.
+ let (is_static_method, self_ty, receiver_mutability) = match self_ty {
+ None => {
+ // Even if we can't find a 'self' parameter this could conceivably
+ // be a static method.
+ let self_ty = fun.self_ty.clone();
+ (self_ty.is_some(), self_ty, None)
+ }
+ Some((self_ty, receiver_mutability)) => {
+ (false, Some(self_ty), Some(receiver_mutability))
+ }
+ };
+
+ // Part two, work out if this is a function, or method, or whatever.
+ // First determine if this is actually a trait implementation.
+ let trait_details = self.trait_creation_details_for_synthetic_function(
+ &fun.add_to_trait,
+ ns,
+ &ideal_rust_name,
+ &self_ty,
+ );
+ let (kind, error_context, rust_name) = if let Some(trait_details) = trait_details {
+ trait_details
+ } else if let Some(self_ty) = self_ty {
+ // Some kind of method or static method.
+ let type_ident = self_ty.get_final_item();
+ // bindgen generates methods with the name:
+ // {class}_{method name}
+ // It then generates an impl section for the Rust type
+ // with the original name, but we currently discard that impl section.
+ // We want to feed cxx methods with just the method name, so let's
+ // strip off the class name.
+ let mut rust_name = ideal_rust_name;
+ let nested_type_ident = self
+ .nested_type_name_map
+ .get(&self_ty)
+ .map(|s| s.as_str())
+ .unwrap_or_else(|| self_ty.get_final_item());
+ if matches!(
+ fun.special_member,
+ Some(SpecialMemberKind::CopyConstructor | SpecialMemberKind::MoveConstructor)
+ ) {
+ let is_move =
+ matches!(fun.special_member, Some(SpecialMemberKind::MoveConstructor));
+ if let Some(constructor_suffix) = rust_name.strip_prefix(nested_type_ident) {
+ rust_name = format!("new{}", constructor_suffix);
+ }
+ rust_name = predetermined_rust_name
+ .unwrap_or_else(|| self.get_overload_name(ns, type_ident, rust_name));
+ let error_context = self.error_context_for_method(&self_ty, &rust_name);
+
+ // If this is 'None', then something weird is going on. We'll check for that
+ // later when we have enough context to generate useful errors.
+ let arg_is_reference = matches!(
+ param_details
+ .get(1)
+ .map(|param| ¶m.conversion.unwrapped_type),
+ Some(Type::Reference(_))
+ );
+ // Some exotic forms of copy constructor have const and/or volatile qualifiers.
+ // These are not sufficient to implement CopyNew, so we just treat them as regular
+ // constructors. We detect them by their argument being translated to Pin at this
+ // point.
+ if is_move || arg_is_reference {
+ let (kind, method_name, trait_id) = if is_move {
+ (
+ TraitMethodKind::MoveConstructor,
+ "move_new",
+ quote! { MoveNew },
+ )
+ } else {
+ (
+ TraitMethodKind::CopyConstructor,
+ "copy_new",
+ quote! { CopyNew },
+ )
+ };
+ let ty = Type::Path(self_ty.to_type_path());
+ (
+ FnKind::TraitMethod {
+ kind,
+ impl_for: self_ty,
+ details: Box::new(TraitMethodDetails {
+ trt: TraitImplSignature {
+ ty,
+ trait_signature: parse_quote! {
+ autocxx::moveit::new:: #trait_id
+ },
+ unsafety: Some(parse_quote! { unsafe }),
+ },
+ avoid_self: true,
+ method_name: make_ident(method_name),
+ parameter_reordering: Some(vec![1, 0]),
+ trait_call_is_unsafe: false,
+ }),
+ },
+ error_context,
+ rust_name,
+ )
+ } else {
+ (
+ FnKind::Method {
+ impl_for: self_ty,
+ method_kind: MethodKind::Constructor { is_default: false },
+ },
+ error_context,
+ rust_name,
+ )
+ }
+ } else if matches!(fun.special_member, Some(SpecialMemberKind::Destructor)) {
+ rust_name = predetermined_rust_name
+ .unwrap_or_else(|| self.get_overload_name(ns, type_ident, rust_name));
+ let error_context = self.error_context_for_method(&self_ty, &rust_name);
+ let ty = Type::Path(self_ty.to_type_path());
+ (
+ FnKind::TraitMethod {
+ kind: TraitMethodKind::Destructor,
+ impl_for: self_ty,
+ details: Box::new(TraitMethodDetails {
+ trt: TraitImplSignature {
+ ty,
+ trait_signature: parse_quote! {
+ Drop
+ },
+ unsafety: None,
+ },
+ avoid_self: false,
+ method_name: make_ident("drop"),
+ parameter_reordering: None,
+ trait_call_is_unsafe: false,
+ }),
+ },
+ error_context,
+ rust_name,
+ )
+ } else {
+ let method_kind = if let Some(constructor_suffix) =
+ constructor_with_suffix(&rust_name, nested_type_ident)
+ {
+ // It's a constructor. bindgen generates
+ // fn Type(this: *mut Type, ...args)
+ // We want
+ // fn new(this: *mut Type, ...args)
+ // Later code will spot this and re-enter us, and we'll make
+ // a duplicate function in the above 'if' clause like this:
+ // fn make_unique(...args) -> Type
+ // which later code will convert to
+ // fn make_unique(...args) -> UniquePtr<Type>
+ // If there are multiple constructors, bindgen generates
+ // new, new1, new2 etc. and we'll keep those suffixes.
+ rust_name = format!("new{}", constructor_suffix);
+ MethodKind::Constructor {
+ is_default: matches!(
+ fun.special_member,
+ Some(SpecialMemberKind::DefaultConstructor)
+ ),
+ }
+ } else if is_static_method {
+ MethodKind::Static
+ } else {
+ let receiver_mutability =
+ receiver_mutability.expect("Failed to find receiver details");
+ match fun.virtualness {
+ Virtualness::None => MethodKind::Normal(receiver_mutability),
+ Virtualness::Virtual => MethodKind::Virtual(receiver_mutability),
+ Virtualness::PureVirtual => MethodKind::PureVirtual(receiver_mutability),
+ }
+ };
+ // Disambiguate overloads.
+ let rust_name = predetermined_rust_name
+ .unwrap_or_else(|| self.get_overload_name(ns, type_ident, rust_name));
+ let error_context = self.error_context_for_method(&self_ty, &rust_name);
+ (
+ FnKind::Method {
+ impl_for: self_ty,
+ method_kind,
+ },
+ error_context,
+ rust_name,
+ )
+ }
+ } else {
+ // Not a method.
+ // What shall we call this function? It may be overloaded.
+ let rust_name = self.get_function_overload_name(ns, ideal_rust_name);
+ (
+ FnKind::Function,
+ ErrorContext::new_for_item(make_ident(&rust_name)),
+ rust_name,
+ )
+ };
+
+ // If we encounter errors from here on, we can give some context around
+ // where the error occurred such that we can put a marker in the output
+ // Rust code to indicate that a problem occurred (benefiting people using
+ // rust-analyzer or similar). Make a closure to make this easy.
+ let mut ignore_reason = Ok(());
+ let mut set_ignore_reason =
+ |err| ignore_reason = Err(ConvertErrorWithContext(err, Some(error_context.clone())));
+
+ // Now we have figured out the type of function (from its parameters)
+ // we might have determined that we have a constructor. If so,
+ // annoyingly, we need to go back and fiddle with the parameters in a
+ // different way. This is because we want the first parameter to be a
+ // pointer not a reference. For copy + move constructors, we also
+ // enforce Rust-side conversions to comply with moveit traits.
+ match kind {
+ FnKind::Method {
+ method_kind: MethodKind::Constructor { .. },
+ ..
+ } => {
+ self.reanalyze_parameter(
+ 0,
+ fun,
+ ns,
+ &rust_name,
+ &mut params,
+ &mut param_details,
+ None,
+ sophistication,
+ true,
+ false,
+ )
+ .unwrap_or_else(&mut set_ignore_reason);
+ }
+
+ FnKind::TraitMethod {
+ kind: TraitMethodKind::Destructor,
+ ..
+ } => {
+ self.reanalyze_parameter(
+ 0,
+ fun,
+ ns,
+ &rust_name,
+ &mut params,
+ &mut param_details,
+ Some(RustConversionType::FromTypeToPtr),
+ sophistication,
+ false,
+ false,
+ )
+ .unwrap_or_else(&mut set_ignore_reason);
+ }
+ FnKind::TraitMethod {
+ kind: TraitMethodKind::CopyConstructor,
+ ..
+ } => {
+ if param_details.len() < 2 {
+ set_ignore_reason(ConvertError::ConstructorWithOnlyOneParam);
+ }
+ if param_details.len() > 2 {
+ set_ignore_reason(ConvertError::ConstructorWithMultipleParams);
+ }
+ self.reanalyze_parameter(
+ 0,
+ fun,
+ ns,
+ &rust_name,
+ &mut params,
+ &mut param_details,
+ Some(RustConversionType::FromPinMaybeUninitToPtr),
+ sophistication,
+ false,
+ false,
+ )
+ .unwrap_or_else(&mut set_ignore_reason);
+ }
+
+ FnKind::TraitMethod {
+ kind: TraitMethodKind::MoveConstructor,
+ ..
+ } => {
+ if param_details.len() < 2 {
+ set_ignore_reason(ConvertError::ConstructorWithOnlyOneParam);
+ }
+ if param_details.len() > 2 {
+ set_ignore_reason(ConvertError::ConstructorWithMultipleParams);
+ }
+ self.reanalyze_parameter(
+ 0,
+ fun,
+ ns,
+ &rust_name,
+ &mut params,
+ &mut param_details,
+ Some(RustConversionType::FromPinMaybeUninitToPtr),
+ sophistication,
+ false,
+ false,
+ )
+ .unwrap_or_else(&mut set_ignore_reason);
+ self.reanalyze_parameter(
+ 1,
+ fun,
+ ns,
+ &rust_name,
+ &mut params,
+ &mut param_details,
+ Some(RustConversionType::FromPinMoveRefToPtr),
+ sophistication,
+ false,
+ true,
+ )
+ .unwrap_or_else(&mut set_ignore_reason);
+ }
+ _ => {}
+ }
+
+ // Now we can add context to the error, check for a variety of error
+ // cases. In each case, we continue to record the API, because it might
+ // influence our later decisions to generate synthetic constructors
+ // or note whether the type is abstract.
+ let externally_callable = match fun.cpp_vis {
+ CppVisibility::Private => {
+ set_ignore_reason(ConvertError::PrivateMethod);
+ false
+ }
+ CppVisibility::Protected => false,
+ CppVisibility::Public => true,
+ };
+ if fun.variadic {
+ set_ignore_reason(ConvertError::Variadic);
+ }
+ if let Some(problem) = bads.into_iter().next() {
+ match problem {
+ Ok(_) => panic!("No error in the error"),
+ Err(problem) => set_ignore_reason(problem),
+ }
+ } else if fun.unused_template_param {
+ // This indicates that bindgen essentially flaked out because templates
+ // were too complex.
+ set_ignore_reason(ConvertError::UnusedTemplateParam)
+ } else if matches!(
+ fun.special_member,
+ Some(SpecialMemberKind::AssignmentOperator)
+ ) {
+ // Be careful with the order of this if-else tree. Anything above here means we won't
+ // treat it as an assignment operator, but anything below we still consider when
+ // deciding which other C++ special member functions are implicitly defined.
+ set_ignore_reason(ConvertError::AssignmentOperator)
+ } else if fun.references.rvalue_ref_return {
+ set_ignore_reason(ConvertError::RValueReturn)
+ } else if fun.is_deleted {
+ set_ignore_reason(ConvertError::Deleted)
+ } else {
+ match kind {
+ FnKind::Method {
+ ref impl_for,
+ method_kind:
+ MethodKind::Constructor { .. }
+ | MethodKind::Normal(..)
+ | MethodKind::PureVirtual(..)
+ | MethodKind::Virtual(..),
+ ..
+ } if !known_types().is_cxx_acceptable_receiver(impl_for) => {
+ set_ignore_reason(ConvertError::UnsupportedReceiver);
+ }
+ FnKind::Method { ref impl_for, .. } if !self.is_on_allowlist(impl_for) => {
+ // Bindgen will output methods for types which have been encountered
+ // virally as arguments on other allowlisted types. But we don't want
+ // to generate methods unless the user has specifically asked us to.
+ // It may, for instance, be a private type.
+ set_ignore_reason(ConvertError::MethodOfNonAllowlistedType);
+ }
+ FnKind::Method { ref impl_for, .. } | FnKind::TraitMethod { ref impl_for, .. } => {
+ if self.is_generic_type(impl_for) {
+ set_ignore_reason(ConvertError::MethodOfGenericType);
+ }
+ if self.types_in_anonymous_namespace.contains(impl_for) {
+ set_ignore_reason(ConvertError::MethodInAnonymousNamespace);
+ }
+ }
+ _ => {}
+ }
+ };
+
+ // The name we use within the cxx::bridge mod may be different
+ // from both the C++ name and the Rust name, because it's a flat
+ // namespace so we might need to prepend some stuff to make it unique.
+ let cxxbridge_name = self.get_cxx_bridge_name(
+ match kind {
+ FnKind::Method { ref impl_for, .. } => Some(impl_for.get_final_item()),
+ FnKind::Function => None,
+ FnKind::TraitMethod { ref impl_for, .. } => Some(impl_for.get_final_item()),
+ },
+ &rust_name,
+ ns,
+ );
+ if cxxbridge_name != rust_name && cpp_name.is_none() {
+ cpp_name = Some(rust_name.clone());
+ }
+ let mut cxxbridge_name = make_ident(&cxxbridge_name);
+
+ // Analyze the return type, just as we previously did for the
+ // parameters.
+ let mut return_analysis = self
+ .convert_return_type(&fun.output, ns, &fun.references, sophistication)
+ .unwrap_or_else(|err| {
+ set_ignore_reason(err);
+ ReturnTypeAnalysis::default()
+ });
+ let mut deps = params_deps;
+ deps.extend(return_analysis.deps.drain(..));
+
+ // Sometimes, the return type will actually be a value type
+ // for which we instead want to _pass_ a pointer into which the value
+ // can be constructed. Handle that case here.
+ if let Some((extra_param, extra_param_details)) = return_analysis.placement_param_needed {
+ param_details.push(extra_param_details);
+ params.push(extra_param);
+ }
+
+ let requires_unsafe = self.should_be_unsafe(¶m_details, &kind);
+
+ let num_input_references = param_details.iter().filter(|pd| pd.has_lifetime).count();
+ if num_input_references != 1 && return_analysis.was_reference {
+ // cxx only allows functions to return a reference if they take exactly
+ // one reference as a parameter. Let's see...
+ set_ignore_reason(ConvertError::NotOneInputReference(rust_name.clone()));
+ }
+ let mut ret_type = return_analysis.rt;
+ let ret_type_conversion = return_analysis.conversion;
+
+ // Do we need to convert either parameters or return type?
+ let param_conversion_needed = param_details.iter().any(|b| b.conversion.cpp_work_needed());
+ let ret_type_conversion_needed = ret_type_conversion
+ .as_ref()
+ .map_or(false, |x| x.cpp_work_needed());
+ // See https://github.com/dtolnay/cxx/issues/878 for the reason for this next line.
+ let effective_cpp_name = cpp_name.as_ref().unwrap_or(&rust_name);
+ let cpp_name_incompatible_with_cxx =
+ validate_ident_ok_for_rust(effective_cpp_name).is_err();
+ // If possible, we'll put knowledge of the C++ API directly into the cxx::bridge
+ // mod. However, there are various circumstances where cxx can't work with the existing
+ // C++ API and we need to create a C++ wrapper function which is more cxx-compliant.
+ // That wrapper function is included in the cxx::bridge, and calls through to the
+ // original function.
+ let wrapper_function_needed = match kind {
+ FnKind::Method {
+ method_kind:
+ MethodKind::Static
+ | MethodKind::Constructor { .. }
+ | MethodKind::Virtual(_)
+ | MethodKind::PureVirtual(_),
+ ..
+ }
+ | FnKind::TraitMethod {
+ kind:
+ TraitMethodKind::CopyConstructor
+ | TraitMethodKind::MoveConstructor
+ | TraitMethodKind::Destructor,
+ ..
+ } => true,
+ FnKind::Method { .. } if cxxbridge_name != rust_name => true,
+ _ if param_conversion_needed => true,
+ _ if ret_type_conversion_needed => true,
+ _ if cpp_name_incompatible_with_cxx => true,
+ _ if fun.synthetic_cpp.is_some() => true,
+ _ => false,
+ };
+
+ let cpp_wrapper = if wrapper_function_needed {
+ // Generate a new layer of C++ code to wrap/unwrap parameters
+ // and return values into/out of std::unique_ptrs.
+ let cpp_construction_ident = make_ident(&effective_cpp_name);
+ let joiner = if cxxbridge_name.to_string().ends_with('_') {
+ ""
+ } else {
+ "_"
+ };
+ cxxbridge_name = make_ident(&format!("{}{}autocxx_wrapper", cxxbridge_name, joiner));
+ let (payload, cpp_function_kind) = match fun.synthetic_cpp.as_ref().cloned() {
+ Some((payload, cpp_function_kind)) => (payload, cpp_function_kind),
+ None => match kind {
+ FnKind::Method {
+ ref impl_for,
+ method_kind: MethodKind::Constructor { .. },
+ ..
+ }
+ | FnKind::TraitMethod {
+ kind: TraitMethodKind::CopyConstructor | TraitMethodKind::MoveConstructor,
+ ref impl_for,
+ ..
+ } => (
+ CppFunctionBody::PlacementNew(ns.clone(), impl_for.get_final_ident()),
+ CppFunctionKind::Constructor,
+ ),
+ FnKind::TraitMethod {
+ kind: TraitMethodKind::Destructor,
+ ref impl_for,
+ ..
+ } => (
+ CppFunctionBody::Destructor(ns.clone(), impl_for.get_final_ident()),
+ CppFunctionKind::Function,
+ ),
+ FnKind::Method {
+ ref impl_for,
+ method_kind: MethodKind::Static,
+ ..
+ } => (
+ CppFunctionBody::StaticMethodCall(
+ ns.clone(),
+ impl_for.get_final_ident(),
+ cpp_construction_ident,
+ ),
+ CppFunctionKind::Function,
+ ),
+ FnKind::Method { .. } => (
+ CppFunctionBody::FunctionCall(ns.clone(), cpp_construction_ident),
+ CppFunctionKind::Method,
+ ),
+ _ => (
+ CppFunctionBody::FunctionCall(ns.clone(), cpp_construction_ident),
+ CppFunctionKind::Function,
+ ),
+ },
+ };
+ // Now modify the cxx::bridge entry we're going to make.
+ if let Some(ref conversion) = ret_type_conversion {
+ if conversion.populate_return_value() {
+ let new_ret_type = conversion.unconverted_rust_type();
+ ret_type = parse_quote!(
+ -> #new_ret_type
+ );
+ }
+ }
+
+ // Amend parameters for the function which we're asking cxx to generate.
+ params.clear();
+ for pd in ¶m_details {
+ let type_name = pd.conversion.converted_rust_type();
+ let arg_name = if pd.self_type.is_some() {
+ parse_quote!(autocxx_gen_this)
+ } else {
+ pd.name.clone()
+ };
+ params.push(parse_quote!(
+ #arg_name: #type_name
+ ));
+ }
+
+ Some(CppFunction {
+ payload,
+ wrapper_function_name: cxxbridge_name.clone(),
+ original_cpp_name: cpp_name
+ .as_ref()
+ .cloned()
+ .unwrap_or_else(|| cxxbridge_name.to_string()),
+ return_conversion: ret_type_conversion.clone(),
+ argument_conversion: param_details.iter().map(|d| d.conversion.clone()).collect(),
+ kind: cpp_function_kind,
+ pass_obs_field: false,
+ qualification: None,
+ })
+ } else {
+ None
+ };
+
+ let vis = fun.vis.clone();
+
+ let any_param_needs_rust_conversion = param_details
+ .iter()
+ .any(|pd| pd.conversion.rust_work_needed());
+
+ let rust_wrapper_needed = match kind {
+ FnKind::TraitMethod { .. } => true,
+ FnKind::Method { .. } => any_param_needs_rust_conversion || cxxbridge_name != rust_name,
+ _ => any_param_needs_rust_conversion,
+ };
+
+ // Naming, part two.
+ // Work out our final naming strategy.
+ validate_ident_ok_for_cxx(&cxxbridge_name.to_string()).unwrap_or_else(set_ignore_reason);
+ let rust_name_ident = make_ident(&rust_name);
+ let rust_rename_strategy = match kind {
+ _ if rust_wrapper_needed => RustRenameStrategy::RenameUsingWrapperFunction,
+ FnKind::Function if cxxbridge_name != rust_name => {
+ RustRenameStrategy::RenameInOutputMod(rust_name_ident)
+ }
+ _ => RustRenameStrategy::None,
+ };
+
+ let analysis = FnAnalysis {
+ cxxbridge_name: cxxbridge_name.clone(),
+ rust_name: rust_name.clone(),
+ rust_rename_strategy,
+ params,
+ ret_conversion: ret_type_conversion,
+ kind,
+ ret_type,
+ param_details,
+ requires_unsafe,
+ vis,
+ cpp_wrapper,
+ deps,
+ ignore_reason,
+ externally_callable,
+ rust_wrapper_needed,
+ };
+ let name = ApiName::new_with_cpp_name(ns, cxxbridge_name, cpp_name);
+ (analysis, name)
+ }
+
+ fn error_context_for_method(&self, self_ty: &QualifiedName, rust_name: &str) -> ErrorContext {
+ if self.is_generic_type(self_ty) {
+ // A 'method' error context would end up in an
+ // impl A {
+ // fn error_thingy
+ // }
+ // block. We can't impl A if it would need to be impl A<B>
+ ErrorContext::new_for_item(make_ident(rust_name))
+ } else {
+ ErrorContext::new_for_method(self_ty.get_final_ident(), make_ident(rust_name))
+ }
+ }
+
+ /// Applies a specific `force_rust_conversion` to the parameter at index
+ /// `param_idx`. Modifies `param_details` and `params` in place.
+ #[allow(clippy::too_many_arguments)] // it's true, but sticking with it for now
+ fn reanalyze_parameter(
+ &mut self,
+ param_idx: usize,
+ fun: &FuncToConvert,
+ ns: &Namespace,
+ rust_name: &str,
+ params: &mut Punctuated<FnArg, Comma>,
+ param_details: &mut [ArgumentAnalysis],
+ force_rust_conversion: Option<RustConversionType>,
+ sophistication: TypeConversionSophistication,
+ construct_into_self: bool,
+ is_move_constructor: bool,
+ ) -> Result<(), ConvertError> {
+ self.convert_fn_arg(
+ fun.inputs.iter().nth(param_idx).unwrap(),
+ ns,
+ rust_name,
+ &fun.synthesized_this_type,
+ &fun.references,
+ false,
+ is_move_constructor,
+ force_rust_conversion,
+ sophistication,
+ construct_into_self,
+ )
+ .map(|(new_arg, new_analysis)| {
+ param_details[param_idx] = new_analysis;
+ let mut params_before = params.clone().into_iter();
+ let prefix = params_before
+ .by_ref()
+ .take(param_idx)
+ .collect_vec()
+ .into_iter();
+ let suffix = params_before.skip(1);
+ *params = prefix
+ .chain(std::iter::once(new_arg))
+ .chain(suffix)
+ .collect()
+ })
+ }
+
+ fn get_overload_name(&mut self, ns: &Namespace, type_ident: &str, rust_name: String) -> String {
+ let overload_tracker = self.overload_trackers_by_mod.entry(ns.clone()).or_default();
+ overload_tracker.get_method_real_name(type_ident, rust_name)
+ }
+
+ /// Determine if this synthetic function should actually result in the implementation
+ /// of a trait, rather than a function/method.
+ fn trait_creation_details_for_synthetic_function(
+ &mut self,
+ synthesis: &Option<TraitSynthesis>,
+ ns: &Namespace,
+ ideal_rust_name: &str,
+ self_ty: &Option<QualifiedName>,
+ ) -> Option<(FnKind, ErrorContext, String)> {
+ synthesis.as_ref().and_then(|synthesis| match synthesis {
+ TraitSynthesis::Cast { to_type, mutable } => {
+ let rust_name = self.get_function_overload_name(ns, ideal_rust_name.to_string());
+ let from_type = self_ty.as_ref().unwrap();
+ let from_type_path = from_type.to_type_path();
+ let to_type = to_type.to_type_path();
+ let (trait_signature, ty, method_name) = match *mutable {
+ CastMutability::ConstToConst => (
+ parse_quote! {
+ AsRef < #to_type >
+ },
+ Type::Path(from_type_path),
+ "as_ref",
+ ),
+ CastMutability::MutToConst => (
+ parse_quote! {
+ AsRef < #to_type >
+ },
+ parse_quote! {
+ &'a mut ::std::pin::Pin < &'a mut #from_type_path >
+ },
+ "as_ref",
+ ),
+ CastMutability::MutToMut => (
+ parse_quote! {
+ autocxx::PinMut < #to_type >
+ },
+ parse_quote! {
+ ::std::pin::Pin < &'a mut #from_type_path >
+ },
+ "pin_mut",
+ ),
+ };
+ let method_name = make_ident(method_name);
+ Some((
+ FnKind::TraitMethod {
+ kind: TraitMethodKind::Cast,
+ impl_for: from_type.clone(),
+ details: Box::new(TraitMethodDetails {
+ trt: TraitImplSignature {
+ ty,
+ trait_signature,
+ unsafety: None,
+ },
+ avoid_self: false,
+ method_name,
+ parameter_reordering: None,
+ trait_call_is_unsafe: false,
+ }),
+ },
+ ErrorContext::new_for_item(make_ident(&rust_name)),
+ rust_name,
+ ))
+ }
+ TraitSynthesis::AllocUninitialized(ty) => self.generate_alloc_or_deallocate(
+ ideal_rust_name,
+ ty,
+ "allocate_uninitialized_cpp_storage",
+ TraitMethodKind::Alloc,
+ ),
+ TraitSynthesis::FreeUninitialized(ty) => self.generate_alloc_or_deallocate(
+ ideal_rust_name,
+ ty,
+ "free_uninitialized_cpp_storage",
+ TraitMethodKind::Dealloc,
+ ),
+ })
+ }
+
+ fn generate_alloc_or_deallocate(
+ &mut self,
+ ideal_rust_name: &str,
+ ty: &QualifiedName,
+ method_name: &str,
+ kind: TraitMethodKind,
+ ) -> Option<(FnKind, ErrorContext, String)> {
+ let rust_name =
+ self.get_function_overload_name(ty.get_namespace(), ideal_rust_name.to_string());
+ let typ = ty.to_type_path();
+ Some((
+ FnKind::TraitMethod {
+ impl_for: ty.clone(),
+ details: Box::new(TraitMethodDetails {
+ trt: TraitImplSignature {
+ ty: Type::Path(typ),
+ trait_signature: parse_quote! { autocxx::moveit::MakeCppStorage },
+ unsafety: Some(parse_quote! { unsafe }),
+ },
+ avoid_self: false,
+ method_name: make_ident(method_name),
+ parameter_reordering: None,
+ trait_call_is_unsafe: false,
+ }),
+ kind,
+ },
+ ErrorContext::new_for_item(make_ident(&rust_name)),
+ rust_name,
+ ))
+ }
+
+ fn get_function_overload_name(&mut self, ns: &Namespace, ideal_rust_name: String) -> String {
+ let overload_tracker = self.overload_trackers_by_mod.entry(ns.clone()).or_default();
+ overload_tracker.get_function_real_name(ideal_rust_name)
+ }
+
+ fn subclasses_by_superclass(&self, sup: &QualifiedName) -> impl Iterator<Item = SubclassName> {
+ match self.subclasses_by_superclass.get(sup) {
+ Some(subs) => subs.clone().into_iter(),
+ None => Vec::new().into_iter(),
+ }
+ }
+
+ #[allow(clippy::too_many_arguments)] // currently reasonably clear
+ fn convert_fn_arg(
+ &mut self,
+ arg: &FnArg,
+ ns: &Namespace,
+ fn_name: &str,
+ virtual_this: &Option<QualifiedName>,
+ references: &References,
+ treat_this_as_reference: bool,
+ is_move_constructor: bool,
+ force_rust_conversion: Option<RustConversionType>,
+ sophistication: TypeConversionSophistication,
+ construct_into_self: bool,
+ ) -> Result<(FnArg, ArgumentAnalysis), ConvertError> {
+ Ok(match arg {
+ FnArg::Typed(pt) => {
+ let mut pt = pt.clone();
+ let mut self_type = None;
+ let old_pat = *pt.pat;
+ let mut pointer_treatment = PointerTreatment::Pointer;
+ let mut is_placement_return_destination = false;
+ let new_pat = match old_pat {
+ syn::Pat::Ident(mut pp) if pp.ident == "this" => {
+ let this_type = match pt.ty.as_ref() {
+ Type::Ptr(TypePtr {
+ elem, mutability, ..
+ }) => match elem.as_ref() {
+ Type::Path(typ) => {
+ let receiver_mutability = if mutability.is_some() {
+ ReceiverMutability::Mutable
+ } else {
+ ReceiverMutability::Const
+ };
+
+ let this_type = if let Some(virtual_this) = virtual_this {
+ let this_type_path = virtual_this.to_type_path();
+ let const_token = if mutability.is_some() {
+ None
+ } else {
+ Some(syn::Token))
+ };
+ pt.ty = Box::new(parse_quote! {
+ * #mutability #const_token #this_type_path
+ });
+ virtual_this.clone()
+ } else {
+ QualifiedName::from_type_path(typ)
+ };
+ Ok((this_type, receiver_mutability))
+ }
+ _ => Err(ConvertError::UnexpectedThisType(QualifiedName::new(
+ ns,
+ make_ident(fn_name),
+ ))),
+ },
+ _ => Err(ConvertError::UnexpectedThisType(QualifiedName::new(
+ ns,
+ make_ident(fn_name),
+ ))),
+ }?;
+ self_type = Some(this_type);
+ is_placement_return_destination = construct_into_self;
+ if treat_this_as_reference {
+ pp.ident = Ident::new("self", pp.ident.span());
+ pointer_treatment = PointerTreatment::Reference;
+ }
+ syn::Pat::Ident(pp)
+ }
+ syn::Pat::Ident(pp) => {
+ validate_ident_ok_for_cxx(&pp.ident.to_string())?;
+ pointer_treatment = references.param_treatment(&pp.ident);
+ syn::Pat::Ident(pp)
+ }
+ _ => old_pat,
+ };
+ let is_placement_return_destination = is_placement_return_destination
+ || matches!(
+ force_rust_conversion,
+ Some(RustConversionType::FromPlacementParamToNewReturn)
+ );
+ let annotated_type = self.convert_boxed_type(pt.ty, ns, pointer_treatment)?;
+ let conversion = self.argument_conversion_details(
+ &annotated_type,
+ is_move_constructor,
+ force_rust_conversion,
+ sophistication,
+ );
+ let new_ty = annotated_type.ty;
+ pt.pat = Box::new(new_pat.clone());
+ pt.ty = new_ty;
+ let requires_unsafe =
+ if matches!(annotated_type.kind, type_converter::TypeKind::Pointer)
+ && !is_placement_return_destination
+ {
+ UnsafetyNeeded::Always
+ } else if conversion.bridge_unsafe_needed() || is_placement_return_destination {
+ UnsafetyNeeded::JustBridge
+ } else {
+ UnsafetyNeeded::None
+ };
+ (
+ FnArg::Typed(pt),
+ ArgumentAnalysis {
+ self_type,
+ name: new_pat,
+ conversion,
+ has_lifetime: matches!(
+ annotated_type.kind,
+ type_converter::TypeKind::Reference
+ | type_converter::TypeKind::MutableReference
+ ),
+ deps: annotated_type.types_encountered,
+ requires_unsafe,
+ is_placement_return_destination,
+ },
+ )
+ }
+ _ => panic!("Did not expect FnArg::Receiver to be generated by bindgen"),
+ })
+ }
+
+ fn argument_conversion_details(
+ &self,
+ annotated_type: &Annotated<Box<Type>>,
+ is_move_constructor: bool,
+ force_rust_conversion: Option<RustConversionType>,
+ sophistication: TypeConversionSophistication,
+ ) -> TypeConversionPolicy {
+ let is_subclass_holder = match &annotated_type.kind {
+ type_converter::TypeKind::SubclassHolder(holder) => Some(holder),
+ _ => None,
+ };
+ let is_rvalue_ref = matches!(
+ annotated_type.kind,
+ type_converter::TypeKind::RValueReference
+ );
+ let ty = &*annotated_type.ty;
+ if let Some(holder_id) = is_subclass_holder {
+ let subclass = SubclassName::from_holder_name(holder_id);
+ return {
+ let ty = parse_quote! {
+ rust::Box<#holder_id>
+ };
+ TypeConversionPolicy {
+ unwrapped_type: ty,
+ cpp_conversion: CppConversionType::Move,
+ rust_conversion: RustConversionType::ToBoxedUpHolder(subclass),
+ }
+ };
+ } else if matches!(
+ force_rust_conversion,
+ Some(RustConversionType::FromPlacementParamToNewReturn)
+ ) && matches!(sophistication, TypeConversionSophistication::Regular)
+ {
+ return TypeConversionPolicy {
+ unwrapped_type: ty.clone(),
+ cpp_conversion: CppConversionType::IgnoredPlacementPtrParameter,
+ rust_conversion: RustConversionType::FromPlacementParamToNewReturn,
+ };
+ }
+ match ty {
+ Type::Path(p) => {
+ let ty = ty.clone();
+ let tn = QualifiedName::from_type_path(p);
+ if self.pod_safe_types.contains(&tn) {
+ if known_types().lacks_copy_constructor(&tn) {
+ TypeConversionPolicy {
+ unwrapped_type: ty,
+ cpp_conversion: CppConversionType::Move,
+ rust_conversion: RustConversionType::None,
+ }
+ } else {
+ TypeConversionPolicy::new_unconverted(ty)
+ }
+ } else if known_types().convertible_from_strs(&tn)
+ && !self.config.exclude_utilities()
+ {
+ TypeConversionPolicy {
+ unwrapped_type: ty,
+ cpp_conversion: CppConversionType::FromUniquePtrToValue,
+ rust_conversion: RustConversionType::FromStr,
+ }
+ } else if matches!(
+ sophistication,
+ TypeConversionSophistication::SimpleForSubclasses
+ ) {
+ TypeConversionPolicy {
+ unwrapped_type: ty,
+ cpp_conversion: CppConversionType::FromUniquePtrToValue,
+ rust_conversion: RustConversionType::None,
+ }
+ } else {
+ TypeConversionPolicy {
+ unwrapped_type: ty,
+ cpp_conversion: CppConversionType::FromPtrToValue,
+ rust_conversion: RustConversionType::FromValueParamToPtr,
+ }
+ }
+ }
+ Type::Ptr(tp) => {
+ let rust_conversion = force_rust_conversion.unwrap_or(RustConversionType::None);
+ if is_move_constructor {
+ TypeConversionPolicy {
+ unwrapped_type: ty.clone(),
+ cpp_conversion: CppConversionType::FromPtrToMove,
+ rust_conversion,
+ }
+ } else if is_rvalue_ref {
+ TypeConversionPolicy {
+ unwrapped_type: *tp.elem.clone(),
+ cpp_conversion: CppConversionType::FromPtrToValue,
+ rust_conversion: RustConversionType::FromRValueParamToPtr,
+ }
+ } else {
+ TypeConversionPolicy {
+ unwrapped_type: ty.clone(),
+ cpp_conversion: CppConversionType::None,
+ rust_conversion,
+ }
+ }
+ }
+ _ => {
+ let rust_conversion = force_rust_conversion.unwrap_or(RustConversionType::None);
+ TypeConversionPolicy {
+ unwrapped_type: ty.clone(),
+ cpp_conversion: CppConversionType::None,
+ rust_conversion,
+ }
+ }
+ }
+ }
+
+ fn convert_return_type(
+ &mut self,
+ rt: &ReturnType,
+ ns: &Namespace,
+ references: &References,
+ sophistication: TypeConversionSophistication,
+ ) -> Result<ReturnTypeAnalysis, ConvertError> {
+ Ok(match rt {
+ ReturnType::Default => ReturnTypeAnalysis::default(),
+ ReturnType::Type(rarrow, boxed_type) => {
+ let annotated_type =
+ self.convert_boxed_type(boxed_type.clone(), ns, references.return_treatment())?;
+ let boxed_type = annotated_type.ty;
+ let ty: &Type = boxed_type.as_ref();
+ match ty {
+ Type::Path(p)
+ if !self
+ .pod_safe_types
+ .contains(&QualifiedName::from_type_path(p)) =>
+ {
+ let tn = QualifiedName::from_type_path(p);
+ if self.moveit_safe_types.contains(&tn)
+ && matches!(sophistication, TypeConversionSophistication::Regular)
+ {
+ // This is a non-POD type we want to return to Rust as an `impl New` so that callers
+ // can decide whether to store this on the stack or heap.
+ // That means, we do not literally _return_ it from C++ to Rust. Instead, our call
+ // from Rust to C++ will include an extra placement parameter into which the object
+ // is constructed.
+ let fnarg = parse_quote! {
+ placement_return_type: *mut #ty
+ };
+ let (fnarg, analysis) = self.convert_fn_arg(
+ &fnarg,
+ ns,
+ "",
+ &None,
+ &References::default(),
+ false,
+ false,
+ Some(RustConversionType::FromPlacementParamToNewReturn),
+ TypeConversionSophistication::Regular,
+ false,
+ )?;
+ ReturnTypeAnalysis {
+ rt: ReturnType::Default,
+ conversion: Some(TypeConversionPolicy::new_for_placement_return(
+ ty.clone(),
+ )),
+ was_reference: false,
+ deps: annotated_type.types_encountered,
+ placement_param_needed: Some((fnarg, analysis)),
+ }
+ } else {
+ // There are some types which we can't currently represent within a moveit::new::New.
+ // That's either because we are obliged to stick to existing protocols for compatibility
+ // (CxxString) or because they're a concrete type where we haven't attempted to do
+ // the analysis to work out the type's size. For these, we always return a plain old
+ // UniquePtr<T>. These restrictions may be fixed in future.
+ let conversion =
+ Some(TypeConversionPolicy::new_to_unique_ptr(ty.clone()));
+ ReturnTypeAnalysis {
+ rt: ReturnType::Type(*rarrow, boxed_type),
+ conversion,
+ was_reference: false,
+ deps: annotated_type.types_encountered,
+ placement_param_needed: None,
+ }
+ }
+ }
+ _ => {
+ let was_reference = matches!(boxed_type.as_ref(), Type::Reference(_));
+ let conversion = Some(TypeConversionPolicy::new_unconverted(ty.clone()));
+ ReturnTypeAnalysis {
+ rt: ReturnType::Type(*rarrow, boxed_type),
+ conversion,
+ was_reference,
+ deps: annotated_type.types_encountered,
+ placement_param_needed: None,
+ }
+ }
+ }
+ }
+ })
+ }
+
+ /// If a type has explicit constructors, bindgen will generate corresponding
+ /// constructor functions, which we'll have already converted to make_unique methods.
+ /// C++ mandates the synthesis of certain implicit constructors, to which we
+ /// need to create bindings too. We do that here.
+ /// It is tempting to make this a separate analysis phase, to be run later than
+ /// the function analysis; but that would make the code much more complex as it
+ /// would need to output a `FnAnalysisBody`. By running it as part of this phase
+ /// we can simply generate the sort of thing bindgen generates, then ask
+ /// the existing code in this phase to figure out what to do with it.
+ ///
+ /// Also fills out the [`PodAndConstructorAnalysis::constructors`] fields with information useful
+ /// for further analysis phases.
+ fn add_constructors_present(&mut self, mut apis: ApiVec<FnPrePhase1>) -> ApiVec<FnPrePhase2> {
+ let all_items_found = find_constructors_present(&apis);
+ for (self_ty, items_found) in all_items_found.iter() {
+ if self.config.exclude_impls {
+ // Remember that `find_constructors_present` mutates `apis`, so we always have to
+ // call that, even if we don't do anything with the return value. This is kind of
+ // messy, see the comment on this function for why.
+ continue;
+ }
+ if self
+ .config
+ .is_on_constructor_blocklist(&self_ty.to_cpp_name())
+ {
+ continue;
+ }
+ let path = self_ty.to_type_path();
+ if items_found.implicit_default_constructor_needed() {
+ self.synthesize_special_member(
+ items_found,
+ "default_ctor",
+ &mut apis,
+ SpecialMemberKind::DefaultConstructor,
+ parse_quote! { this: *mut #path },
+ References::default(),
+ );
+ }
+ if items_found.implicit_move_constructor_needed() {
+ self.synthesize_special_member(
+ items_found,
+ "move_ctor",
+ &mut apis,
+ SpecialMemberKind::MoveConstructor,
+ parse_quote! { this: *mut #path, other: *mut #path },
+ References {
+ rvalue_ref_params: [make_ident("other")].into_iter().collect(),
+ ..Default::default()
+ },
+ )
+ }
+ if items_found.implicit_copy_constructor_needed() {
+ self.synthesize_special_member(
+ items_found,
+ "const_copy_ctor",
+ &mut apis,
+ SpecialMemberKind::CopyConstructor,
+ parse_quote! { this: *mut #path, other: *const #path },
+ References {
+ ref_params: [make_ident("other")].into_iter().collect(),
+ ..Default::default()
+ },
+ )
+ }
+ if items_found.implicit_destructor_needed() {
+ self.synthesize_special_member(
+ items_found,
+ "destructor",
+ &mut apis,
+ SpecialMemberKind::Destructor,
+ parse_quote! { this: *mut #path },
+ References::default(),
+ );
+ }
+ }
+
+ // Also, annotate each type with the constructors we found.
+ let mut results = ApiVec::new();
+ convert_apis(
+ apis,
+ &mut results,
+ Api::fun_unchanged,
+ |name, details, analysis| {
+ let items_found = all_items_found.get(&name.name);
+ Ok(Box::new(std::iter::once(Api::Struct {
+ name,
+ details,
+ analysis: PodAndConstructorAnalysis {
+ pod: analysis,
+ constructors: if let Some(items_found) = items_found {
+ PublicConstructors::from_items_found(items_found)
+ } else {
+ PublicConstructors::default()
+ },
+ },
+ })))
+ },
+ Api::enum_unchanged,
+ Api::typedef_unchanged,
+ );
+ results
+ }
+
+ #[allow(clippy::too_many_arguments)] // it's true, but sticking with it for now
+ fn synthesize_special_member(
+ &mut self,
+ items_found: &ItemsFound,
+ label: &str,
+ apis: &mut ApiVec<FnPrePhase1>,
+ special_member: SpecialMemberKind,
+ inputs: Punctuated<FnArg, Comma>,
+ references: References,
+ ) {
+ let self_ty = items_found.name.as_ref().unwrap();
+ let ident = make_ident(self.config.uniquify_name_per_mod(&format!(
+ "{}_synthetic_{}",
+ self_ty.name.get_final_item(),
+ label
+ )));
+ let cpp_name = if matches!(special_member, SpecialMemberKind::DefaultConstructor) {
+ // Constructors (other than move or copy) are identified in `analyze_foreign_fn` by
+ // being suffixed with the cpp_name, so we have to produce that.
+ self.nested_type_name_map
+ .get(&self_ty.name)
+ .cloned()
+ .or_else(|| Some(self_ty.name.get_final_item().to_string()))
+ } else {
+ None
+ };
+ let fake_api_name =
+ ApiName::new_with_cpp_name(self_ty.name.get_namespace(), ident.clone(), cpp_name);
+ let self_ty = &self_ty.name;
+ let ns = self_ty.get_namespace().clone();
+ let mut any_errors = ApiVec::new();
+ apis.extend(
+ report_any_error(&ns, &mut any_errors, || {
+ self.analyze_foreign_fn_and_subclasses(
+ fake_api_name,
+ Box::new(FuncToConvert {
+ self_ty: Some(self_ty.clone()),
+ ident,
+ doc_attrs: make_doc_attrs(format!("Synthesized {}.", special_member)),
+ inputs,
+ output: ReturnType::Default,
+ vis: parse_quote! { pub },
+ virtualness: Virtualness::None,
+ cpp_vis: CppVisibility::Public,
+ special_member: Some(special_member),
+ unused_template_param: false,
+ references,
+ original_name: None,
+ synthesized_this_type: None,
+ is_deleted: false,
+ add_to_trait: None,
+ synthetic_cpp: None,
+ provenance: Provenance::SynthesizedOther,
+ variadic: false,
+ }),
+ )
+ })
+ .into_iter()
+ .flatten(),
+ );
+ apis.append(&mut any_errors);
+ }
+}
+
+/// Attempts to determine whether this function name is a constructor, and if so,
+/// returns the suffix.
+fn constructor_with_suffix<'a>(rust_name: &'a str, nested_type_ident: &str) -> Option<&'a str> {
+ let suffix = rust_name.strip_prefix(nested_type_ident);
+ suffix.and_then(|suffix| {
+ if suffix.is_empty() || suffix.parse::<u32>().is_ok() {
+ Some(suffix)
+ } else {
+ None
+ }
+ })
+}
+
+impl Api<FnPhase> {
+ pub(crate) fn name_for_allowlist(&self) -> QualifiedName {
+ match &self {
+ Api::Function { analysis, .. } => match analysis.kind {
+ FnKind::Method { ref impl_for, .. } => impl_for.clone(),
+ FnKind::TraitMethod { ref impl_for, .. } => impl_for.clone(),
+ FnKind::Function => {
+ QualifiedName::new(self.name().get_namespace(), make_ident(&analysis.rust_name))
+ }
+ },
+ Api::RustSubclassFn { subclass, .. } => subclass.0.name.clone(),
+ Api::IgnoredItem {
+ name,
+ ctx: Some(ctx),
+ ..
+ } => match ctx.get_type() {
+ ErrorContextType::Method { self_ty, .. } => {
+ QualifiedName::new(name.name.get_namespace(), self_ty.clone())
+ }
+ ErrorContextType::Item(id) => {
+ QualifiedName::new(name.name.get_namespace(), id.clone())
+ }
+ _ => name.name.clone(),
+ },
+ _ => self.name().clone(),
+ }
+ }
+
+ /// Whether this API requires generation of additional C++.
+ /// This seems an odd place for this function (as opposed to in the [codegen_cpp]
+ /// module) but, as it happens, even our Rust codegen phase needs to know if
+ /// more C++ is needed (so it can add #includes in the cxx mod).
+ /// And we can't answer the question _prior_ to this function analysis phase.
+ pub(crate) fn needs_cpp_codegen(&self) -> bool {
+ matches!(
+ &self,
+ Api::Function {
+ analysis: FnAnalysis {
+ cpp_wrapper: Some(..),
+ ignore_reason: Ok(_),
+ externally_callable: true,
+ ..
+ },
+ ..
+ } | Api::StringConstructor { .. }
+ | Api::ConcreteType { .. }
+ | Api::CType { .. }
+ | Api::RustSubclassFn { .. }
+ | Api::Subclass { .. }
+ | Api::Struct {
+ analysis: PodAndDepAnalysis {
+ pod: PodAnalysis {
+ kind: TypeKind::Pod,
+ ..
+ },
+ ..
+ },
+ ..
+ }
+ )
+ }
+
+ pub(crate) fn cxxbridge_name(&self) -> Option<Ident> {
+ match self {
+ Api::Function { ref analysis, .. } => Some(analysis.cxxbridge_name.clone()),
+ Api::StringConstructor { .. }
+ | Api::Const { .. }
+ | Api::IgnoredItem { .. }
+ | Api::RustSubclassFn { .. } => None,
+ _ => Some(self.name().get_final_ident()),
+ }
+ }
+}