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