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