blob: 5dd4cbb8cea0e7c85a63b51ad05595c8d8279cc3 [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 fun_codegen;
10mod function_wrapper_rs;
11mod impl_item_creator;
12mod lifetime;
13mod namespace_organizer;
14mod non_pod_struct;
15pub(crate) mod unqualify;
16
17use indexmap::map::IndexMap as HashMap;
18use indexmap::set::IndexSet as HashSet;
19
Brian Silvermanf3ec38b2022-07-06 20:43:36 -070020use autocxx_parser::{ExternCppType, IncludeCppConfig, RustFun, UnsafePolicy};
Brian Silverman4e662aa2022-05-11 23:10:19 -070021
22use itertools::Itertools;
23use proc_macro2::{Span, TokenStream};
24use syn::{
25 parse_quote, punctuated::Punctuated, token::Comma, Attribute, Expr, FnArg, ForeignItem,
Brian Silvermanf3ec38b2022-07-06 20:43:36 -070026 ForeignItemFn, Ident, ImplItem, Item, ItemForeignMod, ItemMod, Lifetime, TraitItem, Type,
27 TypePath,
Brian Silverman4e662aa2022-05-11 23:10:19 -070028};
29
30use crate::{
31 conversion::{
32 codegen_rs::{
33 non_pod_struct::{make_non_pod, new_non_pod_struct},
34 unqualify::{unqualify_params, unqualify_ret_type},
35 },
36 doc_attr::get_doc_attrs,
37 },
38 types::{make_ident, Namespace, QualifiedName},
39};
40use impl_item_creator::create_impl_items;
41
42use self::{
43 fun_codegen::gen_function,
44 namespace_organizer::{HasNs, NamespaceEntries},
45};
46
47use super::{
48 analysis::{
49 fun::{FnPhase, PodAndDepAnalysis, ReceiverMutability},
50 pod::PodAnalysis,
51 },
52 api::{AnalysisPhase, Api, SubclassName, TypeKind, TypedefKind},
53 convert_error::ErrorContextType,
54};
55use super::{
56 api::{Layout, Provenance, RustSubclassFnDetails, SuperclassMethod, TraitImplSignature},
57 apivec::ApiVec,
58 codegen_cpp::type_to_cpp::{
59 namespaced_name_using_original_name_map, original_name_map_from_apis, CppNameMap,
60 },
61};
62use super::{convert_error::ErrorContext, ConvertError};
63use quote::quote;
64
Brian Silvermanf3ec38b2022-07-06 20:43:36 -070065#[derive(Clone, Hash, PartialEq, Eq)]
66struct ImplBlockKey {
67 ty: Type,
68 lifetime: Option<Lifetime>,
69}
70
Brian Silverman4e662aa2022-05-11 23:10:19 -070071/// An entry which needs to go into an `impl` block for a given type.
72struct ImplBlockDetails {
73 item: ImplItem,
Brian Silvermanf3ec38b2022-07-06 20:43:36 -070074 ty: ImplBlockKey,
Brian Silverman4e662aa2022-05-11 23:10:19 -070075}
76
77struct TraitImplBlockDetails {
78 item: TraitItem,
79 key: TraitImplSignature,
80}
81
82/// Whether and how this item should be exposed in the mods constructed
83/// for actual end-user use.
84#[derive(Clone)]
85enum Use {
86 /// Uses from cxx::bridge
87 UsedFromCxxBridge,
88 /// 'use' points to cxx::bridge with a different name
89 UsedFromCxxBridgeWithAlias(Ident),
90 /// 'use' directive points to bindgen
91 UsedFromBindgen,
92 /// 'use' a specific name from bindgen.
93 SpecificNameFromBindgen(Ident),
94 /// Some kind of custom item
95 Custom(Box<Item>),
96}
97
98fn get_string_items() -> Vec<Item> {
99 [
100 Item::Trait(parse_quote! {
101 pub trait ToCppString {
102 fn into_cpp(self) -> cxx::UniquePtr<cxx::CxxString>;
103 }
104 }),
105 // We can't just impl<T: AsRef<str>> ToCppString for T
106 // because the compiler says that this trait could be implemented
107 // in future for cxx::UniquePtr<cxx::CxxString>. Fair enough.
108 Item::Impl(parse_quote! {
109 impl ToCppString for &str {
110 fn into_cpp(self) -> cxx::UniquePtr<cxx::CxxString> {
111 make_string(self)
112 }
113 }
114 }),
115 Item::Impl(parse_quote! {
116 impl ToCppString for String {
117 fn into_cpp(self) -> cxx::UniquePtr<cxx::CxxString> {
118 make_string(&self)
119 }
120 }
121 }),
122 Item::Impl(parse_quote! {
123 impl ToCppString for &String {
124 fn into_cpp(self) -> cxx::UniquePtr<cxx::CxxString> {
125 make_string(self)
126 }
127 }
128 }),
129 Item::Impl(parse_quote! {
130 impl ToCppString for cxx::UniquePtr<cxx::CxxString> {
131 fn into_cpp(self) -> cxx::UniquePtr<cxx::CxxString> {
132 self
133 }
134 }
135 }),
136 ]
137 .to_vec()
138}
139
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700140fn get_cppref_items() -> Vec<Item> {
141 [
142 Item::Struct(parse_quote! {
143 #[repr(transparent)]
144 pub struct CppRef<'a, T>(pub *const T, pub ::std::marker::PhantomData<&'a T>);
145 }),
146 Item::Impl(parse_quote! {
147 impl<'a, T> autocxx::CppRef<'a, T> for CppRef<'a, T> {
148 fn as_ptr(&self) -> *const T {
149 self.0
150 }
151 }
152 }),
153 Item::Struct(parse_quote! {
154 #[repr(transparent)]
155 pub struct CppMutRef<'a, T>(pub *mut T, pub ::std::marker::PhantomData<&'a T>);
156 }),
157 Item::Impl(parse_quote! {
158 impl<'a, T> autocxx::CppRef<'a, T> for CppMutRef<'a, T> {
159 fn as_ptr(&self) -> *const T {
160 self.0
161 }
162 }
163 }),
164 Item::Impl(parse_quote! {
165 impl<'a, T> autocxx::CppMutRef<'a, T> for CppMutRef<'a, T> {
166 fn as_mut_ptr(&self) -> *mut T {
167 self.0
168 }
169 }
170 }),
171 Item::Impl(parse_quote! {
172 impl<'a, T: ::cxx::private::UniquePtrTarget> CppMutRef<'a, T> {
173 /// Create a const C++ reference from this mutable C++ reference.
174 pub fn as_cpp_ref(&self) -> CppRef<'a, T> {
175 use autocxx::CppRef;
176 CppRef(self.as_ptr(), ::std::marker::PhantomData)
177 }
178 }
179 }),
180 Item::Struct(parse_quote! {
181 /// "Pins" a `UniquePtr` to an object, so that C++-compatible references can be created.
182 /// See [`::autocxx::CppPin`]
183 #[repr(transparent)]
184 pub struct CppUniquePtrPin<T: ::cxx::private::UniquePtrTarget>(::cxx::UniquePtr<T>);
185 }),
186 Item::Impl(parse_quote! {
187 impl<'a, T: 'a + ::cxx::private::UniquePtrTarget> autocxx::CppPin<'a, T> for CppUniquePtrPin<T>
188 {
189 type CppRef = CppRef<'a, T>;
190 type CppMutRef = CppMutRef<'a, T>;
191 fn as_ptr(&self) -> *const T {
192 // TODO add as_ptr to cxx to avoid the ephemeral reference
193 self.0.as_ref().unwrap() as *const T
194 }
195 fn as_mut_ptr(&mut self) -> *mut T {
196 unsafe { ::std::pin::Pin::into_inner_unchecked(self.0.as_mut().unwrap()) as *mut T }
197 }
198 fn as_cpp_ref(&self) -> Self::CppRef {
199 CppRef(self.as_ptr(), ::std::marker::PhantomData)
200 }
201 fn as_cpp_mut_ref(&mut self) -> Self::CppMutRef {
202 CppMutRef(self.as_mut_ptr(), ::std::marker::PhantomData)
203 }
204 }
205 }),
206 Item::Impl(parse_quote! {
207 impl<T: ::cxx::private::UniquePtrTarget> CppUniquePtrPin<T> {
208 pub fn new(item: ::cxx::UniquePtr<T>) -> Self {
209 Self(item)
210 }
211 }
212 }),
213 Item::Fn(parse_quote! {
214 /// Pin this item so that we can create C++ references to it.
215 /// This makes it impossible to hold Rust references because Rust
216 /// references are fundamentally incompatible with C++ references.
217 pub fn cpp_pin_uniqueptr<T: ::cxx::private::UniquePtrTarget> (item: ::cxx::UniquePtr<T>) -> CppUniquePtrPin<T> {
218 CppUniquePtrPin::new(item)
219 }
220 })
221 ]
222 .to_vec()
223}
224
Brian Silverman4e662aa2022-05-11 23:10:19 -0700225/// Type which handles generation of Rust code.
226/// In practice, much of the "generation" involves connecting together
227/// existing lumps of code within the Api structures.
228pub(crate) struct RsCodeGenerator<'a> {
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700229 unsafe_policy: &'a UnsafePolicy,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700230 include_list: &'a [String],
231 bindgen_mod: ItemMod,
232 original_name_map: CppNameMap,
233 config: &'a IncludeCppConfig,
234 header_name: Option<String>,
235}
236
237impl<'a> RsCodeGenerator<'a> {
238 /// Generate code for a set of APIs that was discovered during parsing.
239 pub(crate) fn generate_rs_code(
240 all_apis: ApiVec<FnPhase>,
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700241 unsafe_policy: &'a UnsafePolicy,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700242 include_list: &'a [String],
243 bindgen_mod: ItemMod,
244 config: &'a IncludeCppConfig,
245 header_name: Option<String>,
246 ) -> Vec<Item> {
247 let c = Self {
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700248 unsafe_policy,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700249 include_list,
250 bindgen_mod,
251 original_name_map: original_name_map_from_apis(&all_apis),
252 config,
253 header_name,
254 };
255 c.rs_codegen(all_apis)
256 }
257
258 fn rs_codegen(mut self, all_apis: ApiVec<FnPhase>) -> Vec<Item> {
259 // ... and now let's start to generate the output code.
260 // First off, when we generate structs we may need to add some methods
261 // if they're superclasses.
262 let methods_by_superclass = self.accumulate_superclass_methods(&all_apis);
263 let subclasses_with_a_single_trivial_constructor =
264 find_trivially_constructed_subclasses(&all_apis);
265 let non_pod_types = find_non_pod_types(&all_apis);
266 // Now let's generate the Rust code.
267 let (rs_codegen_results_and_namespaces, additional_cpp_needs): (Vec<_>, Vec<_>) = all_apis
268 .into_iter()
269 .map(|api| {
270 let more_cpp_needed = api.needs_cpp_codegen();
271 let name = api.name().clone();
272 let gen = self.generate_rs_for_api(
273 api,
274 &methods_by_superclass,
275 &subclasses_with_a_single_trivial_constructor,
276 &non_pod_types,
277 );
278 ((name, gen), more_cpp_needed)
279 })
280 .unzip();
281 // First, the hierarchy of mods containing lots of 'use' statements
282 // which is the final API exposed as 'ffi'.
283 let mut use_statements =
284 Self::generate_final_use_statements(&rs_codegen_results_and_namespaces);
285 // And work out what we need for the bindgen mod.
286 let bindgen_root_items =
287 self.generate_final_bindgen_mods(&rs_codegen_results_and_namespaces);
288 // Both of the above ('use' hierarchy and bindgen mod) are organized into
289 // sub-mods by namespace. From here on, things are flat.
290 let (_, rs_codegen_results): (Vec<_>, Vec<_>) =
291 rs_codegen_results_and_namespaces.into_iter().unzip();
292 let (extern_c_mod_items, extern_rust_mod_items, all_items, bridge_items): (
293 Vec<_>,
294 Vec<_>,
295 Vec<_>,
296 Vec<_>,
297 ) = rs_codegen_results
298 .into_iter()
299 .map(|api| {
300 (
301 api.extern_c_mod_items,
302 api.extern_rust_mod_items,
303 api.global_items,
304 api.bridge_items,
305 )
306 })
307 .multiunzip();
308 // Items for the [cxx::bridge] mod...
309 let mut bridge_items: Vec<Item> = bridge_items.into_iter().flatten().collect();
310 // Things to include in the "extern "C"" mod passed within the cxx::bridge
311 let mut extern_c_mod_items: Vec<ForeignItem> =
312 extern_c_mod_items.into_iter().flatten().collect();
313 // The same for extern "Rust"
314 let mut extern_rust_mod_items = extern_rust_mod_items.into_iter().flatten().collect();
315 // And a list of global items to include at the top level.
316 let mut all_items: Vec<Item> = all_items.into_iter().flatten().collect();
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700317 if self.config.unsafe_policy.requires_cpprefs() {
318 all_items.append(&mut get_cppref_items())
319 }
Brian Silverman4e662aa2022-05-11 23:10:19 -0700320 // And finally any C++ we need to generate. And by "we" I mean autocxx not cxx.
321 let has_additional_cpp_needs = additional_cpp_needs.into_iter().any(std::convert::identity);
322 extern_c_mod_items.extend(self.build_include_foreign_items(has_additional_cpp_needs));
323 // We will always create an extern "C" mod even if bindgen
324 // didn't generate one, e.g. because it only generated types.
325 // We still want cxx to know about those types.
326 let mut extern_c_mod: ItemForeignMod = parse_quote!(
327 extern "C++" {}
328 );
329 extern_c_mod.items.append(&mut extern_c_mod_items);
330 bridge_items.push(Self::make_foreign_mod_unsafe(extern_c_mod));
331 let mut extern_rust_mod: ItemForeignMod = parse_quote!(
332 extern "Rust" {}
333 );
334 extern_rust_mod.items.append(&mut extern_rust_mod_items);
335 bridge_items.push(Item::ForeignMod(extern_rust_mod));
336 // The extensive use of parse_quote here could end up
337 // being a performance bottleneck. If so, we might want
338 // to set the 'contents' field of the ItemMod
339 // structures directly.
340 if !bindgen_root_items.is_empty() {
341 self.bindgen_mod.vis = parse_quote! {};
342 self.bindgen_mod.content.as_mut().unwrap().1 = vec![Item::Mod(parse_quote! {
343 pub(super) mod root {
344 #(#bindgen_root_items)*
345 }
346 })];
347 all_items.push(Item::Mod(self.bindgen_mod));
348 }
349 all_items.push(Item::Mod(parse_quote! {
350 #[cxx::bridge]
351 mod cxxbridge {
352 #(#bridge_items)*
353 }
354 }));
355
356 all_items.push(Item::Use(parse_quote! {
357 #[allow(unused_imports)]
358 use bindgen::root;
359 }));
360 all_items.append(&mut use_statements);
361 all_items
362 }
363
364 fn accumulate_superclass_methods(
365 &self,
366 apis: &ApiVec<FnPhase>,
367 ) -> HashMap<QualifiedName, Vec<SuperclassMethod>> {
368 let mut results = HashMap::new();
369 results.extend(
370 self.config
371 .superclasses()
372 .map(|sc| (QualifiedName::new_from_cpp_name(sc), Vec::new())),
373 );
374 for api in apis.iter() {
375 if let Api::SubclassTraitItem { details, .. } = api {
376 let list = results.get_mut(&details.receiver);
377 if let Some(list) = list {
378 list.push(details.clone());
379 }
380 }
381 }
382 results
383 }
384
385 fn make_foreign_mod_unsafe(ifm: ItemForeignMod) -> Item {
386 // At the moment syn does not support outputting 'unsafe extern "C"' except in verbatim
387 // items. See https://github.com/dtolnay/syn/pull/938
388 Item::Verbatim(quote! {
389 unsafe #ifm
390 })
391 }
392
393 fn build_include_foreign_items(&self, has_additional_cpp_needs: bool) -> Vec<ForeignItem> {
394 let extra_inclusion = if has_additional_cpp_needs {
395 Some(self.header_name.clone().unwrap())
396 } else {
397 None
398 };
399 let chained = self.include_list.iter().chain(extra_inclusion.iter());
400 chained
401 .map(|inc| {
402 ForeignItem::Macro(parse_quote! {
403 include!(#inc);
404 })
405 })
406 .collect()
407 }
408
409 /// Generate lots of 'use' statements to pull cxxbridge items into the output
410 /// mod hierarchy according to C++ namespaces.
411 fn generate_final_use_statements(
412 input_items: &[(QualifiedName, RsCodegenResult)],
413 ) -> Vec<Item> {
414 let mut output_items = Vec::new();
415 let ns_entries = NamespaceEntries::new(input_items);
416 Self::append_child_use_namespace(&ns_entries, &mut output_items);
417 output_items
418 }
419
420 fn append_child_use_namespace(
421 ns_entries: &NamespaceEntries<(QualifiedName, RsCodegenResult)>,
422 output_items: &mut Vec<Item>,
423 ) {
424 for (name, codegen) in ns_entries.entries() {
425 output_items.extend(codegen.materializations.iter().map(|materialization| {
426 match materialization {
427 Use::UsedFromCxxBridgeWithAlias(ref alias) => {
428 Self::generate_cxx_use_stmt(name, Some(alias))
429 }
430 Use::UsedFromCxxBridge => Self::generate_cxx_use_stmt(name, None),
431 Use::UsedFromBindgen => Self::generate_bindgen_use_stmt(name),
432 Use::SpecificNameFromBindgen(id) => {
433 let name = QualifiedName::new(name.get_namespace(), id.clone());
434 Self::generate_bindgen_use_stmt(&name)
435 }
436 Use::Custom(item) => *item.clone(),
437 }
438 }));
439 }
440 for (child_name, child_ns_entries) in ns_entries.children() {
441 if child_ns_entries.is_empty() {
442 continue;
443 }
444 let child_id = make_ident(child_name);
445 let mut new_mod: ItemMod = parse_quote!(
446 pub mod #child_id {
447 }
448 );
449 Self::append_child_use_namespace(
450 child_ns_entries,
451 &mut new_mod.content.as_mut().unwrap().1,
452 );
453 output_items.push(Item::Mod(new_mod));
454 }
455 }
456
457 fn append_uses_for_ns(&mut self, items: &mut Vec<Item>, ns: &Namespace) {
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700458 let mut imports_from_super = vec!["cxxbridge"];
459 if !self.config.exclude_utilities() {
460 imports_from_super.push("ToCppString");
461 }
462 if self.config.unsafe_policy.requires_cpprefs() {
463 imports_from_super.extend(["CppRef", "CppMutRef"]);
464 }
465 let imports_from_super = imports_from_super.into_iter().map(make_ident);
Brian Silverman4e662aa2022-05-11 23:10:19 -0700466 let super_duper = std::iter::repeat(make_ident("super")); // I'll get my coat
467 let supers = super_duper.clone().take(ns.depth() + 2);
468 items.push(Item::Use(parse_quote! {
469 #[allow(unused_imports)]
470 use self::
471 #(#supers)::*
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700472 ::{
473 #(#imports_from_super),*
474 };
Brian Silverman4e662aa2022-05-11 23:10:19 -0700475 }));
Brian Silverman4e662aa2022-05-11 23:10:19 -0700476 let supers = super_duper.take(ns.depth() + 1);
477 items.push(Item::Use(parse_quote! {
478 #[allow(unused_imports)]
479 use self::
480 #(#supers)::*
481 ::root;
482 }));
483 }
484
485 fn append_child_bindgen_namespace(
486 &mut self,
487 ns_entries: &NamespaceEntries<(QualifiedName, RsCodegenResult)>,
488 output_items: &mut Vec<Item>,
489 ns: &Namespace,
490 ) {
491 let mut impl_entries_by_type: HashMap<_, Vec<_>> = HashMap::new();
492 let mut trait_impl_entries_by_trait_and_ty: HashMap<_, Vec<_>> = HashMap::new();
493 for item in ns_entries.entries() {
494 output_items.extend(item.1.bindgen_mod_items.iter().cloned());
495 if let Some(impl_entry) = &item.1.impl_entry {
496 impl_entries_by_type
497 .entry(impl_entry.ty.clone())
498 .or_default()
499 .push(&impl_entry.item);
500 }
501 if let Some(trait_impl_entry) = &item.1.trait_impl_entry {
502 trait_impl_entries_by_trait_and_ty
503 .entry(trait_impl_entry.key.clone())
504 .or_default()
505 .push(&trait_impl_entry.item);
506 }
507 }
508 for (ty, entries) in impl_entries_by_type.into_iter() {
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700509 let lt = ty.lifetime.map(|lt| quote! { < #lt > });
510 let ty = ty.ty;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700511 output_items.push(Item::Impl(parse_quote! {
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700512 impl #lt #ty {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700513 #(#entries)*
514 }
515 }))
516 }
517 for (key, entries) in trait_impl_entries_by_trait_and_ty.into_iter() {
518 let unsafety = key.unsafety;
519 let ty = key.ty;
520 let trt = key.trait_signature;
521 output_items.push(Item::Impl(parse_quote! {
522 #unsafety impl #trt for #ty {
523 #(#entries)*
524 }
525 }))
526 }
527 for (child_name, child_ns_entries) in ns_entries.children() {
528 let new_ns = ns.push((*child_name).clone());
529 let child_id = make_ident(child_name);
530
531 let mut inner_output_items = Vec::new();
532 self.append_child_bindgen_namespace(child_ns_entries, &mut inner_output_items, &new_ns);
533 if !inner_output_items.is_empty() {
534 let mut new_mod: ItemMod = parse_quote!(
535 pub mod #child_id {
536 }
537 );
538 self.append_uses_for_ns(&mut inner_output_items, &new_ns);
539 new_mod.content.as_mut().unwrap().1 = inner_output_items;
540 output_items.push(Item::Mod(new_mod));
541 }
542 }
543 }
544
545 fn id_to_expr(id: &Ident) -> Expr {
546 parse_quote! { #id }
547 }
548
549 fn generate_final_bindgen_mods(
550 &mut self,
551 input_items: &[(QualifiedName, RsCodegenResult)],
552 ) -> Vec<Item> {
553 let mut output_items = Vec::new();
554 let ns = Namespace::new();
555 let ns_entries = NamespaceEntries::new(input_items);
556 self.append_child_bindgen_namespace(&ns_entries, &mut output_items, &ns);
557 self.append_uses_for_ns(&mut output_items, &ns);
558 output_items
559 }
560
561 fn generate_rs_for_api(
562 &self,
563 api: Api<FnPhase>,
564 associated_methods: &HashMap<QualifiedName, Vec<SuperclassMethod>>,
565 subclasses_with_a_single_trivial_constructor: &HashSet<QualifiedName>,
566 non_pod_types: &HashSet<QualifiedName>,
567 ) -> RsCodegenResult {
568 let name = api.name().clone();
569 let id = name.get_final_ident();
570 let cpp_call_name = api.effective_cpp_name().to_string();
571 match api {
572 Api::StringConstructor { .. } => {
573 let make_string_name = make_ident(self.config.get_makestring_name());
574 RsCodegenResult {
575 extern_c_mod_items: vec![ForeignItem::Fn(parse_quote!(
576 fn #make_string_name(str_: &str) -> UniquePtr<CxxString>;
577 ))],
578 global_items: get_string_items(),
579 materializations: vec![Use::UsedFromCxxBridgeWithAlias(make_ident(
580 "make_string",
581 ))],
582 ..Default::default()
583 }
584 }
585 Api::Function { fun, analysis, .. } => gen_function(
586 name.get_namespace(),
587 *fun,
588 analysis,
589 cpp_call_name,
590 non_pod_types,
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700591 self.config,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700592 ),
593 Api::Const { const_item, .. } => RsCodegenResult {
594 bindgen_mod_items: vec![Item::Const(const_item)],
595 materializations: vec![Use::UsedFromBindgen],
596 ..Default::default()
597 },
598 Api::Typedef { analysis, .. } => RsCodegenResult {
599 bindgen_mod_items: vec![match analysis.kind {
600 TypedefKind::Type(type_item) => Item::Type(type_item),
601 TypedefKind::Use(use_item, _) => Item::Use(use_item),
602 }],
603 materializations: vec![Use::UsedFromBindgen],
604 ..Default::default()
605 },
606 Api::Struct {
607 details,
608 analysis:
609 PodAndDepAnalysis {
610 pod:
611 PodAnalysis {
612 is_generic, kind, ..
613 },
614 constructors,
615 ..
616 },
617 ..
618 } => {
619 let doc_attrs = get_doc_attrs(&details.item.attrs);
620 let layout = details.layout.clone();
621 self.generate_type(
622 &name,
623 id,
624 kind,
625 constructors.move_constructor,
626 constructors.destructor,
627 || Some((Item::Struct(details.item), doc_attrs)),
628 associated_methods,
629 layout,
630 is_generic,
631 )
632 }
633 Api::Enum { item, .. } => {
634 let doc_attrs = get_doc_attrs(&item.attrs);
635 self.generate_type(
636 &name,
637 id,
638 TypeKind::Pod,
639 true,
640 true,
641 || Some((Item::Enum(item), doc_attrs)),
642 associated_methods,
643 None,
644 false,
645 )
646 }
647 Api::ForwardDeclaration { .. }
648 | Api::ConcreteType { .. }
649 | Api::OpaqueTypedef { .. } => self.generate_type(
650 &name,
651 id,
652 TypeKind::Abstract,
653 false, // assume for now that these types can't be kept in a Vector
654 true, // assume for now that these types can be put in a smart pointer
655 || None,
656 associated_methods,
657 None,
658 false,
659 ),
660 Api::CType { .. } => RsCodegenResult {
661 extern_c_mod_items: vec![ForeignItem::Verbatim(quote! {
662 type #id = autocxx::#id;
663 })],
664 ..Default::default()
665 },
666 Api::RustType { path, .. } => RsCodegenResult {
667 global_items: vec![parse_quote! {
668 use super::#path;
669 }],
670 extern_rust_mod_items: vec![parse_quote! {
671 type #id;
672 }],
673 ..Default::default()
674 },
675 Api::RustFn {
676 details:
677 RustFun {
678 path,
679 sig,
680 receiver: None,
681 ..
682 },
683 ..
684 } => RsCodegenResult {
685 global_items: vec![parse_quote! {
686 use super::#path;
687 }],
688 extern_rust_mod_items: vec![parse_quote! {
689 #sig;
690 }],
691 ..Default::default()
692 },
693 Api::RustFn {
694 details:
695 RustFun {
696 sig,
697 receiver: Some(_),
698 ..
699 },
700 ..
701 } => RsCodegenResult {
702 extern_rust_mod_items: vec![parse_quote! {
703 #sig;
704 }],
705 ..Default::default()
706 },
707 Api::RustSubclassFn {
708 details, subclass, ..
709 } => Self::generate_subclass_fn(id, *details, subclass),
710 Api::Subclass {
711 name, superclass, ..
712 } => {
713 let methods = associated_methods.get(&superclass);
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700714 let generate_peer_constructor = subclasses_with_a_single_trivial_constructor.contains(&name.0.name) &&
715 // TODO: Create an UnsafeCppPeerConstructor trait for calling an unsafe
716 // constructor instead? Need to create unsafe versions of everything that uses
717 // it too.
718 matches!(self.unsafe_policy, UnsafePolicy::AllFunctionsSafe);
Brian Silverman4e662aa2022-05-11 23:10:19 -0700719 self.generate_subclass(name, &superclass, methods, generate_peer_constructor)
720 }
721 Api::ExternCppType {
722 details: ExternCppType { rust_path, .. },
723 ..
724 } => self.generate_extern_cpp_type(&name, rust_path, name.ns_segment_iter().count()),
725 Api::IgnoredItem {
726 err,
727 ctx: Some(ctx),
728 ..
729 } => Self::generate_error_entry(err, ctx),
730 Api::IgnoredItem { .. } | Api::SubclassTraitItem { .. } => RsCodegenResult::default(),
731 }
732 }
733
734 fn generate_subclass(
735 &self,
736 sub: SubclassName,
737 superclass: &QualifiedName,
738 methods: Option<&Vec<SuperclassMethod>>,
739 generate_peer_constructor: bool,
740 ) -> RsCodegenResult {
741 let super_name = superclass.get_final_item();
742 let super_path = superclass.to_type_path();
743 let super_cxxxbridge_id = superclass.get_final_ident();
744 let id = sub.id();
745 let holder = sub.holder();
746 let full_cpp = sub.cpp();
747 let cpp_path = full_cpp.to_type_path();
748 let cpp_id = full_cpp.get_final_ident();
749 let mut global_items = Vec::new();
750 global_items.push(parse_quote! {
751 pub use bindgen::root::#holder;
752 });
753 let relinquish_ownership_call = sub.cpp_remove_ownership();
754 let mut bindgen_mod_items = vec![
755 parse_quote! {
756 pub use cxxbridge::#cpp_id;
757 },
758 parse_quote! {
759 pub struct #holder(pub autocxx::subclass::CppSubclassRustPeerHolder<super::super::super::#id>);
760 },
761 parse_quote! {
762 impl autocxx::subclass::CppSubclassCppPeer for #cpp_id {
763 fn relinquish_ownership(&self) {
764 self.#relinquish_ownership_call();
765 }
766 }
767 },
768 ];
769 let mut extern_c_mod_items = vec![
770 self.generate_cxxbridge_type(&full_cpp, false, Vec::new()),
771 parse_quote! {
772 fn #relinquish_ownership_call(self: &#cpp_id);
773 },
774 ];
775 if let Some(methods) = methods {
776 let supers = SubclassName::get_supers_trait_name(superclass).to_type_path();
777 let methods_impls: Vec<ImplItem> = methods
778 .iter()
779 .filter(|m| !m.is_pure_virtual)
780 .map(|m| {
781 let cpp_super_method_name =
782 SubclassName::get_super_fn_name(&Namespace::new(), &m.name.to_string())
783 .get_final_ident();
784 let mut params = m.params.clone();
785 let ret = &m.ret_type.clone();
786 let (peer_fn, first_param) = match m.receiver_mutability {
787 ReceiverMutability::Const => ("peer", parse_quote!(&self)),
788 ReceiverMutability::Mutable => ("peer_mut", parse_quote!(&mut self)),
789 };
790 let peer_fn = make_ident(peer_fn);
791 *(params.iter_mut().next().unwrap()) = first_param;
792 let param_names = m.param_names.iter().skip(1);
793 let unsafe_token = m.requires_unsafe.wrapper_token();
794 parse_quote! {
795 #unsafe_token fn #cpp_super_method_name(#params) #ret {
796 use autocxx::subclass::CppSubclass;
797 self.#peer_fn().#cpp_super_method_name(#(#param_names),*)
798 }
799 }
800 })
801 .collect();
802 if !methods_impls.is_empty() {
803 bindgen_mod_items.push(parse_quote! {
804 #[allow(non_snake_case)]
805 impl #supers for super::super::super::#id {
806 #(#methods_impls)*
807 }
808 });
809 }
810 }
811 if generate_peer_constructor {
812 bindgen_mod_items.push(parse_quote! {
813 impl autocxx::subclass::CppPeerConstructor<#cpp_id> for super::super::super::#id {
814 fn make_peer(&mut self, peer_holder: autocxx::subclass::CppSubclassRustPeerHolder<Self>) -> cxx::UniquePtr<#cpp_path> {
815 use autocxx::moveit::EmplaceUnpinned;
816 cxx::UniquePtr::emplace(#cpp_id :: new(peer_holder))
817 }
818 }
819 })
820 };
821
822 // Once for each superclass, in future...
823 let as_id = make_ident(format!("As_{}", super_name));
824 extern_c_mod_items.push(parse_quote! {
825 fn #as_id(self: &#cpp_id) -> &#super_cxxxbridge_id;
826 });
827 let as_mut_id = make_ident(format!("As_{}_mut", super_name));
828 extern_c_mod_items.push(parse_quote! {
829 fn #as_mut_id(self: Pin<&mut #cpp_id>) -> Pin<&mut #super_cxxxbridge_id>;
830 });
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700831 let as_unique_ptr_id = make_ident(format!("{}_As_{}_UniquePtr", cpp_id, super_name));
832 extern_c_mod_items.push(parse_quote! {
833 fn #as_unique_ptr_id(u: UniquePtr<#cpp_id>) -> UniquePtr<#super_cxxxbridge_id>;
834 });
Brian Silverman4e662aa2022-05-11 23:10:19 -0700835 bindgen_mod_items.push(parse_quote! {
836 impl AsRef<#super_path> for super::super::super::#id {
837 fn as_ref(&self) -> &cxxbridge::#super_cxxxbridge_id {
838 use autocxx::subclass::CppSubclass;
839 self.peer().#as_id()
840 }
841 }
842 });
843 // TODO it would be nice to impl AsMut here but pin prevents us
844 bindgen_mod_items.push(parse_quote! {
845 impl super::super::super::#id {
846 pub fn pin_mut(&mut self) -> ::std::pin::Pin<&mut cxxbridge::#super_cxxxbridge_id> {
847 use autocxx::subclass::CppSubclass;
848 self.peer_mut().#as_mut_id()
849 }
850 }
851 });
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700852 let rs_as_unique_ptr_id = make_ident(format!("as_{}_unique_ptr", super_name));
853 bindgen_mod_items.push(parse_quote! {
854 impl super::super::super::#id {
855 pub fn #rs_as_unique_ptr_id(u: cxx::UniquePtr<#cpp_id>) -> cxx::UniquePtr<cxxbridge::#super_cxxxbridge_id> {
856 cxxbridge::#as_unique_ptr_id(u)
857 }
858 }
859 });
Brian Silverman4e662aa2022-05-11 23:10:19 -0700860 let remove_ownership = sub.remove_ownership();
861 global_items.push(parse_quote! {
862 #[allow(non_snake_case)]
863 pub fn #remove_ownership(me: Box<#holder>) -> Box<#holder> {
864 Box::new(#holder(me.0.relinquish_ownership()))
865 }
866 });
867 RsCodegenResult {
868 extern_c_mod_items,
869 // For now we just assume we can't keep subclasses in vectors, but we can put them in
870 // smart pointers.
871 // That's the reason for the 'false' and 'true'
872 bridge_items: create_impl_items(&cpp_id, false, true, self.config),
873 bindgen_mod_items,
874 materializations: vec![Use::Custom(Box::new(parse_quote! {
875 pub use cxxbridge::#cpp_id;
876 }))],
877 global_items,
878 extern_rust_mod_items: vec![
879 parse_quote! {
880 pub type #holder;
881 },
882 parse_quote! {
883 fn #remove_ownership(me: Box<#holder>) -> Box<#holder>;
884 },
885 ],
886 ..Default::default()
887 }
888 }
889
890 fn generate_subclass_fn(
891 api_name: Ident,
892 details: RustSubclassFnDetails,
893 subclass: SubclassName,
894 ) -> RsCodegenResult {
895 let params = details.params;
896 let ret = details.ret;
897 let unsafe_token = details.requires_unsafe.wrapper_token();
898 let global_def = quote! { #unsafe_token fn #api_name(#params) #ret };
899 let params = unqualify_params(params);
900 let ret = unqualify_ret_type(ret);
901 let method_name = details.method_name;
902 let cxxbridge_decl: ForeignItemFn =
903 parse_quote! { #unsafe_token fn #api_name(#params) #ret; };
904 let args: Punctuated<Expr, Comma> =
905 Self::args_from_sig(&cxxbridge_decl.sig.inputs).collect();
906 let superclass_id = details.superclass.get_final_ident();
907 let methods_trait = SubclassName::get_methods_trait_name(&details.superclass);
908 let methods_trait = methods_trait.to_type_path();
909 let (deref_ty, deref_call, borrow, mut_token) = match details.receiver_mutability {
910 ReceiverMutability::Const => ("Deref", "deref", "try_borrow", None),
911 ReceiverMutability::Mutable => (
912 "DerefMut",
913 "deref_mut",
914 "try_borrow_mut",
915 Some(syn::token::Mut(Span::call_site())),
916 ),
917 };
918 let deref_ty = make_ident(deref_ty);
919 let deref_call = make_ident(deref_call);
920 let borrow = make_ident(borrow);
921 let destroy_panic_msg = format!("Rust subclass API (method {} of subclass {} of superclass {}) called after subclass destroyed", method_name, subclass.0.name, superclass_id);
922 let reentrancy_panic_msg = format!("Rust subclass API (method {} of subclass {} of superclass {}) called whilst subclass already borrowed - likely a re-entrant call", method_name, subclass.0.name, superclass_id);
923 RsCodegenResult {
924 global_items: vec![parse_quote! {
925 #global_def {
926 let rc = me.0
927 .get()
928 .expect(#destroy_panic_msg);
929 let #mut_token b = rc
930 .as_ref()
931 .#borrow()
932 .expect(#reentrancy_panic_msg);
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700933 let r = ::std::ops::#deref_ty::#deref_call(& #mut_token b);
Brian Silverman4e662aa2022-05-11 23:10:19 -0700934 #methods_trait :: #method_name
935 (r,
936 #args)
937 }
938 }],
939 extern_rust_mod_items: vec![ForeignItem::Fn(cxxbridge_decl)],
940 ..Default::default()
941 }
942 }
943
944 fn args_from_sig(params: &Punctuated<FnArg, Comma>) -> impl Iterator<Item = Expr> + '_ {
945 params.iter().skip(1).filter_map(|fnarg| match fnarg {
946 syn::FnArg::Receiver(_) => None,
947 syn::FnArg::Typed(fnarg) => match &*fnarg.pat {
948 syn::Pat::Ident(id) => Some(Self::id_to_expr(&id.ident)),
949 _ => None,
950 },
951 })
952 }
953
954 #[allow(clippy::too_many_arguments)] // currently the least unclear way
955 fn generate_type<F>(
956 &self,
957 name: &QualifiedName,
958 id: Ident,
959 type_kind: TypeKind,
960 movable: bool,
961 destroyable: bool,
962 item_creator: F,
963 associated_methods: &HashMap<QualifiedName, Vec<SuperclassMethod>>,
964 layout: Option<Layout>,
965 is_generic: bool,
966 ) -> RsCodegenResult
967 where
968 F: FnOnce() -> Option<(Item, Vec<Attribute>)>,
969 {
970 let mut bindgen_mod_items = Vec::new();
971 let mut materializations = vec![Use::UsedFromBindgen];
972 Self::add_superclass_stuff_to_type(
973 name,
974 &mut bindgen_mod_items,
975 &mut materializations,
976 associated_methods.get(name),
977 );
978 let orig_item = item_creator();
979 let doc_attrs = orig_item
980 .as_ref()
981 .map(|maybe_item| maybe_item.1.clone())
982 .unwrap_or_default();
983 // We have a choice here to either:
984 // a) tell cxx to generate an opaque type using 'type A;'
985 // b) generate a concrete type definition, e.g. by using bindgen's
986 // or doing our own, and then telling cxx 'type A = bindgen::A';'
987 match type_kind {
988 TypeKind::Pod | TypeKind::NonPod => {
989 // Feed cxx "type T = root::bindgen::T"
990 // For non-POD types, there might be the option of simply giving
991 // cxx a "type T;" as we do for abstract types below. There's
992 // two reasons we don't:
993 // a) we want to specify size and alignment for the sake of
994 // moveit;
995 // b) for nested types such as 'A::B', there is no combination
996 // of cxx-acceptable attributes which will inform cxx that
997 // A is a class rather than a namespace.
998 let mut item = orig_item
999 .expect("Instantiable types must provide instance")
1000 .0;
1001 if matches!(type_kind, TypeKind::NonPod) {
1002 if let Item::Struct(ref mut s) = item {
1003 // Retain generics and doc attrs.
1004 make_non_pod(s, layout);
1005 } else {
1006 // enum
1007 item = Item::Struct(new_non_pod_struct(id.clone()));
1008 }
1009 }
1010 bindgen_mod_items.push(item);
1011
1012 if is_generic {
1013 // Still generate the type as emitted by bindgen,
1014 // but don't attempt to tell cxx about it
1015 RsCodegenResult {
1016 bindgen_mod_items,
1017 materializations,
1018 ..Default::default()
1019 }
1020 } else {
1021 RsCodegenResult {
1022 global_items: self.generate_extern_type_impl(type_kind, name),
1023 bridge_items: create_impl_items(&id, movable, destroyable, self.config),
1024 extern_c_mod_items: vec![
1025 self.generate_cxxbridge_type(name, true, doc_attrs)
1026 ],
1027 bindgen_mod_items,
1028 materializations,
1029 ..Default::default()
1030 }
1031 }
1032 }
1033 TypeKind::Abstract => {
1034 if is_generic {
1035 RsCodegenResult::default()
1036 } else {
1037 // Feed cxx "type T;"
1038 // We MUST do this because otherwise cxx assumes this can be
1039 // instantiated using UniquePtr etc.
1040 bindgen_mod_items.push(Item::Use(parse_quote! { pub use cxxbridge::#id; }));
1041 RsCodegenResult {
1042 extern_c_mod_items: vec![
1043 self.generate_cxxbridge_type(name, false, doc_attrs)
1044 ],
1045 bindgen_mod_items,
1046 materializations,
1047 ..Default::default()
1048 }
1049 }
1050 }
1051 }
1052 }
1053
1054 fn add_superclass_stuff_to_type(
1055 name: &QualifiedName,
1056 bindgen_mod_items: &mut Vec<Item>,
1057 materializations: &mut Vec<Use>,
1058 methods: Option<&Vec<SuperclassMethod>>,
1059 ) {
1060 if let Some(methods) = methods {
1061 let (supers, mains): (Vec<_>, Vec<_>) = methods
1062 .iter()
1063 .map(|method| {
1064 let id = &method.name;
1065 let super_id =
1066 SubclassName::get_super_fn_name(&Namespace::new(), &id.to_string())
1067 .get_final_ident();
1068 let param_names: Punctuated<Expr, Comma> =
1069 Self::args_from_sig(&method.params).collect();
1070 let mut params = method.params.clone();
1071 *(params.iter_mut().next().unwrap()) = match method.receiver_mutability {
1072 ReceiverMutability::Const => parse_quote!(&self),
1073 ReceiverMutability::Mutable => parse_quote!(&mut self),
1074 };
1075 let ret_type = &method.ret_type;
1076 let unsafe_token = method.requires_unsafe.wrapper_token();
1077 if method.is_pure_virtual {
1078 (
1079 None,
1080 parse_quote!(
1081 #unsafe_token fn #id(#params) #ret_type;
1082 ),
1083 )
1084 } else {
1085 let a: Option<TraitItem> = Some(parse_quote!(
1086 #unsafe_token fn #super_id(#params) #ret_type;
1087 ));
1088 let b: TraitItem = parse_quote!(
1089 #unsafe_token fn #id(#params) #ret_type {
1090 self.#super_id(#param_names)
1091 }
1092 );
1093 (a, b)
1094 }
1095 })
1096 .unzip();
1097 let supers: Vec<_> = supers.into_iter().flatten().collect();
1098 let supers_name = SubclassName::get_supers_trait_name(name).get_final_ident();
1099 let methods_name = SubclassName::get_methods_trait_name(name).get_final_ident();
1100 if !supers.is_empty() {
1101 bindgen_mod_items.push(parse_quote! {
1102 #[allow(non_snake_case)]
1103 pub trait #supers_name {
1104 #(#supers)*
1105 }
1106 });
1107 bindgen_mod_items.push(parse_quote! {
1108 #[allow(non_snake_case)]
1109 pub trait #methods_name : #supers_name {
1110 #(#mains)*
1111 }
1112 });
1113 materializations.push(Use::SpecificNameFromBindgen(supers_name));
1114 } else {
1115 bindgen_mod_items.push(parse_quote! {
1116 #[allow(non_snake_case)]
1117 pub trait #methods_name {
1118 #(#mains)*
1119 }
1120 });
1121 }
1122 materializations.push(Use::SpecificNameFromBindgen(methods_name));
1123 }
1124 }
1125
1126 fn generate_extern_cpp_type(
1127 &self,
1128 name: &QualifiedName,
1129 rust_path: TypePath,
1130 ns_depth: usize,
1131 ) -> RsCodegenResult {
Brian Silvermanf3ec38b2022-07-06 20:43:36 -07001132 let id = name.type_path_from_root();
Brian Silverman4e662aa2022-05-11 23:10:19 -07001133 let super_duper = std::iter::repeat(make_ident("super"));
1134 let supers = super_duper.take(ns_depth + 2);
1135 let use_statement = parse_quote! {
1136 pub use #(#supers)::* :: #id;
1137 };
1138 RsCodegenResult {
1139 bindgen_mod_items: vec![use_statement],
1140 extern_c_mod_items: vec![self.generate_cxxbridge_type(name, true, Vec::new())],
1141 materializations: vec![Use::Custom(Box::new(parse_quote! { pub use #rust_path; }))],
1142 ..Default::default()
1143 }
1144 }
1145
1146 /// Generates something in the output mod that will carry a docstring
1147 /// explaining why a given type or function couldn't have bindings
1148 /// generated.
1149 fn generate_error_entry(err: ConvertError, ctx: ErrorContext) -> RsCodegenResult {
1150 let err = format!("autocxx bindings couldn't be generated: {}", err);
1151 let (impl_entry, bindgen_mod_item, materialization) = match ctx.into_type() {
1152 ErrorContextType::Item(id) => (
1153 // Populate within bindgen mod because impl blocks may attach.
1154 None,
1155 Some(parse_quote! {
1156 #[doc = #err]
1157 pub struct #id;
1158 }),
1159 Some(Use::SpecificNameFromBindgen(id)),
1160 ),
1161 ErrorContextType::SanitizedItem(id) => (
1162 // Guaranteed to be no impl blocks - populate directly in output mod.
1163 None,
1164 None,
1165 Some(Use::Custom(Box::new(parse_quote! {
1166 #[doc = #err]
1167 pub struct #id;
1168 }))),
1169 ),
1170 ErrorContextType::Method { self_ty, method } => (
1171 Some(Box::new(ImplBlockDetails {
1172 item: parse_quote! {
1173 #[doc = #err]
1174 fn #method(_uhoh: autocxx::BindingGenerationFailure) {
1175 }
1176 },
Brian Silvermanf3ec38b2022-07-06 20:43:36 -07001177 ty: ImplBlockKey {
1178 ty: parse_quote! { #self_ty },
1179 lifetime: None,
1180 },
Brian Silverman4e662aa2022-05-11 23:10:19 -07001181 })),
1182 None,
1183 None,
1184 ),
1185 };
1186 RsCodegenResult {
1187 impl_entry,
1188 bindgen_mod_items: bindgen_mod_item.into_iter().collect(),
1189 materializations: materialization.into_iter().collect(),
1190 ..Default::default()
1191 }
1192 }
1193
1194 fn generate_cxx_use_stmt(name: &QualifiedName, alias: Option<&Ident>) -> Item {
1195 let segs = Self::find_output_mod_root(name.get_namespace())
1196 .chain(std::iter::once(make_ident("cxxbridge")))
1197 .chain(std::iter::once(name.get_final_ident()));
1198 Item::Use(match alias {
1199 None => parse_quote! {
1200 pub use #(#segs)::*;
1201 },
1202 Some(alias) => parse_quote! {
1203 pub use #(#segs)::* as #alias;
1204 },
1205 })
1206 }
1207
1208 fn generate_bindgen_use_stmt(name: &QualifiedName) -> Item {
1209 let segs =
1210 Self::find_output_mod_root(name.get_namespace()).chain(name.get_bindgen_path_idents());
1211 Item::Use(parse_quote! {
1212 pub use #(#segs)::*;
1213 })
1214 }
1215
1216 fn generate_extern_type_impl(&self, type_kind: TypeKind, tyname: &QualifiedName) -> Vec<Item> {
1217 let tynamestring = namespaced_name_using_original_name_map(tyname, &self.original_name_map);
1218 let fulltypath = tyname.get_bindgen_path_idents();
1219 let kind_item = match type_kind {
1220 TypeKind::Pod => "Trivial",
1221 _ => "Opaque",
1222 };
1223 let kind_item = make_ident(kind_item);
1224 vec![Item::Impl(parse_quote! {
1225 unsafe impl cxx::ExternType for #(#fulltypath)::* {
1226 type Id = cxx::type_id!(#tynamestring);
1227 type Kind = cxx::kind::#kind_item;
1228 }
1229 })]
1230 }
1231
1232 fn generate_cxxbridge_type(
1233 &self,
1234 name: &QualifiedName,
1235 references_bindgen: bool,
1236 doc_attrs: Vec<Attribute>,
1237 ) -> ForeignItem {
1238 let ns = name.get_namespace();
1239 let id = name.get_final_ident();
1240 // The following lines actually Tell A Lie.
1241 // If we have a nested class, B::C, within namespace A,
1242 // we actually have to tell cxx that we have nested class C
1243 // within namespace A.
1244 let mut ns_components: Vec<_> = ns.iter().cloned().collect();
1245 let mut cxx_name = None;
1246 if let Some(cpp_name) = self.original_name_map.get(name) {
1247 let cpp_name = QualifiedName::new_from_cpp_name(cpp_name);
1248 cxx_name = Some(cpp_name.get_final_item().to_string());
1249 ns_components.extend(cpp_name.ns_segment_iter().cloned());
1250 };
1251
1252 let mut for_extern_c_ts = if !ns_components.is_empty() {
1253 let ns_string = ns_components.join("::");
1254 quote! {
1255 #[namespace = #ns_string]
1256 }
1257 } else {
1258 TokenStream::new()
1259 };
1260
1261 if let Some(n) = cxx_name {
1262 for_extern_c_ts.extend(quote! {
1263 #[cxx_name = #n]
1264 });
1265 }
1266
1267 for_extern_c_ts.extend(quote! {
1268 #(#doc_attrs)*
1269 });
1270
1271 if references_bindgen {
1272 for_extern_c_ts.extend(quote! {
1273 type #id = super::bindgen::root::
1274 });
1275 for_extern_c_ts.extend(ns.iter().map(make_ident).map(|id| {
1276 quote! {
1277 #id::
1278 }
1279 }));
1280 for_extern_c_ts.extend(quote! {
1281 #id;
1282 });
1283 } else {
1284 for_extern_c_ts.extend(quote! {
1285 type #id;
1286 });
1287 }
1288 ForeignItem::Verbatim(for_extern_c_ts)
1289 }
1290
1291 fn find_output_mod_root(ns: &Namespace) -> impl Iterator<Item = Ident> {
1292 std::iter::repeat(make_ident("super")).take(ns.depth())
1293 }
1294}
1295
1296fn find_trivially_constructed_subclasses(apis: &ApiVec<FnPhase>) -> HashSet<QualifiedName> {
1297 let (simple_constructors, complex_constructors): (Vec<_>, Vec<_>) = apis
1298 .iter()
1299 .filter_map(|api| match api {
1300 Api::Function { fun, .. } => match &fun.provenance {
1301 Provenance::SynthesizedSubclassConstructor(details) => {
1302 Some((&details.subclass.0.name, details.is_trivial))
1303 }
1304 _ => None,
1305 },
1306 _ => None,
1307 })
1308 .partition(|(_, trivial)| *trivial);
1309 let simple_constructors: HashSet<_> =
1310 simple_constructors.into_iter().map(|(qn, _)| qn).collect();
1311 let complex_constructors: HashSet<_> =
1312 complex_constructors.into_iter().map(|(qn, _)| qn).collect();
1313 (&simple_constructors - &complex_constructors)
1314 .into_iter()
1315 .cloned()
1316 .collect()
1317}
1318
1319fn find_non_pod_types(apis: &ApiVec<FnPhase>) -> HashSet<QualifiedName> {
1320 apis.iter()
1321 .filter_map(|api| match api {
1322 Api::Struct {
1323 name,
1324 analysis:
1325 PodAndDepAnalysis {
1326 pod:
1327 PodAnalysis {
1328 kind: TypeKind::NonPod,
1329 ..
1330 },
1331 ..
1332 },
1333 ..
1334 } => Some(name.name.clone()),
1335 _ => None,
1336 })
1337 .collect()
1338}
1339
1340impl HasNs for (QualifiedName, RsCodegenResult) {
1341 fn get_namespace(&self) -> &Namespace {
1342 self.0.get_namespace()
1343 }
1344}
1345
1346impl<T: AnalysisPhase> HasNs for Api<T> {
1347 fn get_namespace(&self) -> &Namespace {
1348 self.name().get_namespace()
1349 }
1350}
1351
1352/// Snippets of code generated from a particular API.
1353/// These are then concatenated together into the final generated code.
1354#[derive(Default)]
1355struct RsCodegenResult {
1356 extern_c_mod_items: Vec<ForeignItem>,
1357 extern_rust_mod_items: Vec<ForeignItem>,
1358 bridge_items: Vec<Item>,
1359 global_items: Vec<Item>,
1360 bindgen_mod_items: Vec<Item>,
1361 impl_entry: Option<Box<ImplBlockDetails>>,
1362 trait_impl_entry: Option<Box<TraitImplBlockDetails>>,
1363 materializations: Vec<Use>,
1364}
1365
1366/// An [`Item`] that always needs to be in an unsafe block.
1367#[derive(Clone)]
1368enum MaybeUnsafeStmt {
1369 // This could almost be a syn::Stmt, but that doesn't quite work
1370 // because the last stmt in a function is actually an expression
1371 // thus lacking a semicolon.
1372 Normal(TokenStream),
1373 NeedsUnsafe(TokenStream),
1374 Binary {
1375 in_safe_context: TokenStream,
1376 in_unsafe_context: TokenStream,
1377 },
1378}
1379
1380impl MaybeUnsafeStmt {
1381 fn new(stmt: TokenStream) -> Self {
1382 Self::Normal(stmt)
1383 }
1384
1385 fn needs_unsafe(stmt: TokenStream) -> Self {
1386 Self::NeedsUnsafe(stmt)
1387 }
1388
1389 fn maybe_unsafe(stmt: TokenStream, needs_unsafe: bool) -> Self {
1390 if needs_unsafe {
1391 Self::NeedsUnsafe(stmt)
1392 } else {
1393 Self::Normal(stmt)
1394 }
1395 }
1396
1397 fn binary(in_safe_context: TokenStream, in_unsafe_context: TokenStream) -> Self {
1398 Self::Binary {
1399 in_safe_context,
1400 in_unsafe_context,
1401 }
1402 }
1403}
1404
1405fn maybe_unsafes_to_tokens(
1406 items: Vec<MaybeUnsafeStmt>,
1407 context_is_already_unsafe: bool,
1408) -> TokenStream {
1409 if context_is_already_unsafe {
1410 let items = items.into_iter().map(|item| match item {
1411 MaybeUnsafeStmt::Normal(stmt)
1412 | MaybeUnsafeStmt::NeedsUnsafe(stmt)
1413 | MaybeUnsafeStmt::Binary {
1414 in_unsafe_context: stmt,
1415 ..
1416 } => stmt,
1417 });
1418 quote! {
1419 #(#items)*
1420 }
1421 } else {
1422 let mut currently_unsafe_list = None;
1423 let mut output = Vec::new();
1424 for item in items {
1425 match item {
1426 MaybeUnsafeStmt::NeedsUnsafe(stmt) => {
1427 if currently_unsafe_list.is_none() {
1428 currently_unsafe_list = Some(Vec::new());
1429 }
1430 currently_unsafe_list.as_mut().unwrap().push(stmt);
1431 }
1432 MaybeUnsafeStmt::Normal(stmt)
1433 | MaybeUnsafeStmt::Binary {
1434 in_safe_context: stmt,
1435 ..
1436 } => {
1437 if let Some(currently_unsafe_list) = currently_unsafe_list.take() {
1438 output.push(quote! {
1439 unsafe {
1440 #(#currently_unsafe_list)*
1441 }
1442 })
1443 }
1444 output.push(stmt);
1445 }
1446 }
1447 }
1448 if let Some(currently_unsafe_list) = currently_unsafe_list.take() {
1449 output.push(quote! {
1450 unsafe {
1451 #(#currently_unsafe_list)*
1452 }
1453 })
1454 }
1455 quote! {
1456 #(#output)*
1457 }
1458 }
1459}
1460
1461#[test]
1462fn test_maybe_unsafes_to_tokens() {
1463 let items = vec![
1464 MaybeUnsafeStmt::new(quote! { use A; }),
1465 MaybeUnsafeStmt::new(quote! { use B; }),
1466 MaybeUnsafeStmt::needs_unsafe(quote! { use C; }),
1467 MaybeUnsafeStmt::needs_unsafe(quote! { use D; }),
1468 MaybeUnsafeStmt::new(quote! { use E; }),
1469 MaybeUnsafeStmt::needs_unsafe(quote! { use F; }),
1470 ];
1471 assert_eq!(
1472 maybe_unsafes_to_tokens(items.clone(), false).to_string(),
1473 quote! {
1474 use A;
1475 use B;
1476 unsafe {
1477 use C;
1478 use D;
1479 }
1480 use E;
1481 unsafe {
1482 use F;
1483 }
1484 }
1485 .to_string()
1486 );
1487 assert_eq!(
1488 maybe_unsafes_to_tokens(items, true).to_string(),
1489 quote! {
1490 use A;
1491 use B;
1492 use C;
1493 use D;
1494 use E;
1495 use F;
1496 }
1497 .to_string()
1498 );
1499}