blob: 19340f927598588051ff27183d8c482e5040dc85 [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// Copyright 2020 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9mod bridge_name_tracker;
10pub(crate) mod function_wrapper;
11mod implicit_constructors;
12mod overload_tracker;
13mod subclass;
14
15use crate::{
16 conversion::{
17 analysis::{
18 fun::function_wrapper::{CppConversionType, CppFunctionKind},
19 type_converter::{self, add_analysis, TypeConversionContext, TypeConverter},
20 },
21 api::{
22 ApiName, CastMutability, CppVisibility, FuncToConvert, NullPhase, Provenance,
23 References, SpecialMemberKind, SubclassName, TraitImplSignature, TraitSynthesis,
24 UnsafetyNeeded, Virtualness,
25 },
26 apivec::ApiVec,
27 convert_error::ErrorContext,
28 convert_error::{ConvertErrorWithContext, ErrorContextType},
29 error_reporter::{convert_apis, report_any_error},
30 },
31 known_types::known_types,
32 types::validate_ident_ok_for_rust,
33};
34use indexmap::map::IndexMap as HashMap;
35use indexmap::set::IndexSet as HashSet;
36
37use autocxx_parser::{ExternCppType, IncludeCppConfig, UnsafePolicy};
38use function_wrapper::{CppFunction, CppFunctionBody, TypeConversionPolicy};
39use itertools::Itertools;
40use proc_macro2::Span;
41use quote::quote;
42use syn::{
43 parse_quote, punctuated::Punctuated, token::Comma, FnArg, Ident, Pat, ReturnType, Type,
44 TypePtr, Visibility,
45};
46
47use crate::{
48 conversion::{
49 api::{AnalysisPhase, Api, TypeKind},
50 ConvertError,
51 },
52 types::{make_ident, validate_ident_ok_for_cxx, Namespace, QualifiedName},
53};
54
55use self::{
56 bridge_name_tracker::BridgeNameTracker,
57 function_wrapper::RustConversionType,
58 implicit_constructors::{find_constructors_present, ItemsFound},
59 overload_tracker::OverloadTracker,
60 subclass::{
61 create_subclass_constructor, create_subclass_fn_wrapper, create_subclass_function,
62 create_subclass_trait_item,
63 },
64};
65
66use super::{
67 doc_label::make_doc_attrs,
68 pod::{PodAnalysis, PodPhase},
69 tdef::TypedefAnalysis,
70 type_converter::{Annotated, PointerTreatment},
71};
72
73#[derive(Clone, Debug)]
74pub(crate) enum ReceiverMutability {
75 Const,
76 Mutable,
77}
78
79#[derive(Clone, Debug)]
80pub(crate) enum MethodKind {
81 Normal(ReceiverMutability),
82 Constructor { is_default: bool },
83 Static,
84 Virtual(ReceiverMutability),
85 PureVirtual(ReceiverMutability),
86}
87
88#[derive(Clone)]
89pub(crate) enum TraitMethodKind {
90 CopyConstructor,
91 MoveConstructor,
92 Cast,
93 Destructor,
94 Alloc,
95 Dealloc,
96}
97
98#[derive(Clone)]
99pub(crate) struct TraitMethodDetails {
100 pub(crate) trt: TraitImplSignature,
101 pub(crate) avoid_self: bool,
102 pub(crate) method_name: Ident,
103 /// For traits, where we're trying to implement a specific existing
104 /// interface, we may need to reorder the parameters to fit that
105 /// interface.
106 pub(crate) parameter_reordering: Option<Vec<usize>>,
107 /// The function we're calling from the trait requires unsafe even
108 /// though the trait and its function aren't.
109 pub(crate) trait_call_is_unsafe: bool,
110}
111
112#[derive(Clone)]
113pub(crate) enum FnKind {
114 Function,
115 Method {
116 method_kind: MethodKind,
117 impl_for: QualifiedName,
118 },
119 TraitMethod {
120 kind: TraitMethodKind,
121 /// The name of the type T for which we're implementing a trait,
122 /// though we may be actually implementing the trait for &mut T or
123 /// similar, so we store more details of both the type and the
124 /// method in `details`
125 impl_for: QualifiedName,
126 details: Box<TraitMethodDetails>,
127 },
128}
129
130/// Strategy for ensuring that the final, callable, Rust name
131/// is what the user originally expected.
132#[derive(Clone)]
133
134pub(crate) enum RustRenameStrategy {
135 /// cxx::bridge name matches user expectations
136 None,
137 /// Even the #[rust_name] attribute would cause conflicts, and we need
138 /// to use a 'use XYZ as ABC'
139 RenameInOutputMod(Ident),
140 /// This function requires us to generate a Rust function to do
141 /// parameter conversion.
142 RenameUsingWrapperFunction,
143}
144
145#[derive(Clone)]
146pub(crate) struct FnAnalysis {
147 /// Each entry in the cxx::bridge needs to have a unique name, even if
148 /// (from the perspective of Rust and C++) things are in different
149 /// namespaces/mods.
150 pub(crate) cxxbridge_name: Ident,
151 /// ... so record also the name under which we wish to expose it in Rust.
152 pub(crate) rust_name: String,
153 pub(crate) rust_rename_strategy: RustRenameStrategy,
154 pub(crate) params: Punctuated<FnArg, Comma>,
155 pub(crate) kind: FnKind,
156 pub(crate) ret_type: ReturnType,
157 pub(crate) param_details: Vec<ArgumentAnalysis>,
158 pub(crate) ret_conversion: Option<TypeConversionPolicy>,
159 pub(crate) requires_unsafe: UnsafetyNeeded,
160 pub(crate) vis: Visibility,
161 pub(crate) cpp_wrapper: Option<CppFunction>,
162 pub(crate) deps: HashSet<QualifiedName>,
163 /// Some methods still need to be recorded because we want
164 /// to (a) generate the ability to call superclasses, (b) create
165 /// subclass entries for them. But we do not want to have them
166 /// be externally callable.
167 pub(crate) ignore_reason: Result<(), ConvertErrorWithContext>,
168 /// Whether this can be called by external code. Not so for
169 /// protected methods.
170 pub(crate) externally_callable: bool,
171 /// Whether we need to generate a Rust-side calling function
172 pub(crate) rust_wrapper_needed: bool,
173}
174
175#[derive(Clone)]
176pub(crate) struct ArgumentAnalysis {
177 pub(crate) conversion: TypeConversionPolicy,
178 pub(crate) name: Pat,
179 pub(crate) self_type: Option<(QualifiedName, ReceiverMutability)>,
180 pub(crate) has_lifetime: bool,
181 pub(crate) deps: HashSet<QualifiedName>,
182 pub(crate) requires_unsafe: UnsafetyNeeded,
183 pub(crate) is_placement_return_destination: bool,
184}
185
186struct ReturnTypeAnalysis {
187 rt: ReturnType,
188 conversion: Option<TypeConversionPolicy>,
189 was_reference: bool,
190 deps: HashSet<QualifiedName>,
191 placement_param_needed: Option<(FnArg, ArgumentAnalysis)>,
192}
193
194impl Default for ReturnTypeAnalysis {
195 fn default() -> Self {
196 Self {
197 rt: parse_quote! {},
198 conversion: None,
199 was_reference: false,
200 deps: Default::default(),
201 placement_param_needed: None,
202 }
203 }
204}
205
206pub(crate) struct PodAndConstructorAnalysis {
207 pub(crate) pod: PodAnalysis,
208 pub(crate) constructors: PublicConstructors,
209}
210
211/// An analysis phase where we've analyzed each function, but
212/// haven't yet determined which constructors/etc. belong to each type.
213pub(crate) struct FnPrePhase1;
214
215impl AnalysisPhase for FnPrePhase1 {
216 type TypedefAnalysis = TypedefAnalysis;
217 type StructAnalysis = PodAnalysis;
218 type FunAnalysis = FnAnalysis;
219}
220
221/// An analysis phase where we've analyzed each function, and identified
222/// what implicit constructors/destructors are present in each type.
223pub(crate) struct FnPrePhase2;
224
225impl AnalysisPhase for FnPrePhase2 {
226 type TypedefAnalysis = TypedefAnalysis;
227 type StructAnalysis = PodAndConstructorAnalysis;
228 type FunAnalysis = FnAnalysis;
229}
230
231pub(crate) struct PodAndDepAnalysis {
232 pub(crate) pod: PodAnalysis,
233 pub(crate) constructor_and_allocator_deps: Vec<QualifiedName>,
234 pub(crate) constructors: PublicConstructors,
235}
236
237/// Analysis phase after we've finished analyzing functions and determined
238/// which constructors etc. belong to them.
239pub(crate) struct FnPhase;
240
241/// Indicates which kinds of public constructors are known to exist for a type.
242#[derive(Debug, Default, Copy, Clone)]
243pub(crate) struct PublicConstructors {
244 pub(crate) move_constructor: bool,
245 pub(crate) destructor: bool,
246}
247
248impl PublicConstructors {
249 fn from_items_found(items_found: &ItemsFound) -> Self {
250 Self {
251 move_constructor: items_found.move_constructor.callable_any(),
252 destructor: items_found.destructor.callable_any(),
253 }
254 }
255}
256
257impl AnalysisPhase for FnPhase {
258 type TypedefAnalysis = TypedefAnalysis;
259 type StructAnalysis = PodAndDepAnalysis;
260 type FunAnalysis = FnAnalysis;
261}
262
263/// Whether to allow highly optimized calls because this is a simple Rust->C++ call,
264/// or to use a simpler set of policies because this is a subclass call where
265/// we may have C++->Rust->C++ etc.
266#[derive(Copy, Clone)]
267enum TypeConversionSophistication {
268 Regular,
269 SimpleForSubclasses,
270}
271
272pub(crate) struct FnAnalyzer<'a> {
273 unsafe_policy: UnsafePolicy,
274 extra_apis: ApiVec<NullPhase>,
275 type_converter: TypeConverter<'a>,
276 bridge_name_tracker: BridgeNameTracker,
277 pod_safe_types: HashSet<QualifiedName>,
278 moveit_safe_types: HashSet<QualifiedName>,
279 config: &'a IncludeCppConfig,
280 overload_trackers_by_mod: HashMap<Namespace, OverloadTracker>,
281 subclasses_by_superclass: HashMap<QualifiedName, Vec<SubclassName>>,
282 nested_type_name_map: HashMap<QualifiedName, String>,
283 generic_types: HashSet<QualifiedName>,
284 types_in_anonymous_namespace: HashSet<QualifiedName>,
285 existing_superclass_trait_api_names: HashSet<QualifiedName>,
286}
287
288impl<'a> FnAnalyzer<'a> {
289 pub(crate) fn analyze_functions(
290 apis: ApiVec<PodPhase>,
291 unsafe_policy: UnsafePolicy,
292 config: &'a IncludeCppConfig,
293 ) -> ApiVec<FnPrePhase2> {
294 let mut me = Self {
295 unsafe_policy,
296 extra_apis: ApiVec::new(),
297 type_converter: TypeConverter::new(config, &apis),
298 bridge_name_tracker: BridgeNameTracker::new(),
299 config,
300 overload_trackers_by_mod: HashMap::new(),
301 pod_safe_types: Self::build_pod_safe_type_set(&apis),
302 moveit_safe_types: Self::build_correctly_sized_type_set(&apis),
303 subclasses_by_superclass: subclass::subclasses_by_superclass(&apis),
304 nested_type_name_map: Self::build_nested_type_map(&apis),
305 generic_types: Self::build_generic_type_set(&apis),
306 existing_superclass_trait_api_names: HashSet::new(),
307 types_in_anonymous_namespace: Self::build_types_in_anonymous_namespace(&apis),
308 };
309 let mut results = ApiVec::new();
310 convert_apis(
311 apis,
312 &mut results,
313 |name, fun, _| me.analyze_foreign_fn_and_subclasses(name, fun),
314 Api::struct_unchanged,
315 Api::enum_unchanged,
316 Api::typedef_unchanged,
317 );
318 let mut results = me.add_constructors_present(results);
319 me.add_subclass_constructors(&mut results);
320 results.extend(me.extra_apis.into_iter().map(add_analysis));
321 results
322 }
323
324 fn build_pod_safe_type_set(apis: &ApiVec<PodPhase>) -> HashSet<QualifiedName> {
325 apis.iter()
326 .filter_map(|api| match api {
327 Api::Struct {
328 analysis:
329 PodAnalysis {
330 kind: TypeKind::Pod,
331 ..
332 },
333 ..
334 } => Some(api.name().clone()),
335 Api::Enum { .. } => Some(api.name().clone()),
336 Api::ExternCppType { pod: true, .. } => Some(api.name().clone()),
337 _ => None,
338 })
339 .chain(
340 known_types()
341 .get_pod_safe_types()
342 .filter_map(
343 |(tn, is_pod_safe)| {
344 if is_pod_safe {
345 Some(tn)
346 } else {
347 None
348 }
349 },
350 ),
351 )
352 .collect()
353 }
354
355 /// Return the set of 'moveit safe' types. That must include only types where
356 /// the size is known to be correct.
357 fn build_correctly_sized_type_set(apis: &ApiVec<PodPhase>) -> HashSet<QualifiedName> {
358 apis.iter()
359 .filter(|api| {
360 matches!(
361 api,
362 Api::Struct { .. }
363 | Api::Enum { .. }
364 | Api::ExternCppType {
365 details: ExternCppType { opaque: false, .. },
366 ..
367 }
368 )
369 })
370 .map(|api| api.name().clone())
371 .chain(known_types().get_moveit_safe_types())
372 .collect()
373 }
374
375 fn build_generic_type_set(apis: &ApiVec<PodPhase>) -> HashSet<QualifiedName> {
376 apis.iter()
377 .filter_map(|api| match api {
378 Api::Struct {
379 analysis:
380 PodAnalysis {
381 is_generic: true, ..
382 },
383 ..
384 } => Some(api.name().clone()),
385 _ => None,
386 })
387 .collect()
388 }
389
390 fn build_types_in_anonymous_namespace(apis: &ApiVec<PodPhase>) -> HashSet<QualifiedName> {
391 apis.iter()
392 .filter_map(|api| match api {
393 Api::Struct {
394 analysis:
395 PodAnalysis {
396 in_anonymous_namespace: true,
397 ..
398 },
399 ..
400 } => Some(api.name().clone()),
401 _ => None,
402 })
403 .collect()
404 }
405
406 /// Builds a mapping from a qualified type name to the last 'nest'
407 /// of its name, if it has multiple elements.
408 fn build_nested_type_map(apis: &ApiVec<PodPhase>) -> HashMap<QualifiedName, String> {
409 apis.iter()
410 .filter_map(|api| match api {
411 Api::Struct { name, .. } | Api::Enum { name, .. } => {
412 let cpp_name = name
413 .cpp_name_if_present()
414 .cloned()
415 .unwrap_or_else(|| name.name.get_final_item().to_string());
416 cpp_name
417 .rsplit_once("::")
418 .map(|(_, suffix)| (name.name.clone(), suffix.to_string()))
419 }
420 _ => None,
421 })
422 .collect()
423 }
424
425 fn convert_boxed_type(
426 &mut self,
427 ty: Box<Type>,
428 ns: &Namespace,
429 pointer_treatment: PointerTreatment,
430 ) -> Result<Annotated<Box<Type>>, ConvertError> {
431 let ctx = TypeConversionContext::OuterType { pointer_treatment };
432 let mut annotated = self.type_converter.convert_boxed_type(ty, ns, &ctx)?;
433 self.extra_apis.append(&mut annotated.extra_apis);
434 Ok(annotated)
435 }
436
437 fn get_cxx_bridge_name(
438 &mut self,
439 type_name: Option<&str>,
440 found_name: &str,
441 ns: &Namespace,
442 ) -> String {
443 self.bridge_name_tracker
444 .get_unique_cxx_bridge_name(type_name, found_name, ns)
445 }
446
447 fn is_on_allowlist(&self, type_name: &QualifiedName) -> bool {
448 self.config.is_on_allowlist(&type_name.to_cpp_name())
449 }
450
451 fn is_generic_type(&self, type_name: &QualifiedName) -> bool {
452 self.generic_types.contains(type_name)
453 }
454
455 #[allow(clippy::if_same_then_else)] // clippy bug doesn't notice the two
456 // closures below are different.
457 fn should_be_unsafe(
458 &self,
459 param_details: &[ArgumentAnalysis],
460 kind: &FnKind,
461 ) -> UnsafetyNeeded {
462 let unsafest_non_placement_param = UnsafetyNeeded::from_param_details(param_details, true);
463 let unsafest_param = UnsafetyNeeded::from_param_details(param_details, false);
464 match kind {
465 // Trait unsafety must always correspond to the norms for the
466 // trait we're implementing.
467 FnKind::TraitMethod {
468 kind:
469 TraitMethodKind::CopyConstructor
470 | TraitMethodKind::MoveConstructor
471 | TraitMethodKind::Alloc
472 | TraitMethodKind::Dealloc,
473 ..
474 } => UnsafetyNeeded::Always,
475 FnKind::TraitMethod { .. } => match unsafest_param {
476 UnsafetyNeeded::Always => UnsafetyNeeded::JustBridge,
477 _ => unsafest_param,
478 },
479 _ if self.unsafe_policy == UnsafePolicy::AllFunctionsUnsafe => UnsafetyNeeded::Always,
480 _ => match unsafest_non_placement_param {
481 UnsafetyNeeded::Always => UnsafetyNeeded::Always,
482 UnsafetyNeeded::JustBridge => match unsafest_param {
483 UnsafetyNeeded::Always => UnsafetyNeeded::JustBridge,
484 _ => unsafest_non_placement_param,
485 },
486 UnsafetyNeeded::None => match unsafest_param {
487 UnsafetyNeeded::Always => UnsafetyNeeded::JustBridge,
488 _ => unsafest_param,
489 },
490 },
491 }
492 }
493
494 fn add_subclass_constructors(&mut self, apis: &mut ApiVec<FnPrePhase2>) {
495 let mut results = ApiVec::new();
496
497 // Pre-assemble a list of types with known destructors, to avoid having to
498 // do a O(n^2) nested loop.
499 let types_with_destructors: HashSet<_> = apis
500 .iter()
501 .filter_map(|api| match api {
502 Api::Function {
503 fun,
504 analysis:
505 FnAnalysis {
506 kind: FnKind::TraitMethod { impl_for, .. },
507 ..
508 },
509 ..
510 } if matches!(
511 **fun,
512 FuncToConvert {
513 special_member: Some(SpecialMemberKind::Destructor),
514 is_deleted: false,
515 cpp_vis: CppVisibility::Public,
516 ..
517 }
518 ) =>
519 {
520 Some(impl_for)
521 }
522 _ => None,
523 })
524 .cloned()
525 .collect();
526
527 for api in apis.iter() {
528 if let Api::Function {
529 fun,
530 analysis:
531 analysis @ FnAnalysis {
532 kind:
533 FnKind::Method {
534 impl_for: sup,
535 method_kind: MethodKind::Constructor { .. },
536 ..
537 },
538 ..
539 },
540 ..
541 } = api
542 {
543 // If we don't have an accessible destructor, then std::unique_ptr cannot be
544 // instantiated for this C++ type.
545 if !types_with_destructors.contains(sup) {
546 continue;
547 }
548
549 for sub in self.subclasses_by_superclass(sup) {
550 // Create a subclass constructor. This is a synthesized function
551 // which didn't exist in the original C++.
552 let (subclass_constructor_func, subclass_constructor_name) =
553 create_subclass_constructor(sub, analysis, sup, fun);
554 self.analyze_and_add(
555 subclass_constructor_name.clone(),
556 subclass_constructor_func.clone(),
557 &mut results,
558 TypeConversionSophistication::Regular,
559 );
560 }
561 }
562 }
563 apis.extend(results.into_iter());
564 }
565
566 /// Analyze a given function, and any permutations of that function which
567 /// we might additionally generate (e.g. for subclasses.)
568 ///
569 /// Leaves the [`FnKind::Method::type_constructors`] at its default for [`add_constructors_present`]
570 /// to fill out.
571 fn analyze_foreign_fn_and_subclasses(
572 &mut self,
573 name: ApiName,
574 fun: Box<FuncToConvert>,
575 ) -> Result<Box<dyn Iterator<Item = Api<FnPrePhase1>>>, ConvertErrorWithContext> {
576 let (analysis, name) =
577 self.analyze_foreign_fn(name, &fun, TypeConversionSophistication::Regular, None);
578 let mut results = ApiVec::new();
579
580 // Consider whether we need to synthesize subclass items.
581 if let FnKind::Method {
582 impl_for: sup,
583 method_kind:
584 MethodKind::Virtual(receiver_mutability) | MethodKind::PureVirtual(receiver_mutability),
585 ..
586 } = &analysis.kind
587 {
588 let (simpler_analysis, _) = self.analyze_foreign_fn(
589 name.clone(),
590 &fun,
591 TypeConversionSophistication::SimpleForSubclasses,
592 Some(analysis.rust_name.clone()),
593 );
594 for sub in self.subclasses_by_superclass(sup) {
595 // For each subclass, we need to create a plain-C++ method to call its superclass
596 // and a Rust/C++ bridge API to call _that_.
597 // What we're generating here is entirely about the subclass, so the
598 // superclass's namespace is irrelevant. We generate
599 // all subclasses in the root namespace.
600 let is_pure_virtual = matches!(
601 &simpler_analysis.kind,
602 FnKind::Method {
603 method_kind: MethodKind::PureVirtual(..),
604 ..
605 }
606 );
607
608 let super_fn_call_name =
609 SubclassName::get_super_fn_name(&Namespace::new(), &analysis.rust_name);
610 let super_fn_api_name = SubclassName::get_super_fn_name(
611 &Namespace::new(),
612 &analysis.cxxbridge_name.to_string(),
613 );
614 let trait_api_name = SubclassName::get_trait_api_name(sup, &analysis.rust_name);
615
616 let mut subclass_fn_deps = vec![trait_api_name.clone()];
617 if !is_pure_virtual {
618 // Create a C++ API representing the superclass implementation (allowing
619 // calls from Rust->C++)
620 let maybe_wrap = create_subclass_fn_wrapper(&sub, &super_fn_call_name, &fun);
621 let super_fn_name = ApiName::new_from_qualified_name(super_fn_api_name);
622 let super_fn_call_api_name = self.analyze_and_add(
623 super_fn_name,
624 maybe_wrap,
625 &mut results,
626 TypeConversionSophistication::SimpleForSubclasses,
627 );
628 subclass_fn_deps.push(super_fn_call_api_name);
629 }
630
631 // Create the Rust API representing the subclass implementation (allowing calls
632 // from C++ -> Rust)
633 results.push(create_subclass_function(
634 // RustSubclassFn
635 &sub,
636 &simpler_analysis,
637 &name,
638 receiver_mutability,
639 sup,
640 subclass_fn_deps,
641 ));
642
643 // Create the trait item for the <superclass>_methods and <superclass>_supers
644 // traits. This is required per-superclass, not per-subclass, so don't
645 // create it if it already exists.
646 if !self
647 .existing_superclass_trait_api_names
648 .contains(&trait_api_name)
649 {
650 self.existing_superclass_trait_api_names
651 .insert(trait_api_name.clone());
652 results.push(create_subclass_trait_item(
653 ApiName::new_from_qualified_name(trait_api_name),
654 &simpler_analysis,
655 receiver_mutability,
656 sup.clone(),
657 is_pure_virtual,
658 ));
659 }
660 }
661 }
662
663 results.push(Api::Function {
664 fun,
665 analysis,
666 name,
667 });
668
669 Ok(Box::new(results.into_iter()))
670 }
671
672 /// Adds an API, usually a synthesized API. Returns the final calculated API name, which can be used
673 /// for others to depend on this.
674 fn analyze_and_add<P: AnalysisPhase<FunAnalysis = FnAnalysis>>(
675 &mut self,
676 name: ApiName,
677 new_func: Box<FuncToConvert>,
678 results: &mut ApiVec<P>,
679 sophistication: TypeConversionSophistication,
680 ) -> QualifiedName {
681 let (analysis, name) = self.analyze_foreign_fn(name, &new_func, sophistication, None);
682 results.push(Api::Function {
683 fun: new_func,
684 analysis,
685 name: name.clone(),
686 });
687 name.name
688 }
689
690 /// Determine how to materialize a function.
691 ///
692 /// The main job here is to determine whether a function can simply be noted
693 /// in the [cxx::bridge] mod and passed directly to cxx, or if it needs a Rust-side
694 /// wrapper function, or if it needs a C++-side wrapper function, or both.
695 /// We aim for the simplest case but, for example:
696 /// * We'll need a C++ wrapper for static methods
697 /// * We'll need a C++ wrapper for parameters which need to be wrapped and unwrapped
698 /// to [cxx::UniquePtr]
699 /// * We'll need a Rust wrapper if we've got a C++ wrapper and it's a method.
700 /// * We may need wrappers if names conflict.
701 /// etc.
702 /// The other major thing we do here is figure out naming for the function.
703 /// This depends on overloads, and what other functions are floating around.
704 /// The output of this analysis phase is used by both Rust and C++ codegen.
705 fn analyze_foreign_fn(
706 &mut self,
707 name: ApiName,
708 fun: &FuncToConvert,
709 sophistication: TypeConversionSophistication,
710 predetermined_rust_name: Option<String>,
711 ) -> (FnAnalysis, ApiName) {
712 let mut cpp_name = name.cpp_name_if_present().cloned();
713 let ns = name.name.get_namespace();
714
715 // Let's gather some pre-wisdom about the name of the function.
716 // We're shortly going to plunge into analyzing the parameters,
717 // and it would be nice to have some idea of the function name
718 // for diagnostics whilst we do that.
719 let initial_rust_name = fun.ident.to_string();
720 let diagnostic_display_name = cpp_name.as_ref().unwrap_or(&initial_rust_name);
721
722 // Now let's analyze all the parameters.
723 // See if any have annotations which our fork of bindgen has craftily inserted...
724 let (param_details, bads): (Vec<_>, Vec<_>) = fun
725 .inputs
726 .iter()
727 .map(|i| {
728 self.convert_fn_arg(
729 i,
730 ns,
731 diagnostic_display_name,
732 &fun.synthesized_this_type,
733 &fun.references,
734 true,
735 false,
736 None,
737 sophistication,
738 false,
739 )
740 })
741 .partition(Result::is_ok);
742 let (mut params, mut param_details): (Punctuated<_, Comma>, Vec<_>) =
743 param_details.into_iter().map(Result::unwrap).unzip();
744
745 let params_deps: HashSet<_> = param_details
746 .iter()
747 .flat_map(|p| p.deps.iter().cloned())
748 .collect();
749 let self_ty = param_details
750 .iter()
751 .filter_map(|pd| pd.self_type.as_ref())
752 .next()
753 .cloned();
754
755 // End of parameter processing.
756 // Work out naming, part one.
757 // bindgen may have mangled the name either because it's invalid Rust
758 // syntax (e.g. a keyword like 'async') or it's an overload.
759 // If the former, we respect that mangling. If the latter, we don't,
760 // because we'll add our own overload counting mangling later.
761 // Cases:
762 // function, IRN=foo, CN=<none> output: foo case 1
763 // function, IRN=move_, CN=move (keyword problem) output: move_ case 2
764 // function, IRN=foo1, CN=foo (overload) output: foo case 3
765 // method, IRN=A_foo, CN=foo output: foo case 4
766 // method, IRN=A_move, CN=move (keyword problem) output: move_ case 5
767 // method, IRN=A_foo1, CN=foo (overload) output: foo case 6
768 let ideal_rust_name = match &cpp_name {
769 None => initial_rust_name, // case 1
770 Some(cpp_name) => {
771 if initial_rust_name.ends_with('_') {
772 initial_rust_name // case 2
773 } else if validate_ident_ok_for_rust(cpp_name).is_err() {
774 format!("{}_", cpp_name) // case 5
775 } else {
776 cpp_name.to_string() // cases 3, 4, 6
777 }
778 }
779 };
780
781 // Let's spend some time figuring out the kind of this function (i.e. method,
782 // virtual function, etc.)
783 // Part one, work out if this is a static method.
784 let (is_static_method, self_ty, receiver_mutability) = match self_ty {
785 None => {
786 // Even if we can't find a 'self' parameter this could conceivably
787 // be a static method.
788 let self_ty = fun.self_ty.clone();
789 (self_ty.is_some(), self_ty, None)
790 }
791 Some((self_ty, receiver_mutability)) => {
792 (false, Some(self_ty), Some(receiver_mutability))
793 }
794 };
795
796 // Part two, work out if this is a function, or method, or whatever.
797 // First determine if this is actually a trait implementation.
798 let trait_details = self.trait_creation_details_for_synthetic_function(
799 &fun.add_to_trait,
800 ns,
801 &ideal_rust_name,
802 &self_ty,
803 );
804 let (kind, error_context, rust_name) = if let Some(trait_details) = trait_details {
805 trait_details
806 } else if let Some(self_ty) = self_ty {
807 // Some kind of method or static method.
808 let type_ident = self_ty.get_final_item();
809 // bindgen generates methods with the name:
810 // {class}_{method name}
811 // It then generates an impl section for the Rust type
812 // with the original name, but we currently discard that impl section.
813 // We want to feed cxx methods with just the method name, so let's
814 // strip off the class name.
815 let mut rust_name = ideal_rust_name;
816 let nested_type_ident = self
817 .nested_type_name_map
818 .get(&self_ty)
819 .map(|s| s.as_str())
820 .unwrap_or_else(|| self_ty.get_final_item());
821 if matches!(
822 fun.special_member,
823 Some(SpecialMemberKind::CopyConstructor | SpecialMemberKind::MoveConstructor)
824 ) {
825 let is_move =
826 matches!(fun.special_member, Some(SpecialMemberKind::MoveConstructor));
827 if let Some(constructor_suffix) = rust_name.strip_prefix(nested_type_ident) {
828 rust_name = format!("new{}", constructor_suffix);
829 }
830 rust_name = predetermined_rust_name
831 .unwrap_or_else(|| self.get_overload_name(ns, type_ident, rust_name));
832 let error_context = self.error_context_for_method(&self_ty, &rust_name);
833
834 // If this is 'None', then something weird is going on. We'll check for that
835 // later when we have enough context to generate useful errors.
836 let arg_is_reference = matches!(
837 param_details
838 .get(1)
839 .map(|param| &param.conversion.unwrapped_type),
840 Some(Type::Reference(_))
841 );
842 // Some exotic forms of copy constructor have const and/or volatile qualifiers.
843 // These are not sufficient to implement CopyNew, so we just treat them as regular
844 // constructors. We detect them by their argument being translated to Pin at this
845 // point.
846 if is_move || arg_is_reference {
847 let (kind, method_name, trait_id) = if is_move {
848 (
849 TraitMethodKind::MoveConstructor,
850 "move_new",
851 quote! { MoveNew },
852 )
853 } else {
854 (
855 TraitMethodKind::CopyConstructor,
856 "copy_new",
857 quote! { CopyNew },
858 )
859 };
860 let ty = Type::Path(self_ty.to_type_path());
861 (
862 FnKind::TraitMethod {
863 kind,
864 impl_for: self_ty,
865 details: Box::new(TraitMethodDetails {
866 trt: TraitImplSignature {
867 ty,
868 trait_signature: parse_quote! {
869 autocxx::moveit::new:: #trait_id
870 },
871 unsafety: Some(parse_quote! { unsafe }),
872 },
873 avoid_self: true,
874 method_name: make_ident(method_name),
875 parameter_reordering: Some(vec![1, 0]),
876 trait_call_is_unsafe: false,
877 }),
878 },
879 error_context,
880 rust_name,
881 )
882 } else {
883 (
884 FnKind::Method {
885 impl_for: self_ty,
886 method_kind: MethodKind::Constructor { is_default: false },
887 },
888 error_context,
889 rust_name,
890 )
891 }
892 } else if matches!(fun.special_member, Some(SpecialMemberKind::Destructor)) {
893 rust_name = predetermined_rust_name
894 .unwrap_or_else(|| self.get_overload_name(ns, type_ident, rust_name));
895 let error_context = self.error_context_for_method(&self_ty, &rust_name);
896 let ty = Type::Path(self_ty.to_type_path());
897 (
898 FnKind::TraitMethod {
899 kind: TraitMethodKind::Destructor,
900 impl_for: self_ty,
901 details: Box::new(TraitMethodDetails {
902 trt: TraitImplSignature {
903 ty,
904 trait_signature: parse_quote! {
905 Drop
906 },
907 unsafety: None,
908 },
909 avoid_self: false,
910 method_name: make_ident("drop"),
911 parameter_reordering: None,
912 trait_call_is_unsafe: false,
913 }),
914 },
915 error_context,
916 rust_name,
917 )
918 } else {
919 let method_kind = if let Some(constructor_suffix) =
920 constructor_with_suffix(&rust_name, nested_type_ident)
921 {
922 // It's a constructor. bindgen generates
923 // fn Type(this: *mut Type, ...args)
924 // We want
925 // fn new(this: *mut Type, ...args)
926 // Later code will spot this and re-enter us, and we'll make
927 // a duplicate function in the above 'if' clause like this:
928 // fn make_unique(...args) -> Type
929 // which later code will convert to
930 // fn make_unique(...args) -> UniquePtr<Type>
931 // If there are multiple constructors, bindgen generates
932 // new, new1, new2 etc. and we'll keep those suffixes.
933 rust_name = format!("new{}", constructor_suffix);
934 MethodKind::Constructor {
935 is_default: matches!(
936 fun.special_member,
937 Some(SpecialMemberKind::DefaultConstructor)
938 ),
939 }
940 } else if is_static_method {
941 MethodKind::Static
942 } else {
943 let receiver_mutability =
944 receiver_mutability.expect("Failed to find receiver details");
945 match fun.virtualness {
946 Virtualness::None => MethodKind::Normal(receiver_mutability),
947 Virtualness::Virtual => MethodKind::Virtual(receiver_mutability),
948 Virtualness::PureVirtual => MethodKind::PureVirtual(receiver_mutability),
949 }
950 };
951 // Disambiguate overloads.
952 let rust_name = predetermined_rust_name
953 .unwrap_or_else(|| self.get_overload_name(ns, type_ident, rust_name));
954 let error_context = self.error_context_for_method(&self_ty, &rust_name);
955 (
956 FnKind::Method {
957 impl_for: self_ty,
958 method_kind,
959 },
960 error_context,
961 rust_name,
962 )
963 }
964 } else {
965 // Not a method.
966 // What shall we call this function? It may be overloaded.
967 let rust_name = self.get_function_overload_name(ns, ideal_rust_name);
968 (
969 FnKind::Function,
970 ErrorContext::new_for_item(make_ident(&rust_name)),
971 rust_name,
972 )
973 };
974
975 // If we encounter errors from here on, we can give some context around
976 // where the error occurred such that we can put a marker in the output
977 // Rust code to indicate that a problem occurred (benefiting people using
978 // rust-analyzer or similar). Make a closure to make this easy.
979 let mut ignore_reason = Ok(());
980 let mut set_ignore_reason =
981 |err| ignore_reason = Err(ConvertErrorWithContext(err, Some(error_context.clone())));
982
983 // Now we have figured out the type of function (from its parameters)
984 // we might have determined that we have a constructor. If so,
985 // annoyingly, we need to go back and fiddle with the parameters in a
986 // different way. This is because we want the first parameter to be a
987 // pointer not a reference. For copy + move constructors, we also
988 // enforce Rust-side conversions to comply with moveit traits.
989 match kind {
990 FnKind::Method {
991 method_kind: MethodKind::Constructor { .. },
992 ..
993 } => {
994 self.reanalyze_parameter(
995 0,
996 fun,
997 ns,
998 &rust_name,
999 &mut params,
1000 &mut param_details,
1001 None,
1002 sophistication,
1003 true,
1004 false,
1005 )
1006 .unwrap_or_else(&mut set_ignore_reason);
1007 }
1008
1009 FnKind::TraitMethod {
1010 kind: TraitMethodKind::Destructor,
1011 ..
1012 } => {
1013 self.reanalyze_parameter(
1014 0,
1015 fun,
1016 ns,
1017 &rust_name,
1018 &mut params,
1019 &mut param_details,
1020 Some(RustConversionType::FromTypeToPtr),
1021 sophistication,
1022 false,
1023 false,
1024 )
1025 .unwrap_or_else(&mut set_ignore_reason);
1026 }
1027 FnKind::TraitMethod {
1028 kind: TraitMethodKind::CopyConstructor,
1029 ..
1030 } => {
1031 if param_details.len() < 2 {
1032 set_ignore_reason(ConvertError::ConstructorWithOnlyOneParam);
1033 }
1034 if param_details.len() > 2 {
1035 set_ignore_reason(ConvertError::ConstructorWithMultipleParams);
1036 }
1037 self.reanalyze_parameter(
1038 0,
1039 fun,
1040 ns,
1041 &rust_name,
1042 &mut params,
1043 &mut param_details,
1044 Some(RustConversionType::FromPinMaybeUninitToPtr),
1045 sophistication,
1046 false,
1047 false,
1048 )
1049 .unwrap_or_else(&mut set_ignore_reason);
1050 }
1051
1052 FnKind::TraitMethod {
1053 kind: TraitMethodKind::MoveConstructor,
1054 ..
1055 } => {
1056 if param_details.len() < 2 {
1057 set_ignore_reason(ConvertError::ConstructorWithOnlyOneParam);
1058 }
1059 if param_details.len() > 2 {
1060 set_ignore_reason(ConvertError::ConstructorWithMultipleParams);
1061 }
1062 self.reanalyze_parameter(
1063 0,
1064 fun,
1065 ns,
1066 &rust_name,
1067 &mut params,
1068 &mut param_details,
1069 Some(RustConversionType::FromPinMaybeUninitToPtr),
1070 sophistication,
1071 false,
1072 false,
1073 )
1074 .unwrap_or_else(&mut set_ignore_reason);
1075 self.reanalyze_parameter(
1076 1,
1077 fun,
1078 ns,
1079 &rust_name,
1080 &mut params,
1081 &mut param_details,
1082 Some(RustConversionType::FromPinMoveRefToPtr),
1083 sophistication,
1084 false,
1085 true,
1086 )
1087 .unwrap_or_else(&mut set_ignore_reason);
1088 }
1089 _ => {}
1090 }
1091
1092 // Now we can add context to the error, check for a variety of error
1093 // cases. In each case, we continue to record the API, because it might
1094 // influence our later decisions to generate synthetic constructors
1095 // or note whether the type is abstract.
1096 let externally_callable = match fun.cpp_vis {
1097 CppVisibility::Private => {
1098 set_ignore_reason(ConvertError::PrivateMethod);
1099 false
1100 }
1101 CppVisibility::Protected => false,
1102 CppVisibility::Public => true,
1103 };
1104 if fun.variadic {
1105 set_ignore_reason(ConvertError::Variadic);
1106 }
1107 if let Some(problem) = bads.into_iter().next() {
1108 match problem {
1109 Ok(_) => panic!("No error in the error"),
1110 Err(problem) => set_ignore_reason(problem),
1111 }
1112 } else if fun.unused_template_param {
1113 // This indicates that bindgen essentially flaked out because templates
1114 // were too complex.
1115 set_ignore_reason(ConvertError::UnusedTemplateParam)
1116 } else if matches!(
1117 fun.special_member,
1118 Some(SpecialMemberKind::AssignmentOperator)
1119 ) {
1120 // Be careful with the order of this if-else tree. Anything above here means we won't
1121 // treat it as an assignment operator, but anything below we still consider when
1122 // deciding which other C++ special member functions are implicitly defined.
1123 set_ignore_reason(ConvertError::AssignmentOperator)
1124 } else if fun.references.rvalue_ref_return {
1125 set_ignore_reason(ConvertError::RValueReturn)
1126 } else if fun.is_deleted {
1127 set_ignore_reason(ConvertError::Deleted)
1128 } else {
1129 match kind {
1130 FnKind::Method {
1131 ref impl_for,
1132 method_kind:
1133 MethodKind::Constructor { .. }
1134 | MethodKind::Normal(..)
1135 | MethodKind::PureVirtual(..)
1136 | MethodKind::Virtual(..),
1137 ..
1138 } if !known_types().is_cxx_acceptable_receiver(impl_for) => {
1139 set_ignore_reason(ConvertError::UnsupportedReceiver);
1140 }
1141 FnKind::Method { ref impl_for, .. } if !self.is_on_allowlist(impl_for) => {
1142 // Bindgen will output methods for types which have been encountered
1143 // virally as arguments on other allowlisted types. But we don't want
1144 // to generate methods unless the user has specifically asked us to.
1145 // It may, for instance, be a private type.
1146 set_ignore_reason(ConvertError::MethodOfNonAllowlistedType);
1147 }
1148 FnKind::Method { ref impl_for, .. } | FnKind::TraitMethod { ref impl_for, .. } => {
1149 if self.is_generic_type(impl_for) {
1150 set_ignore_reason(ConvertError::MethodOfGenericType);
1151 }
1152 if self.types_in_anonymous_namespace.contains(impl_for) {
1153 set_ignore_reason(ConvertError::MethodInAnonymousNamespace);
1154 }
1155 }
1156 _ => {}
1157 }
1158 };
1159
1160 // The name we use within the cxx::bridge mod may be different
1161 // from both the C++ name and the Rust name, because it's a flat
1162 // namespace so we might need to prepend some stuff to make it unique.
1163 let cxxbridge_name = self.get_cxx_bridge_name(
1164 match kind {
1165 FnKind::Method { ref impl_for, .. } => Some(impl_for.get_final_item()),
1166 FnKind::Function => None,
1167 FnKind::TraitMethod { ref impl_for, .. } => Some(impl_for.get_final_item()),
1168 },
1169 &rust_name,
1170 ns,
1171 );
1172 if cxxbridge_name != rust_name && cpp_name.is_none() {
1173 cpp_name = Some(rust_name.clone());
1174 }
1175 let mut cxxbridge_name = make_ident(&cxxbridge_name);
1176
1177 // Analyze the return type, just as we previously did for the
1178 // parameters.
1179 let mut return_analysis = self
1180 .convert_return_type(&fun.output, ns, &fun.references, sophistication)
1181 .unwrap_or_else(|err| {
1182 set_ignore_reason(err);
1183 ReturnTypeAnalysis::default()
1184 });
1185 let mut deps = params_deps;
1186 deps.extend(return_analysis.deps.drain(..));
1187
1188 // Sometimes, the return type will actually be a value type
1189 // for which we instead want to _pass_ a pointer into which the value
1190 // can be constructed. Handle that case here.
1191 if let Some((extra_param, extra_param_details)) = return_analysis.placement_param_needed {
1192 param_details.push(extra_param_details);
1193 params.push(extra_param);
1194 }
1195
1196 let requires_unsafe = self.should_be_unsafe(&param_details, &kind);
1197
1198 let num_input_references = param_details.iter().filter(|pd| pd.has_lifetime).count();
1199 if num_input_references != 1 && return_analysis.was_reference {
1200 // cxx only allows functions to return a reference if they take exactly
1201 // one reference as a parameter. Let's see...
1202 set_ignore_reason(ConvertError::NotOneInputReference(rust_name.clone()));
1203 }
1204 let mut ret_type = return_analysis.rt;
1205 let ret_type_conversion = return_analysis.conversion;
1206
1207 // Do we need to convert either parameters or return type?
1208 let param_conversion_needed = param_details.iter().any(|b| b.conversion.cpp_work_needed());
1209 let ret_type_conversion_needed = ret_type_conversion
1210 .as_ref()
1211 .map_or(false, |x| x.cpp_work_needed());
1212 // See https://github.com/dtolnay/cxx/issues/878 for the reason for this next line.
1213 let effective_cpp_name = cpp_name.as_ref().unwrap_or(&rust_name);
1214 let cpp_name_incompatible_with_cxx =
1215 validate_ident_ok_for_rust(effective_cpp_name).is_err();
1216 // If possible, we'll put knowledge of the C++ API directly into the cxx::bridge
1217 // mod. However, there are various circumstances where cxx can't work with the existing
1218 // C++ API and we need to create a C++ wrapper function which is more cxx-compliant.
1219 // That wrapper function is included in the cxx::bridge, and calls through to the
1220 // original function.
1221 let wrapper_function_needed = match kind {
1222 FnKind::Method {
1223 method_kind:
1224 MethodKind::Static
1225 | MethodKind::Constructor { .. }
1226 | MethodKind::Virtual(_)
1227 | MethodKind::PureVirtual(_),
1228 ..
1229 }
1230 | FnKind::TraitMethod {
1231 kind:
1232 TraitMethodKind::CopyConstructor
1233 | TraitMethodKind::MoveConstructor
1234 | TraitMethodKind::Destructor,
1235 ..
1236 } => true,
1237 FnKind::Method { .. } if cxxbridge_name != rust_name => true,
1238 _ if param_conversion_needed => true,
1239 _ if ret_type_conversion_needed => true,
1240 _ if cpp_name_incompatible_with_cxx => true,
1241 _ if fun.synthetic_cpp.is_some() => true,
1242 _ => false,
1243 };
1244
1245 let cpp_wrapper = if wrapper_function_needed {
1246 // Generate a new layer of C++ code to wrap/unwrap parameters
1247 // and return values into/out of std::unique_ptrs.
1248 let cpp_construction_ident = make_ident(&effective_cpp_name);
1249 let joiner = if cxxbridge_name.to_string().ends_with('_') {
1250 ""
1251 } else {
1252 "_"
1253 };
1254 cxxbridge_name = make_ident(&format!("{}{}autocxx_wrapper", cxxbridge_name, joiner));
1255 let (payload, cpp_function_kind) = match fun.synthetic_cpp.as_ref().cloned() {
1256 Some((payload, cpp_function_kind)) => (payload, cpp_function_kind),
1257 None => match kind {
1258 FnKind::Method {
1259 ref impl_for,
1260 method_kind: MethodKind::Constructor { .. },
1261 ..
1262 }
1263 | FnKind::TraitMethod {
1264 kind: TraitMethodKind::CopyConstructor | TraitMethodKind::MoveConstructor,
1265 ref impl_for,
1266 ..
1267 } => (
1268 CppFunctionBody::PlacementNew(ns.clone(), impl_for.get_final_ident()),
1269 CppFunctionKind::Constructor,
1270 ),
1271 FnKind::TraitMethod {
1272 kind: TraitMethodKind::Destructor,
1273 ref impl_for,
1274 ..
1275 } => (
1276 CppFunctionBody::Destructor(ns.clone(), impl_for.get_final_ident()),
1277 CppFunctionKind::Function,
1278 ),
1279 FnKind::Method {
1280 ref impl_for,
1281 method_kind: MethodKind::Static,
1282 ..
1283 } => (
1284 CppFunctionBody::StaticMethodCall(
1285 ns.clone(),
1286 impl_for.get_final_ident(),
1287 cpp_construction_ident,
1288 ),
1289 CppFunctionKind::Function,
1290 ),
1291 FnKind::Method { .. } => (
1292 CppFunctionBody::FunctionCall(ns.clone(), cpp_construction_ident),
1293 CppFunctionKind::Method,
1294 ),
1295 _ => (
1296 CppFunctionBody::FunctionCall(ns.clone(), cpp_construction_ident),
1297 CppFunctionKind::Function,
1298 ),
1299 },
1300 };
1301 // Now modify the cxx::bridge entry we're going to make.
1302 if let Some(ref conversion) = ret_type_conversion {
1303 if conversion.populate_return_value() {
1304 let new_ret_type = conversion.unconverted_rust_type();
1305 ret_type = parse_quote!(
1306 -> #new_ret_type
1307 );
1308 }
1309 }
1310
1311 // Amend parameters for the function which we're asking cxx to generate.
1312 params.clear();
1313 for pd in &param_details {
1314 let type_name = pd.conversion.converted_rust_type();
1315 let arg_name = if pd.self_type.is_some() {
1316 parse_quote!(autocxx_gen_this)
1317 } else {
1318 pd.name.clone()
1319 };
1320 params.push(parse_quote!(
1321 #arg_name: #type_name
1322 ));
1323 }
1324
1325 Some(CppFunction {
1326 payload,
1327 wrapper_function_name: cxxbridge_name.clone(),
1328 original_cpp_name: cpp_name
1329 .as_ref()
1330 .cloned()
1331 .unwrap_or_else(|| cxxbridge_name.to_string()),
1332 return_conversion: ret_type_conversion.clone(),
1333 argument_conversion: param_details.iter().map(|d| d.conversion.clone()).collect(),
1334 kind: cpp_function_kind,
1335 pass_obs_field: false,
1336 qualification: None,
1337 })
1338 } else {
1339 None
1340 };
1341
1342 let vis = fun.vis.clone();
1343
1344 let any_param_needs_rust_conversion = param_details
1345 .iter()
1346 .any(|pd| pd.conversion.rust_work_needed());
1347
1348 let rust_wrapper_needed = match kind {
1349 FnKind::TraitMethod { .. } => true,
1350 FnKind::Method { .. } => any_param_needs_rust_conversion || cxxbridge_name != rust_name,
1351 _ => any_param_needs_rust_conversion,
1352 };
1353
1354 // Naming, part two.
1355 // Work out our final naming strategy.
1356 validate_ident_ok_for_cxx(&cxxbridge_name.to_string()).unwrap_or_else(set_ignore_reason);
1357 let rust_name_ident = make_ident(&rust_name);
1358 let rust_rename_strategy = match kind {
1359 _ if rust_wrapper_needed => RustRenameStrategy::RenameUsingWrapperFunction,
1360 FnKind::Function if cxxbridge_name != rust_name => {
1361 RustRenameStrategy::RenameInOutputMod(rust_name_ident)
1362 }
1363 _ => RustRenameStrategy::None,
1364 };
1365
1366 let analysis = FnAnalysis {
1367 cxxbridge_name: cxxbridge_name.clone(),
1368 rust_name: rust_name.clone(),
1369 rust_rename_strategy,
1370 params,
1371 ret_conversion: ret_type_conversion,
1372 kind,
1373 ret_type,
1374 param_details,
1375 requires_unsafe,
1376 vis,
1377 cpp_wrapper,
1378 deps,
1379 ignore_reason,
1380 externally_callable,
1381 rust_wrapper_needed,
1382 };
1383 let name = ApiName::new_with_cpp_name(ns, cxxbridge_name, cpp_name);
1384 (analysis, name)
1385 }
1386
1387 fn error_context_for_method(&self, self_ty: &QualifiedName, rust_name: &str) -> ErrorContext {
1388 if self.is_generic_type(self_ty) {
1389 // A 'method' error context would end up in an
1390 // impl A {
1391 // fn error_thingy
1392 // }
1393 // block. We can't impl A if it would need to be impl A<B>
1394 ErrorContext::new_for_item(make_ident(rust_name))
1395 } else {
1396 ErrorContext::new_for_method(self_ty.get_final_ident(), make_ident(rust_name))
1397 }
1398 }
1399
1400 /// Applies a specific `force_rust_conversion` to the parameter at index
1401 /// `param_idx`. Modifies `param_details` and `params` in place.
1402 #[allow(clippy::too_many_arguments)] // it's true, but sticking with it for now
1403 fn reanalyze_parameter(
1404 &mut self,
1405 param_idx: usize,
1406 fun: &FuncToConvert,
1407 ns: &Namespace,
1408 rust_name: &str,
1409 params: &mut Punctuated<FnArg, Comma>,
1410 param_details: &mut [ArgumentAnalysis],
1411 force_rust_conversion: Option<RustConversionType>,
1412 sophistication: TypeConversionSophistication,
1413 construct_into_self: bool,
1414 is_move_constructor: bool,
1415 ) -> Result<(), ConvertError> {
1416 self.convert_fn_arg(
1417 fun.inputs.iter().nth(param_idx).unwrap(),
1418 ns,
1419 rust_name,
1420 &fun.synthesized_this_type,
1421 &fun.references,
1422 false,
1423 is_move_constructor,
1424 force_rust_conversion,
1425 sophistication,
1426 construct_into_self,
1427 )
1428 .map(|(new_arg, new_analysis)| {
1429 param_details[param_idx] = new_analysis;
1430 let mut params_before = params.clone().into_iter();
1431 let prefix = params_before
1432 .by_ref()
1433 .take(param_idx)
1434 .collect_vec()
1435 .into_iter();
1436 let suffix = params_before.skip(1);
1437 *params = prefix
1438 .chain(std::iter::once(new_arg))
1439 .chain(suffix)
1440 .collect()
1441 })
1442 }
1443
1444 fn get_overload_name(&mut self, ns: &Namespace, type_ident: &str, rust_name: String) -> String {
1445 let overload_tracker = self.overload_trackers_by_mod.entry(ns.clone()).or_default();
1446 overload_tracker.get_method_real_name(type_ident, rust_name)
1447 }
1448
1449 /// Determine if this synthetic function should actually result in the implementation
1450 /// of a trait, rather than a function/method.
1451 fn trait_creation_details_for_synthetic_function(
1452 &mut self,
1453 synthesis: &Option<TraitSynthesis>,
1454 ns: &Namespace,
1455 ideal_rust_name: &str,
1456 self_ty: &Option<QualifiedName>,
1457 ) -> Option<(FnKind, ErrorContext, String)> {
1458 synthesis.as_ref().and_then(|synthesis| match synthesis {
1459 TraitSynthesis::Cast { to_type, mutable } => {
1460 let rust_name = self.get_function_overload_name(ns, ideal_rust_name.to_string());
1461 let from_type = self_ty.as_ref().unwrap();
1462 let from_type_path = from_type.to_type_path();
1463 let to_type = to_type.to_type_path();
1464 let (trait_signature, ty, method_name) = match *mutable {
1465 CastMutability::ConstToConst => (
1466 parse_quote! {
1467 AsRef < #to_type >
1468 },
1469 Type::Path(from_type_path),
1470 "as_ref",
1471 ),
1472 CastMutability::MutToConst => (
1473 parse_quote! {
1474 AsRef < #to_type >
1475 },
1476 parse_quote! {
1477 &'a mut ::std::pin::Pin < &'a mut #from_type_path >
1478 },
1479 "as_ref",
1480 ),
1481 CastMutability::MutToMut => (
1482 parse_quote! {
1483 autocxx::PinMut < #to_type >
1484 },
1485 parse_quote! {
1486 ::std::pin::Pin < &'a mut #from_type_path >
1487 },
1488 "pin_mut",
1489 ),
1490 };
1491 let method_name = make_ident(method_name);
1492 Some((
1493 FnKind::TraitMethod {
1494 kind: TraitMethodKind::Cast,
1495 impl_for: from_type.clone(),
1496 details: Box::new(TraitMethodDetails {
1497 trt: TraitImplSignature {
1498 ty,
1499 trait_signature,
1500 unsafety: None,
1501 },
1502 avoid_self: false,
1503 method_name,
1504 parameter_reordering: None,
1505 trait_call_is_unsafe: false,
1506 }),
1507 },
1508 ErrorContext::new_for_item(make_ident(&rust_name)),
1509 rust_name,
1510 ))
1511 }
1512 TraitSynthesis::AllocUninitialized(ty) => self.generate_alloc_or_deallocate(
1513 ideal_rust_name,
1514 ty,
1515 "allocate_uninitialized_cpp_storage",
1516 TraitMethodKind::Alloc,
1517 ),
1518 TraitSynthesis::FreeUninitialized(ty) => self.generate_alloc_or_deallocate(
1519 ideal_rust_name,
1520 ty,
1521 "free_uninitialized_cpp_storage",
1522 TraitMethodKind::Dealloc,
1523 ),
1524 })
1525 }
1526
1527 fn generate_alloc_or_deallocate(
1528 &mut self,
1529 ideal_rust_name: &str,
1530 ty: &QualifiedName,
1531 method_name: &str,
1532 kind: TraitMethodKind,
1533 ) -> Option<(FnKind, ErrorContext, String)> {
1534 let rust_name =
1535 self.get_function_overload_name(ty.get_namespace(), ideal_rust_name.to_string());
1536 let typ = ty.to_type_path();
1537 Some((
1538 FnKind::TraitMethod {
1539 impl_for: ty.clone(),
1540 details: Box::new(TraitMethodDetails {
1541 trt: TraitImplSignature {
1542 ty: Type::Path(typ),
1543 trait_signature: parse_quote! { autocxx::moveit::MakeCppStorage },
1544 unsafety: Some(parse_quote! { unsafe }),
1545 },
1546 avoid_self: false,
1547 method_name: make_ident(method_name),
1548 parameter_reordering: None,
1549 trait_call_is_unsafe: false,
1550 }),
1551 kind,
1552 },
1553 ErrorContext::new_for_item(make_ident(&rust_name)),
1554 rust_name,
1555 ))
1556 }
1557
1558 fn get_function_overload_name(&mut self, ns: &Namespace, ideal_rust_name: String) -> String {
1559 let overload_tracker = self.overload_trackers_by_mod.entry(ns.clone()).or_default();
1560 overload_tracker.get_function_real_name(ideal_rust_name)
1561 }
1562
1563 fn subclasses_by_superclass(&self, sup: &QualifiedName) -> impl Iterator<Item = SubclassName> {
1564 match self.subclasses_by_superclass.get(sup) {
1565 Some(subs) => subs.clone().into_iter(),
1566 None => Vec::new().into_iter(),
1567 }
1568 }
1569
1570 #[allow(clippy::too_many_arguments)] // currently reasonably clear
1571 fn convert_fn_arg(
1572 &mut self,
1573 arg: &FnArg,
1574 ns: &Namespace,
1575 fn_name: &str,
1576 virtual_this: &Option<QualifiedName>,
1577 references: &References,
1578 treat_this_as_reference: bool,
1579 is_move_constructor: bool,
1580 force_rust_conversion: Option<RustConversionType>,
1581 sophistication: TypeConversionSophistication,
1582 construct_into_self: bool,
1583 ) -> Result<(FnArg, ArgumentAnalysis), ConvertError> {
1584 Ok(match arg {
1585 FnArg::Typed(pt) => {
1586 let mut pt = pt.clone();
1587 let mut self_type = None;
1588 let old_pat = *pt.pat;
1589 let mut pointer_treatment = PointerTreatment::Pointer;
1590 let mut is_placement_return_destination = false;
1591 let new_pat = match old_pat {
1592 syn::Pat::Ident(mut pp) if pp.ident == "this" => {
1593 let this_type = match pt.ty.as_ref() {
1594 Type::Ptr(TypePtr {
1595 elem, mutability, ..
1596 }) => match elem.as_ref() {
1597 Type::Path(typ) => {
1598 let receiver_mutability = if mutability.is_some() {
1599 ReceiverMutability::Mutable
1600 } else {
1601 ReceiverMutability::Const
1602 };
1603
1604 let this_type = if let Some(virtual_this) = virtual_this {
1605 let this_type_path = virtual_this.to_type_path();
1606 let const_token = if mutability.is_some() {
1607 None
1608 } else {
1609 Some(syn::Token![const](Span::call_site()))
1610 };
1611 pt.ty = Box::new(parse_quote! {
1612 * #mutability #const_token #this_type_path
1613 });
1614 virtual_this.clone()
1615 } else {
1616 QualifiedName::from_type_path(typ)
1617 };
1618 Ok((this_type, receiver_mutability))
1619 }
1620 _ => Err(ConvertError::UnexpectedThisType(QualifiedName::new(
1621 ns,
1622 make_ident(fn_name),
1623 ))),
1624 },
1625 _ => Err(ConvertError::UnexpectedThisType(QualifiedName::new(
1626 ns,
1627 make_ident(fn_name),
1628 ))),
1629 }?;
1630 self_type = Some(this_type);
1631 is_placement_return_destination = construct_into_self;
1632 if treat_this_as_reference {
1633 pp.ident = Ident::new("self", pp.ident.span());
1634 pointer_treatment = PointerTreatment::Reference;
1635 }
1636 syn::Pat::Ident(pp)
1637 }
1638 syn::Pat::Ident(pp) => {
1639 validate_ident_ok_for_cxx(&pp.ident.to_string())?;
1640 pointer_treatment = references.param_treatment(&pp.ident);
1641 syn::Pat::Ident(pp)
1642 }
1643 _ => old_pat,
1644 };
1645 let is_placement_return_destination = is_placement_return_destination
1646 || matches!(
1647 force_rust_conversion,
1648 Some(RustConversionType::FromPlacementParamToNewReturn)
1649 );
1650 let annotated_type = self.convert_boxed_type(pt.ty, ns, pointer_treatment)?;
1651 let conversion = self.argument_conversion_details(
1652 &annotated_type,
1653 is_move_constructor,
1654 force_rust_conversion,
1655 sophistication,
1656 );
1657 let new_ty = annotated_type.ty;
1658 pt.pat = Box::new(new_pat.clone());
1659 pt.ty = new_ty;
1660 let requires_unsafe =
1661 if matches!(annotated_type.kind, type_converter::TypeKind::Pointer)
1662 && !is_placement_return_destination
1663 {
1664 UnsafetyNeeded::Always
1665 } else if conversion.bridge_unsafe_needed() || is_placement_return_destination {
1666 UnsafetyNeeded::JustBridge
1667 } else {
1668 UnsafetyNeeded::None
1669 };
1670 (
1671 FnArg::Typed(pt),
1672 ArgumentAnalysis {
1673 self_type,
1674 name: new_pat,
1675 conversion,
1676 has_lifetime: matches!(
1677 annotated_type.kind,
1678 type_converter::TypeKind::Reference
1679 | type_converter::TypeKind::MutableReference
1680 ),
1681 deps: annotated_type.types_encountered,
1682 requires_unsafe,
1683 is_placement_return_destination,
1684 },
1685 )
1686 }
1687 _ => panic!("Did not expect FnArg::Receiver to be generated by bindgen"),
1688 })
1689 }
1690
1691 fn argument_conversion_details(
1692 &self,
1693 annotated_type: &Annotated<Box<Type>>,
1694 is_move_constructor: bool,
1695 force_rust_conversion: Option<RustConversionType>,
1696 sophistication: TypeConversionSophistication,
1697 ) -> TypeConversionPolicy {
1698 let is_subclass_holder = match &annotated_type.kind {
1699 type_converter::TypeKind::SubclassHolder(holder) => Some(holder),
1700 _ => None,
1701 };
1702 let is_rvalue_ref = matches!(
1703 annotated_type.kind,
1704 type_converter::TypeKind::RValueReference
1705 );
1706 let ty = &*annotated_type.ty;
1707 if let Some(holder_id) = is_subclass_holder {
1708 let subclass = SubclassName::from_holder_name(holder_id);
1709 return {
1710 let ty = parse_quote! {
1711 rust::Box<#holder_id>
1712 };
1713 TypeConversionPolicy {
1714 unwrapped_type: ty,
1715 cpp_conversion: CppConversionType::Move,
1716 rust_conversion: RustConversionType::ToBoxedUpHolder(subclass),
1717 }
1718 };
1719 } else if matches!(
1720 force_rust_conversion,
1721 Some(RustConversionType::FromPlacementParamToNewReturn)
1722 ) && matches!(sophistication, TypeConversionSophistication::Regular)
1723 {
1724 return TypeConversionPolicy {
1725 unwrapped_type: ty.clone(),
1726 cpp_conversion: CppConversionType::IgnoredPlacementPtrParameter,
1727 rust_conversion: RustConversionType::FromPlacementParamToNewReturn,
1728 };
1729 }
1730 match ty {
1731 Type::Path(p) => {
1732 let ty = ty.clone();
1733 let tn = QualifiedName::from_type_path(p);
1734 if self.pod_safe_types.contains(&tn) {
1735 if known_types().lacks_copy_constructor(&tn) {
1736 TypeConversionPolicy {
1737 unwrapped_type: ty,
1738 cpp_conversion: CppConversionType::Move,
1739 rust_conversion: RustConversionType::None,
1740 }
1741 } else {
1742 TypeConversionPolicy::new_unconverted(ty)
1743 }
1744 } else if known_types().convertible_from_strs(&tn)
1745 && !self.config.exclude_utilities()
1746 {
1747 TypeConversionPolicy {
1748 unwrapped_type: ty,
1749 cpp_conversion: CppConversionType::FromUniquePtrToValue,
1750 rust_conversion: RustConversionType::FromStr,
1751 }
1752 } else if matches!(
1753 sophistication,
1754 TypeConversionSophistication::SimpleForSubclasses
1755 ) {
1756 TypeConversionPolicy {
1757 unwrapped_type: ty,
1758 cpp_conversion: CppConversionType::FromUniquePtrToValue,
1759 rust_conversion: RustConversionType::None,
1760 }
1761 } else {
1762 TypeConversionPolicy {
1763 unwrapped_type: ty,
1764 cpp_conversion: CppConversionType::FromPtrToValue,
1765 rust_conversion: RustConversionType::FromValueParamToPtr,
1766 }
1767 }
1768 }
1769 Type::Ptr(tp) => {
1770 let rust_conversion = force_rust_conversion.unwrap_or(RustConversionType::None);
1771 if is_move_constructor {
1772 TypeConversionPolicy {
1773 unwrapped_type: ty.clone(),
1774 cpp_conversion: CppConversionType::FromPtrToMove,
1775 rust_conversion,
1776 }
1777 } else if is_rvalue_ref {
1778 TypeConversionPolicy {
1779 unwrapped_type: *tp.elem.clone(),
1780 cpp_conversion: CppConversionType::FromPtrToValue,
1781 rust_conversion: RustConversionType::FromRValueParamToPtr,
1782 }
1783 } else {
1784 TypeConversionPolicy {
1785 unwrapped_type: ty.clone(),
1786 cpp_conversion: CppConversionType::None,
1787 rust_conversion,
1788 }
1789 }
1790 }
1791 _ => {
1792 let rust_conversion = force_rust_conversion.unwrap_or(RustConversionType::None);
1793 TypeConversionPolicy {
1794 unwrapped_type: ty.clone(),
1795 cpp_conversion: CppConversionType::None,
1796 rust_conversion,
1797 }
1798 }
1799 }
1800 }
1801
1802 fn convert_return_type(
1803 &mut self,
1804 rt: &ReturnType,
1805 ns: &Namespace,
1806 references: &References,
1807 sophistication: TypeConversionSophistication,
1808 ) -> Result<ReturnTypeAnalysis, ConvertError> {
1809 Ok(match rt {
1810 ReturnType::Default => ReturnTypeAnalysis::default(),
1811 ReturnType::Type(rarrow, boxed_type) => {
1812 let annotated_type =
1813 self.convert_boxed_type(boxed_type.clone(), ns, references.return_treatment())?;
1814 let boxed_type = annotated_type.ty;
1815 let ty: &Type = boxed_type.as_ref();
1816 match ty {
1817 Type::Path(p)
1818 if !self
1819 .pod_safe_types
1820 .contains(&QualifiedName::from_type_path(p)) =>
1821 {
1822 let tn = QualifiedName::from_type_path(p);
1823 if self.moveit_safe_types.contains(&tn)
1824 && matches!(sophistication, TypeConversionSophistication::Regular)
1825 {
1826 // This is a non-POD type we want to return to Rust as an `impl New` so that callers
1827 // can decide whether to store this on the stack or heap.
1828 // That means, we do not literally _return_ it from C++ to Rust. Instead, our call
1829 // from Rust to C++ will include an extra placement parameter into which the object
1830 // is constructed.
1831 let fnarg = parse_quote! {
1832 placement_return_type: *mut #ty
1833 };
1834 let (fnarg, analysis) = self.convert_fn_arg(
1835 &fnarg,
1836 ns,
1837 "",
1838 &None,
1839 &References::default(),
1840 false,
1841 false,
1842 Some(RustConversionType::FromPlacementParamToNewReturn),
1843 TypeConversionSophistication::Regular,
1844 false,
1845 )?;
1846 ReturnTypeAnalysis {
1847 rt: ReturnType::Default,
1848 conversion: Some(TypeConversionPolicy::new_for_placement_return(
1849 ty.clone(),
1850 )),
1851 was_reference: false,
1852 deps: annotated_type.types_encountered,
1853 placement_param_needed: Some((fnarg, analysis)),
1854 }
1855 } else {
1856 // There are some types which we can't currently represent within a moveit::new::New.
1857 // That's either because we are obliged to stick to existing protocols for compatibility
1858 // (CxxString) or because they're a concrete type where we haven't attempted to do
1859 // the analysis to work out the type's size. For these, we always return a plain old
1860 // UniquePtr<T>. These restrictions may be fixed in future.
1861 let conversion =
1862 Some(TypeConversionPolicy::new_to_unique_ptr(ty.clone()));
1863 ReturnTypeAnalysis {
1864 rt: ReturnType::Type(*rarrow, boxed_type),
1865 conversion,
1866 was_reference: false,
1867 deps: annotated_type.types_encountered,
1868 placement_param_needed: None,
1869 }
1870 }
1871 }
1872 _ => {
1873 let was_reference = matches!(boxed_type.as_ref(), Type::Reference(_));
1874 let conversion = Some(TypeConversionPolicy::new_unconverted(ty.clone()));
1875 ReturnTypeAnalysis {
1876 rt: ReturnType::Type(*rarrow, boxed_type),
1877 conversion,
1878 was_reference,
1879 deps: annotated_type.types_encountered,
1880 placement_param_needed: None,
1881 }
1882 }
1883 }
1884 }
1885 })
1886 }
1887
1888 /// If a type has explicit constructors, bindgen will generate corresponding
1889 /// constructor functions, which we'll have already converted to make_unique methods.
1890 /// C++ mandates the synthesis of certain implicit constructors, to which we
1891 /// need to create bindings too. We do that here.
1892 /// It is tempting to make this a separate analysis phase, to be run later than
1893 /// the function analysis; but that would make the code much more complex as it
1894 /// would need to output a `FnAnalysisBody`. By running it as part of this phase
1895 /// we can simply generate the sort of thing bindgen generates, then ask
1896 /// the existing code in this phase to figure out what to do with it.
1897 ///
1898 /// Also fills out the [`PodAndConstructorAnalysis::constructors`] fields with information useful
1899 /// for further analysis phases.
1900 fn add_constructors_present(&mut self, mut apis: ApiVec<FnPrePhase1>) -> ApiVec<FnPrePhase2> {
1901 let all_items_found = find_constructors_present(&apis);
1902 for (self_ty, items_found) in all_items_found.iter() {
1903 if self.config.exclude_impls {
1904 // Remember that `find_constructors_present` mutates `apis`, so we always have to
1905 // call that, even if we don't do anything with the return value. This is kind of
1906 // messy, see the comment on this function for why.
1907 continue;
1908 }
1909 if self
1910 .config
1911 .is_on_constructor_blocklist(&self_ty.to_cpp_name())
1912 {
1913 continue;
1914 }
1915 let path = self_ty.to_type_path();
1916 if items_found.implicit_default_constructor_needed() {
1917 self.synthesize_special_member(
1918 items_found,
1919 "default_ctor",
1920 &mut apis,
1921 SpecialMemberKind::DefaultConstructor,
1922 parse_quote! { this: *mut #path },
1923 References::default(),
1924 );
1925 }
1926 if items_found.implicit_move_constructor_needed() {
1927 self.synthesize_special_member(
1928 items_found,
1929 "move_ctor",
1930 &mut apis,
1931 SpecialMemberKind::MoveConstructor,
1932 parse_quote! { this: *mut #path, other: *mut #path },
1933 References {
1934 rvalue_ref_params: [make_ident("other")].into_iter().collect(),
1935 ..Default::default()
1936 },
1937 )
1938 }
1939 if items_found.implicit_copy_constructor_needed() {
1940 self.synthesize_special_member(
1941 items_found,
1942 "const_copy_ctor",
1943 &mut apis,
1944 SpecialMemberKind::CopyConstructor,
1945 parse_quote! { this: *mut #path, other: *const #path },
1946 References {
1947 ref_params: [make_ident("other")].into_iter().collect(),
1948 ..Default::default()
1949 },
1950 )
1951 }
1952 if items_found.implicit_destructor_needed() {
1953 self.synthesize_special_member(
1954 items_found,
1955 "destructor",
1956 &mut apis,
1957 SpecialMemberKind::Destructor,
1958 parse_quote! { this: *mut #path },
1959 References::default(),
1960 );
1961 }
1962 }
1963
1964 // Also, annotate each type with the constructors we found.
1965 let mut results = ApiVec::new();
1966 convert_apis(
1967 apis,
1968 &mut results,
1969 Api::fun_unchanged,
1970 |name, details, analysis| {
1971 let items_found = all_items_found.get(&name.name);
1972 Ok(Box::new(std::iter::once(Api::Struct {
1973 name,
1974 details,
1975 analysis: PodAndConstructorAnalysis {
1976 pod: analysis,
1977 constructors: if let Some(items_found) = items_found {
1978 PublicConstructors::from_items_found(items_found)
1979 } else {
1980 PublicConstructors::default()
1981 },
1982 },
1983 })))
1984 },
1985 Api::enum_unchanged,
1986 Api::typedef_unchanged,
1987 );
1988 results
1989 }
1990
1991 #[allow(clippy::too_many_arguments)] // it's true, but sticking with it for now
1992 fn synthesize_special_member(
1993 &mut self,
1994 items_found: &ItemsFound,
1995 label: &str,
1996 apis: &mut ApiVec<FnPrePhase1>,
1997 special_member: SpecialMemberKind,
1998 inputs: Punctuated<FnArg, Comma>,
1999 references: References,
2000 ) {
2001 let self_ty = items_found.name.as_ref().unwrap();
2002 let ident = make_ident(self.config.uniquify_name_per_mod(&format!(
2003 "{}_synthetic_{}",
2004 self_ty.name.get_final_item(),
2005 label
2006 )));
2007 let cpp_name = if matches!(special_member, SpecialMemberKind::DefaultConstructor) {
2008 // Constructors (other than move or copy) are identified in `analyze_foreign_fn` by
2009 // being suffixed with the cpp_name, so we have to produce that.
2010 self.nested_type_name_map
2011 .get(&self_ty.name)
2012 .cloned()
2013 .or_else(|| Some(self_ty.name.get_final_item().to_string()))
2014 } else {
2015 None
2016 };
2017 let fake_api_name =
2018 ApiName::new_with_cpp_name(self_ty.name.get_namespace(), ident.clone(), cpp_name);
2019 let self_ty = &self_ty.name;
2020 let ns = self_ty.get_namespace().clone();
2021 let mut any_errors = ApiVec::new();
2022 apis.extend(
2023 report_any_error(&ns, &mut any_errors, || {
2024 self.analyze_foreign_fn_and_subclasses(
2025 fake_api_name,
2026 Box::new(FuncToConvert {
2027 self_ty: Some(self_ty.clone()),
2028 ident,
2029 doc_attrs: make_doc_attrs(format!("Synthesized {}.", special_member)),
2030 inputs,
2031 output: ReturnType::Default,
2032 vis: parse_quote! { pub },
2033 virtualness: Virtualness::None,
2034 cpp_vis: CppVisibility::Public,
2035 special_member: Some(special_member),
2036 unused_template_param: false,
2037 references,
2038 original_name: None,
2039 synthesized_this_type: None,
2040 is_deleted: false,
2041 add_to_trait: None,
2042 synthetic_cpp: None,
2043 provenance: Provenance::SynthesizedOther,
2044 variadic: false,
2045 }),
2046 )
2047 })
2048 .into_iter()
2049 .flatten(),
2050 );
2051 apis.append(&mut any_errors);
2052 }
2053}
2054
2055/// Attempts to determine whether this function name is a constructor, and if so,
2056/// returns the suffix.
2057fn constructor_with_suffix<'a>(rust_name: &'a str, nested_type_ident: &str) -> Option<&'a str> {
2058 let suffix = rust_name.strip_prefix(nested_type_ident);
2059 suffix.and_then(|suffix| {
2060 if suffix.is_empty() || suffix.parse::<u32>().is_ok() {
2061 Some(suffix)
2062 } else {
2063 None
2064 }
2065 })
2066}
2067
2068impl Api<FnPhase> {
2069 pub(crate) fn name_for_allowlist(&self) -> QualifiedName {
2070 match &self {
2071 Api::Function { analysis, .. } => match analysis.kind {
2072 FnKind::Method { ref impl_for, .. } => impl_for.clone(),
2073 FnKind::TraitMethod { ref impl_for, .. } => impl_for.clone(),
2074 FnKind::Function => {
2075 QualifiedName::new(self.name().get_namespace(), make_ident(&analysis.rust_name))
2076 }
2077 },
2078 Api::RustSubclassFn { subclass, .. } => subclass.0.name.clone(),
2079 Api::IgnoredItem {
2080 name,
2081 ctx: Some(ctx),
2082 ..
2083 } => match ctx.get_type() {
2084 ErrorContextType::Method { self_ty, .. } => {
2085 QualifiedName::new(name.name.get_namespace(), self_ty.clone())
2086 }
2087 ErrorContextType::Item(id) => {
2088 QualifiedName::new(name.name.get_namespace(), id.clone())
2089 }
2090 _ => name.name.clone(),
2091 },
2092 _ => self.name().clone(),
2093 }
2094 }
2095
2096 /// Whether this API requires generation of additional C++.
2097 /// This seems an odd place for this function (as opposed to in the [codegen_cpp]
2098 /// module) but, as it happens, even our Rust codegen phase needs to know if
2099 /// more C++ is needed (so it can add #includes in the cxx mod).
2100 /// And we can't answer the question _prior_ to this function analysis phase.
2101 pub(crate) fn needs_cpp_codegen(&self) -> bool {
2102 matches!(
2103 &self,
2104 Api::Function {
2105 analysis: FnAnalysis {
2106 cpp_wrapper: Some(..),
2107 ignore_reason: Ok(_),
2108 externally_callable: true,
2109 ..
2110 },
2111 ..
2112 } | Api::StringConstructor { .. }
2113 | Api::ConcreteType { .. }
2114 | Api::CType { .. }
2115 | Api::RustSubclassFn { .. }
2116 | Api::Subclass { .. }
2117 | Api::Struct {
2118 analysis: PodAndDepAnalysis {
2119 pod: PodAnalysis {
2120 kind: TypeKind::Pod,
2121 ..
2122 },
2123 ..
2124 },
2125 ..
2126 }
2127 )
2128 }
2129
2130 pub(crate) fn cxxbridge_name(&self) -> Option<Ident> {
2131 match self {
2132 Api::Function { ref analysis, .. } => Some(analysis.cxxbridge_name.clone()),
2133 Api::StringConstructor { .. }
2134 | Api::Const { .. }
2135 | Api::IgnoredItem { .. }
2136 | Api::RustSubclassFn { .. } => None,
2137 _ => Some(self.name().get_final_ident()),
2138 }
2139 }
2140}