swift_bridge_ir/codegen/
generate_rust_tokens.rs

1//! More tests can be found in src/codegen/codegen_tests.rs and its submodules.
2
3use std::collections::HashMap;
4
5use proc_macro2::TokenStream;
6use quote::ToTokens;
7use quote::{quote, quote_spanned};
8
9use self::vec::vec_of_opaque_rust_type::generate_vec_of_opaque_rust_type_functions;
10use crate::bridge_module_attributes::CfgAttr;
11use crate::parse::{HostLang, SharedTypeDeclaration, TypeDeclaration};
12use crate::SwiftBridgeModule;
13
14mod shared_enum;
15mod shared_struct;
16mod vec;
17
18impl ToTokens for SwiftBridgeModule {
19    fn to_tokens(&self, tokens: &mut TokenStream) {
20        let mod_name = &self.name;
21        let vis = &self.vis;
22        let swift_bridge_path = &self.swift_bridge_path;
23
24        let mut extern_rust_fn_tokens = vec![];
25
26        let mut structs_for_swift_classes = vec![];
27
28        let mut shared_struct_definitions = vec![];
29        let mut shared_enum_definitions = vec![];
30        let mut custom_type_definitions: HashMap<String, TokenStream> = HashMap::new();
31        let mut impl_fn_tokens: HashMap<String, Vec<TokenStream>> = HashMap::new();
32        let mut callbacks_support = vec![];
33        let mut freestanding_rust_call_swift_fn_tokens = vec![];
34        let mut extern_swift_fn_tokens = vec![];
35
36        for func in &self.functions {
37            match func.host_lang {
38                HostLang::Rust => {
39                    extern_rust_fn_tokens.push(func.to_extern_c_function_tokens(
40                        &self.swift_bridge_path,
41                        &self.types,
42                        &mut custom_type_definitions,
43                    ));
44                }
45                HostLang::Swift => {
46                    let tokens = func
47                        .to_rust_fn_that_calls_a_swift_extern(&self.swift_bridge_path, &self.types);
48                    callbacks_support
49                        .push(func.callbacks_support(&self.swift_bridge_path, &self.types));
50
51                    if let Some(ty) = func.associated_type.as_ref() {
52                        match ty {
53                            TypeDeclaration::Shared(_) => {
54                                //
55
56                                todo!()
57                            }
58                            TypeDeclaration::Opaque(ty) => {
59                                impl_fn_tokens
60                                    .entry(ty.to_string())
61                                    .or_default()
62                                    .push(tokens);
63                            }
64                        };
65                    } else {
66                        freestanding_rust_call_swift_fn_tokens.push(tokens);
67                    }
68
69                    extern_swift_fn_tokens.push(func.to_extern_c_function_tokens(
70                        &self.swift_bridge_path,
71                        &self.types,
72                        &mut custom_type_definitions,
73                    ));
74                }
75            };
76        }
77
78        for ty in &self.types.types() {
79            match ty {
80                TypeDeclaration::Shared(SharedTypeDeclaration::Struct(shared_struct)) => {
81                    if let Some(definition) = self.generate_shared_struct_tokens(shared_struct) {
82                        shared_struct_definitions.push(definition);
83                    }
84                }
85                TypeDeclaration::Shared(SharedTypeDeclaration::Enum(shared_enum)) => {
86                    if let Some(definition) =
87                        self.generate_shared_enum_tokens(shared_enum, &self.types)
88                    {
89                        shared_enum_definitions.push(definition);
90                    }
91                }
92                TypeDeclaration::Opaque(ty) => {
93                    if ty.attributes.declare_generic {
94                        continue;
95                    }
96
97                    let link_name = ty.free_rust_opaque_type_ffi_name();
98                    let free_mem_func_name = ty.free_rust_opaque_type_ident();
99                    let this = &ty.ty;
100                    let ty_name = &ty.ty;
101
102                    match ty.host_lang {
103                        HostLang::Rust => {
104                            if ty.attributes.hashable {
105                                let export_name = format!("__swift_bridge__${}$_hash", ty_name);
106                                let function_name = syn::Ident::new(
107                                    &format!("__swift_bridge__{}__hash", ty_name),
108                                    ty.ty.span(),
109                                );
110                                let tokens = quote! {
111                                #[export_name = #export_name]
112                                pub extern "C" fn #function_name (
113                                    this: *const super::#ty_name,
114                                ) -> u64 {
115                                    use std::hash::{Hash, Hasher};
116                                    use std::collections::hash_map::DefaultHasher;
117                                    let mut s = DefaultHasher::new();
118                                    (unsafe {&*this}).hash(&mut s);
119                                    s.finish()
120                                }
121                                };
122                                extern_rust_fn_tokens.push(tokens);
123                            }
124                            if ty.attributes.equatable {
125                                let export_name =
126                                    format!("__swift_bridge__${}$_partial_eq", ty_name);
127                                let function_name = syn::Ident::new(
128                                    &format!("__swift_bridge__{}__partial_eq", ty_name),
129                                    ty.ty.span(),
130                                );
131                                let tokens = quote! {
132                                    #[export_name = #export_name]
133                                    pub extern "C" fn #function_name (
134                                        lhs: *const super::#ty_name,
135                                        rhs: *const super::#ty_name
136                                    ) -> bool {
137                                        unsafe { &*lhs == &*rhs }
138                                    }
139                                };
140                                extern_rust_fn_tokens.push(tokens);
141                            }
142                            if let Some(copy) = ty.attributes.copy {
143                                let size = copy.size_bytes;
144
145                                let generics = ty
146                                    .generics
147                                    .angle_bracketed_concrete_generics_tokens(&self.types);
148
149                                // We use a somewhat hacky approach to asserting that the size
150                                // is correct at compile time.
151                                // In the future we'd prefer something like
152                                //  `assert_eq!(std::mem::size_of::<super::#ty_name>(), #size);`
153                                // If compile time assertions are ever supported by Rust.
154                                // https://github.com/rust-lang/rfcs/issues/2790
155                                let assert_size = quote_spanned! {ty.ty.span()=>
156                                    const _: () = {
157                                        let _: [u8; std::mem::size_of::<super::#ty_name #generics>()] = [0; #size];
158                                        fn _assert_copy() {
159                                            #swift_bridge_path::copy_support::assert_copy::<super::#ty_name #generics>();
160                                        }
161                                    };
162                                };
163
164                                let copy_ty_name = ty.ffi_copy_repr_ident();
165                                let option_copy_ty_name = ty.ffi_option_copy_repr_ident();
166
167                                let copy_ty = quote! {
168                                    #[repr(C)]
169                                    #[doc(hidden)]
170                                    pub struct #copy_ty_name([u8; #size]);
171                                    impl #copy_ty_name {
172                                        #[inline(always)]
173                                        fn into_rust_repr(self) -> super:: #ty_name #generics {
174                                            unsafe { std::mem::transmute(self) }
175                                        }
176                                        #[inline(always)]
177                                        fn from_rust_repr(repr: super:: #ty_name #generics) -> Self {
178                                            unsafe { std::mem::transmute(repr) }
179                                        }
180                                    }
181
182                                    #[repr(C)]
183                                    #[doc(hidden)]
184                                    pub struct #option_copy_ty_name {
185                                        is_some: bool,
186                                        val: std::mem::MaybeUninit<#copy_ty_name>
187                                    }
188                                };
189
190                                extern_rust_fn_tokens.push(assert_size);
191                                extern_rust_fn_tokens.push(copy_ty);
192                            }
193
194                            if !ty.attributes.already_declared {
195                                if ty.attributes.copy.is_none() {
196                                    let generics = ty
197                                        .generics
198                                        .angle_bracketed_concrete_generics_tokens(&self.types);
199
200                                    let free = quote! {
201                                        #[export_name = #link_name]
202                                        pub extern "C" fn #free_mem_func_name (this: *mut super::#this #generics) {
203                                            let this = unsafe { Box::from_raw(this) };
204                                            drop(this);
205                                        }
206                                    };
207
208                                    extern_rust_fn_tokens.push(free);
209
210                                    // TODO: Support Vec<OpaqueCopyType>. Add codegen tests and then
211                                    //  make them pass.
212                                    // TODO: Support Vec<GenericOpaqueRustType
213                                    if ty.generics.len() == 0 {
214                                        let vec_functions =
215                                            generate_vec_of_opaque_rust_type_functions(ty_name);
216                                        extern_rust_fn_tokens.push(vec_functions);
217                                    }
218                                }
219                            }
220                        }
221                        HostLang::Swift => {
222                            let ty_name = &ty.ty;
223
224                            let impls = match impl_fn_tokens.get(&ty_name.to_string()) {
225                                Some(impls) if impls.len() > 0 => {
226                                    quote! {
227                                        impl #ty_name {
228                                            #(#impls)*
229                                        }
230                                    }
231                                }
232                                _ => {
233                                    quote! {}
234                                }
235                            };
236
237                            let struct_tokens = quote! {
238                                #[repr(C)]
239                                pub struct #ty_name(*mut std::ffi::c_void);
240
241                                #impls
242
243                                impl Drop for #ty_name {
244                                    fn drop (&mut self) {
245                                        unsafe { #free_mem_func_name(self.0) }
246                                    }
247                                }
248                            };
249                            structs_for_swift_classes.push(struct_tokens);
250
251                            let free = quote! {
252                                #[link_name = #link_name]
253                                fn #free_mem_func_name (this: *mut std::ffi::c_void);
254                            };
255                            extern_swift_fn_tokens.push(free);
256                        }
257                    };
258                }
259            }
260        }
261
262        let extern_swift_fn_tokens = if extern_swift_fn_tokens.len() > 0 {
263            generate_extern_c_block(extern_swift_fn_tokens)
264        } else {
265            quote! {}
266        };
267
268        let mut module_attributes = vec![];
269
270        for cfg in &self.cfg_attrs {
271            match cfg {
272                CfgAttr::Feature(feature_name) => {
273                    //
274                    module_attributes.push(quote! {
275                        #[cfg(feature = #feature_name)]
276                    });
277                }
278            };
279        }
280        let custom_type_definitions = custom_type_definitions.into_values();
281        let module_inner = quote! {
282            #(#shared_struct_definitions)*
283
284            #(#shared_enum_definitions)*
285
286            #(#custom_type_definitions)*
287
288            #(#extern_rust_fn_tokens)*
289
290            #(#freestanding_rust_call_swift_fn_tokens)*
291
292            #(#structs_for_swift_classes)*
293
294            #extern_swift_fn_tokens
295
296            #(#callbacks_support)*
297        };
298
299        let t = quote! {
300            #[allow(non_snake_case)]
301            #(#module_attributes)*
302            #vis mod #mod_name {
303                #module_inner
304            }
305        };
306        t.to_tokens(tokens);
307    }
308}
309
310/// Generate an `extern "C"` block such as:
311///
312/// ```no_run
313/// extern "C" {
314///     #[link_name = "some_swift_function_name"]
315///     fn __swift_bridge__some_swift_function_name();
316/// }
317/// ```
318///
319/// ## `improper_ctypes` lint suppression
320///
321/// We suppress the `improper_ctypes` lint with `#[allow(improper_ctypes)]`.
322///
323/// Given the following bridge module:
324///
325/// ```ignore
326/// #[swift_bridge::bridge]
327/// mod ffi {
328///     struct SomeStruct {
329///         string: String
330///     }
331///
332///     extern "Swift" {
333///         fn return_struct() -> SomeStruct;
334///     }
335/// }
336/// ```
337///
338/// We would generate the following struct FFI representation and `extern "C"` block:
339///
340/// ```no_run
341/// struct __swift_bridge__SomeStruct {
342///     string: *mut swift_bridge::string::RustString
343/// }
344///
345/// extern "C" {
346///     #[link_name = "__swift_bridge__$rust_calls_swift_struct_repr_struct_one_string_field"]
347///     fn __swift_bridge__return_struct() -> __swift_bridge__SomeStruct;
348/// }
349///
350/// # mod swift_bridge { pub mod string { pub struct RustString; }}
351/// ```
352///
353/// The `__swift_bridge__SomeStruct` holds a pointer to a `RustString`.
354///
355/// Since `RustString` is not FFI safe, and the Rust compiler cannot know that we only plan to use
356/// the pointer as an opaque pointer, the Rust compiler emits an `improper_ctypes` lint.
357///
358/// We silence this lint since we know that our usage is FFI safe.
359///
360/// The risk in this is that if in the future we accidentally pass a truly improper ctype over FFI
361/// this `#[allow(improper_ctypes)]` might prevent us from noticing.
362///
363/// Given that our codegen is heavily tested we are not currently concerned about this.
364///
365/// Should we become concerned about this in the future we could consider solutions such as:
366///
367/// - Generate an `__swift_bridge__SomeStruct_INNER_OPAQUE` that only held opaque pointers.
368///   We could then transmute the `__swift_bridge__SomeStruct` to/from this type when
369///   passing/receiving it across the FFI boundary.
370///     ```
371///     struct __swift_bridge__SomeStruct_INNER_OPAQUE {
372///         string: *mut std::ffi::c_void
373///     }
374///     ```
375///   - This would involve generating an extra type, but given that they would have the same layout
376///     and simply get transmuted into each other we could imagine that the optimizer would erase
377///     all overhead.
378fn generate_extern_c_block(extern_swift_fn_tokens: Vec<TokenStream>) -> TokenStream {
379    quote! {
380        #[allow(improper_ctypes)]
381        extern "C" {
382            #(#extern_swift_fn_tokens)*
383        }
384    }
385}
386
387#[cfg(test)]
388mod tests {
389    //! More tests can be found in src/codegen/codegen_tests.rs and its submodules.
390
391    use quote::quote;
392
393    use crate::parse::SwiftBridgeModuleAndErrors;
394    use crate::test_utils::{assert_tokens_contain, assert_tokens_eq};
395
396    use super::*;
397
398    /// Verify that we generate tokens for a freestanding Rust function with no arguments.
399    #[test]
400    fn freestanding_rust_function_no_args() {
401        let start = quote! {
402            mod foo {
403                extern "Rust" {
404                    fn some_function ();
405                }
406            }
407        };
408        let expected = quote! {
409            #[allow(non_snake_case)]
410            mod foo {
411                #[export_name = "__swift_bridge__$some_function"]
412                pub extern "C" fn __swift_bridge__some_function () {
413                    super::some_function()
414                }
415            }
416        };
417
418        assert_tokens_eq(&parse_ok(start).to_token_stream(), &expected);
419    }
420
421    /// Verify that we generate an extern function for a freestanding extern Swift function.
422    #[test]
423    fn freestanding_swift_function_no_args() {
424        let start = quote! {
425            mod foo {
426                extern "Swift" {
427                    fn some_function ();
428                }
429            }
430        };
431        let expected = quote! {
432            #[allow(non_snake_case)]
433            mod foo {
434                pub fn some_function() {
435                    unsafe { __swift_bridge__some_function() }
436                }
437
438                #[allow(improper_ctypes)]
439                extern "C" {
440                    #[link_name = "__swift_bridge__$some_function"]
441                    fn __swift_bridge__some_function ();
442                }
443            }
444        };
445
446        assert_tokens_eq(&parse_ok(start).to_token_stream(), &expected);
447    }
448
449    /// Verify that we generate functions for calling a freestanding extern Swift function with
450    /// one argument.
451    #[test]
452    fn freestanding_swift_function_one_arg() {
453        let start = quote! {
454            mod foo {
455                extern "Swift" {
456                    fn some_function (start: bool);
457                }
458            }
459        };
460        let expected = quote! {
461            #[allow(non_snake_case)]
462            mod foo {
463                pub fn some_function(start: bool) {
464                    unsafe { __swift_bridge__some_function(start) }
465                }
466
467                #[allow(improper_ctypes)]
468                extern "C" {
469                    #[link_name = "__swift_bridge__$some_function"]
470                    fn __swift_bridge__some_function (start: bool);
471                }
472            }
473        };
474
475        assert_tokens_eq(&parse_ok(start).to_token_stream(), &expected);
476    }
477
478    /// Verify that we generate tokens for a freestanding Rust function with no arguments.
479    #[test]
480    fn freestanding_rust_one_arg() {
481        let start = quote! {
482            mod foo {
483                extern "Rust" {
484                    fn some_function (bar: u8);
485                }
486            }
487        };
488        let expected = quote! {
489            #[allow(non_snake_case)]
490            mod foo {
491                #[export_name = "__swift_bridge__$some_function"]
492                pub extern "C" fn __swift_bridge__some_function (bar: u8) {
493                    super::some_function(bar)
494                }
495            }
496        };
497
498        assert_tokens_eq(&parse_ok(start).to_token_stream(), &expected);
499    }
500
501    /// Verify that we generate tokens for a freestanding Rust function with an argument of a
502    /// declared type.
503    #[test]
504    fn freestanding_rust_func_with_declared_swift_type_arg() {
505        let start = quote! {
506            mod foo {
507                extern "Rust" {
508                    fn some_function (bar: MyType);
509                }
510
511                extern "Swift" {
512                    type MyType;
513                }
514            }
515        };
516        let expected = quote! {
517            #[export_name = "__swift_bridge__$some_function"]
518            pub extern "C" fn __swift_bridge__some_function (
519                bar: MyType
520            ) {
521                super::some_function(
522                    bar
523                )
524            }
525        };
526
527        assert_tokens_contain(&parse_ok(start).to_token_stream(), &expected);
528    }
529
530    /// Verify that we generate tokens for a freestanding Rust function that returns an opaque
531    /// Swift type.
532    #[test]
533    fn freestanding_rust_func_returns_opaque_swift_type() {
534        let start = quote! {
535            mod foo {
536                extern "Rust" {
537                    fn some_function () -> MyType;
538                }
539
540                extern "Swift" {
541                    type MyType;
542                }
543            }
544        };
545        let expected = quote! {
546            #[export_name = "__swift_bridge__$some_function"]
547            pub extern "C" fn __swift_bridge__some_function () -> MyType {
548                super::some_function()
549            }
550        };
551
552        assert_tokens_contain(&parse_ok(start).to_token_stream(), &expected);
553    }
554
555    /// Verify that we generate tokens for a freestanding Rust function with a return type.
556    #[test]
557    fn freestanding_rust_with_built_in_return_type() {
558        let start = quote! {
559            mod foo {
560                extern "Rust" {
561                    fn some_function () -> u8;
562                }
563            }
564        };
565        let expected = quote! {
566            #[allow(non_snake_case)]
567            mod foo {
568                #[export_name = "__swift_bridge__$some_function"]
569                pub extern "C" fn __swift_bridge__some_function () -> u8 {
570                    super::some_function()
571                }
572            }
573        };
574
575        assert_tokens_eq(&parse_ok(start).to_token_stream(), &expected);
576    }
577
578    /// Verify that the `rust_name` attribute works on extern "Rust" functions.
579    #[test]
580    fn extern_rust_freestanding_function_rust_name() {
581        let start = quote! {
582            #[swift_bridge::bridge]
583            mod foo {
584                extern "Rust" {
585                    type Foo;
586
587                    #[swift_bridge(rust_name = "another_function")]
588                    fn some_function () -> Foo;
589                }
590            }
591        };
592        let expected_func = quote! {
593            #[export_name = "__swift_bridge__$some_function"]
594            pub extern "C" fn __swift_bridge__some_function () -> *mut super::Foo {
595                Box::into_raw(Box::new({
596                    let val: super::Foo = super::another_function();
597                    val
598                })) as *mut super::Foo
599            }
600        };
601
602        assert_tokens_contain(&parse_ok(start).to_token_stream(), &expected_func);
603    }
604
605    /// Verify that we respect the `return_into` attribute from within extern "Rust" blocks.
606    #[test]
607    fn extern_rust_return_into() {
608        let start = quote! {
609            #[swift_bridge::bridge]
610            mod foo {
611                extern "Rust" {
612                    type Foo;
613
614                    #[swift_bridge(return_into)]
615                    fn some_function () -> Foo;
616                }
617            }
618        };
619        let expected_func = quote! {
620            #[export_name = "__swift_bridge__$some_function"]
621            pub extern "C" fn __swift_bridge__some_function () -> *mut super::Foo {
622                Box::into_raw(Box::new({
623                    let val: super::Foo = super::some_function().into();
624                    val
625                })) as *mut super::Foo
626            }
627        };
628
629        assert_tokens_contain(&parse_ok(start).to_token_stream(), &expected_func);
630    }
631
632    /// Verify that an associated function we can return a reference to a declared type.
633    #[test]
634    fn associated_fn_return_reference_to_bridged_type() {
635        let start = quote! {
636            #[swift_bridge::bridge]
637            mod foo {
638                extern "Rust" {
639                    type Foo;
640                    fn some_function () -> &'static Foo;
641                }
642            }
643        };
644        let expected_func = quote! {
645            #[export_name = "__swift_bridge__$some_function"]
646            pub extern "C" fn __swift_bridge__some_function () -> *const super::Foo {
647                super::some_function() as *const super::Foo
648            }
649        };
650
651        assert_tokens_contain(&parse_ok(start).to_token_stream(), &expected_func);
652    }
653
654    /// Verify that a method can return a reference to a declared type.
655    #[test]
656    fn method_return_reference_to_bridged_type() {
657        let start = quote! {
658            #[swift_bridge::bridge]
659            mod foo {
660                extern "Rust" {
661                    type Foo;
662                    fn some_function (&mut self) -> &mut Foo;
663                }
664            }
665        };
666        let expected_func = quote! {
667            #[export_name = "__swift_bridge__$Foo$some_function"]
668            pub extern "C" fn __swift_bridge__Foo_some_function (
669                this: *mut super::Foo
670            ) -> *mut super::Foo {
671                (unsafe { &mut * this }).some_function() as *mut super::Foo
672            }
673        };
674
675        assert_tokens_contain(&parse_ok(start).to_token_stream(), &expected_func);
676    }
677
678    /// Verify that we generate a struct for an extern Swift type.
679    #[test]
680    fn generates_struct_for_swift_type() {
681        let start = quote! {
682            #[swift_bridge::bridge]
683            mod foo {
684                extern "Swift" {
685                    type Foo;
686                }
687            }
688        };
689        let expected = quote! {
690            #[repr(C)]
691            pub struct Foo(*mut std::ffi::c_void);
692
693            impl Drop for Foo {
694                fn drop (&mut self) {
695                    unsafe { __swift_bridge__Foo__free(self.0) }
696                }
697            }
698
699            #[allow(improper_ctypes)]
700            extern "C" {
701                #[link_name = "__swift_bridge__$Foo$_free"]
702                fn __swift_bridge__Foo__free (this: *mut std::ffi::c_void);
703            }
704        };
705
706        assert_tokens_contain(&parse_ok(start).to_token_stream(), &expected);
707    }
708
709    /// Verify that we generate tokens for a static method that's associated to a type.
710    #[test]
711    fn associated_rust_static_method_no_args() {
712        let start = quote! {
713            mod foo {
714                extern "Rust" {
715                    type SomeType;
716
717                    #[swift_bridge(associated_to = SomeType)]
718                    fn new () -> SomeType;
719                }
720            }
721        };
722        let expected = quote! {
723            #[export_name = "__swift_bridge__$SomeType$new"]
724            pub extern "C" fn __swift_bridge__SomeType_new () -> *mut super::SomeType {
725                Box::into_raw(Box::new({
726                    let val: super::SomeType = super::SomeType::new();
727                    val
728                })) as *mut super::SomeType
729            }
730        };
731
732        assert_to_extern_c_function_tokens(start, &expected);
733    }
734
735    /// Verify that we generate an associated function for a Swift class method.
736    #[test]
737    fn swift_class_method_no_args() {
738        let start = quote! {
739            #[swift_bridge::bridge]
740            mod foo {
741                extern "Swift" {
742                    type Foo;
743
744                    #[swift_bridge(associated_to = Foo)]
745                    fn new () -> Foo;
746                }
747            }
748        };
749        let expected = quote! {
750            #[repr(C)]
751            pub struct Foo(*mut std::ffi::c_void);
752
753            impl Foo {
754                pub fn new () -> Foo {
755                    unsafe{ __swift_bridge__Foo_new() }
756                }
757            }
758
759            impl Drop for Foo {
760                fn drop (&mut self) {
761                    unsafe { __swift_bridge__Foo__free(self.0) }
762                }
763            }
764
765            #[allow(improper_ctypes)]
766            extern "C" {
767                #[link_name = "__swift_bridge__$Foo$new"]
768                fn __swift_bridge__Foo_new() -> Foo;
769
770                #[link_name = "__swift_bridge__$Foo$_free"]
771                fn __swift_bridge__Foo__free (this: *mut std::ffi::c_void);
772            }
773        };
774
775        assert_tokens_contain(&parse_ok(start).to_token_stream(), &expected);
776    }
777
778    /// Verify that we generate tokens for a static method that have arguments.
779    #[test]
780    fn associated_rust_static_method_one_arg() {
781        let start = quote! {
782            mod foo {
783                extern "Rust" {
784                    type SomeType;
785
786                    #[swift_bridge(associated_to = SomeType)]
787                    fn new (foo: u8) -> u8;
788                }
789            }
790        };
791        let expected = quote! {
792            #[export_name = "__swift_bridge__$SomeType$new"]
793            pub extern "C" fn __swift_bridge__SomeType_new (foo: u8) -> u8 {
794                super::SomeType::new(foo)
795            }
796        };
797
798        assert_to_extern_c_function_tokens(start, &expected);
799    }
800
801    /// Verify that we generate tokens for a static method that does not have a return type.
802    #[test]
803    fn associated_static_method_no_return_type() {
804        let start = quote! {
805            mod foo {
806                extern "Rust" {
807                    type SomeType;
808
809                    #[swift_bridge(associated_to = SomeType)]
810                    fn new ();
811                }
812            }
813        };
814        let expected = quote! {
815            #[export_name = "__swift_bridge__$SomeType$new"]
816            pub extern "C" fn __swift_bridge__SomeType_new ()  {
817                 super::SomeType::new()
818            }
819        };
820
821        assert_to_extern_c_function_tokens(start, &expected);
822    }
823
824    /// Verify that we generate the tokens for exposing an associated method.
825    #[test]
826    fn associated_rust_method_no_args() {
827        let start = quote! {
828            mod foo {
829                extern "Rust" {
830                    type MyType;
831                    fn increment (&mut self);
832                }
833            }
834        };
835        let expected = quote! {
836            #[export_name = "__swift_bridge__$MyType$increment"]
837            pub extern "C" fn __swift_bridge__MyType_increment (
838                this: *mut super::MyType
839            ) {
840                (unsafe { &mut *this }).increment()
841            }
842        };
843
844        assert_to_extern_c_function_tokens(start, &expected);
845    }
846
847    /// Verify that we generate a method for a Swift class' instance method.
848    #[test]
849    fn swift_instance_methods() {
850        let start = quote! {
851            #[swift_bridge::bridge]
852            mod foo {
853                extern "Swift" {
854                    type Foo;
855
856                    fn notify (&self);
857                    fn message (self: &Foo);
858                    fn call (&mut self, volume: u8);
859                }
860            }
861        };
862        let expected = quote! {
863            #[repr(C)]
864            pub struct Foo(*mut std::ffi::c_void);
865
866            impl Foo {
867                pub fn notify (&self) {
868                    unsafe { __swift_bridge__Foo_notify(swift_bridge::PointerToSwiftType(self.0)) }
869                }
870
871                pub fn message (&self) {
872                    unsafe { __swift_bridge__Foo_message(swift_bridge::PointerToSwiftType(self.0)) }
873                }
874
875                pub fn call (&mut self, volume: u8) {
876                    unsafe { __swift_bridge__Foo_call(swift_bridge::PointerToSwiftType(self.0), volume) }
877                }
878            }
879
880            impl Drop for Foo {
881                fn drop (&mut self) {
882                    unsafe { __swift_bridge__Foo__free(self.0) }
883                }
884            }
885
886            #[allow(improper_ctypes)]
887            extern "C" {
888                #[link_name = "__swift_bridge__$Foo$notify"]
889                fn __swift_bridge__Foo_notify(this: swift_bridge::PointerToSwiftType);
890
891                #[link_name = "__swift_bridge__$Foo$message"]
892                fn __swift_bridge__Foo_message(this: swift_bridge::PointerToSwiftType);
893
894                #[link_name = "__swift_bridge__$Foo$call"]
895                fn __swift_bridge__Foo_call(this: swift_bridge::PointerToSwiftType, volume: u8);
896
897                #[link_name = "__swift_bridge__$Foo$_free"]
898                fn __swift_bridge__Foo__free (this: *mut std::ffi::c_void);
899            }
900        };
901
902        assert_tokens_contain(&parse_ok(start).to_token_stream(), &expected);
903    }
904
905    /// Verify that we generate the tokens for exposing an associated method.
906    #[test]
907    fn rust_associated_method_one_args() {
908        let start = quote! {
909            mod foo {
910                extern "Rust" {
911                    type SomeType;
912                    fn message (&self, val: u8);
913                }
914            }
915        };
916        let expected = quote! {
917            #[export_name = "__swift_bridge__$SomeType$message"]
918            pub extern "C" fn __swift_bridge__SomeType_message (
919                this: *mut super::SomeType,
920                val: u8
921            ) {
922                (unsafe { &*this }).message(val)
923            }
924        };
925
926        assert_to_extern_c_function_tokens(start, &expected);
927    }
928
929    /// Verify that extern "Rust" functions we can accept and return void pointers.
930    #[test]
931    fn extern_rust_void_pointers() {
932        let start = quote! {
933            mod foo {
934                extern "Rust" {
935                    fn void_pointers (arg1: *const c_void, arg2: *mut c_void) -> *const c_void;
936                }
937            }
938        };
939        let expected = quote! {
940            #[export_name = "__swift_bridge__$void_pointers"]
941            pub extern "C" fn __swift_bridge__void_pointers (
942                arg1: *const super::c_void,
943                arg2: *mut super::c_void
944            ) -> *const super::c_void {
945                super::void_pointers(arg1, arg2)
946            }
947        };
948
949        assert_to_extern_c_function_tokens(start, &expected);
950    }
951
952    /// Verify that extern "Rust" functions we can accept and return pointers to built in types.
953    #[test]
954    fn extern_swift_built_in_pointers() {
955        let start = quote! {
956            mod foo {
957                extern "Swift" {
958                    fn built_in_pointers (arg1: *const u8, arg2: *mut i16) -> *const u32;
959                }
960            }
961        };
962        let expected = quote! {
963            pub fn built_in_pointers (arg1: *const u8, arg2: *mut i16) -> *const u32 {
964                unsafe { __swift_bridge__built_in_pointers(arg1, arg2) }
965            }
966
967            #[allow(improper_ctypes)]
968            extern "C" {
969                #[link_name = "__swift_bridge__$built_in_pointers"]
970                fn __swift_bridge__built_in_pointers (
971                    arg1: *const u8,
972                    arg2: *mut i16
973                ) -> *const u32;
974            }
975        };
976
977        assert_tokens_contain(&parse_ok(start).to_token_stream(), &expected);
978    }
979
980    /// Verify that extern "Swift" functions we can accept and return void pointers.
981    #[test]
982    fn extern_swift_void_pointers() {
983        let start = quote! {
984            mod foo {
985                extern "Swift" {
986                    fn void_pointers (arg1: *const c_void, arg2: *mut c_void) -> *const c_void;
987                }
988            }
989        };
990        let expected = quote! {
991            pub fn void_pointers (arg1: *const super::c_void, arg2: *mut super::c_void) -> *const super::c_void {
992                unsafe { __swift_bridge__void_pointers(arg1, arg2) }
993            }
994
995            #[allow(improper_ctypes)]
996            extern "C" {
997                #[link_name = "__swift_bridge__$void_pointers"]
998                fn __swift_bridge__void_pointers (
999                    arg1: *const super::c_void,
1000                    arg2: *mut super::c_void
1001                ) -> *const super::c_void;
1002            }
1003        };
1004
1005        assert_tokens_contain(&parse_ok(start).to_token_stream(), &expected);
1006    }
1007
1008    /// Verify that we can take an owned self in a method.
1009    #[test]
1010    fn associated_method_drops_owned_self() {
1011        let start = quote! {
1012            mod foo {
1013                extern "Rust" {
1014                    type SomeType;
1015                    fn consume (self);
1016                }
1017            }
1018        };
1019        let expected = quote! {
1020            #[export_name = "__swift_bridge__$SomeType$consume"]
1021            pub extern "C" fn __swift_bridge__SomeType_consume (
1022                this: *mut super::SomeType
1023            ) {
1024                (* unsafe { Box::from_raw(this) }).consume()
1025            }
1026        };
1027
1028        assert_to_extern_c_function_tokens(start, &expected);
1029    }
1030
1031    fn parse_ok(tokens: TokenStream) -> SwiftBridgeModule {
1032        let module_and_errors: SwiftBridgeModuleAndErrors = syn::parse2(tokens).unwrap();
1033        module_and_errors.module
1034    }
1035
1036    fn assert_to_extern_c_function_tokens(module: TokenStream, expected_fn: &TokenStream) {
1037        let module = parse_ok(module);
1038        let function = &module.functions[0];
1039
1040        assert_tokens_eq(
1041            &function.to_extern_c_function_tokens(
1042                &module.swift_bridge_path,
1043                &module.types,
1044                &mut HashMap::new(),
1045            ),
1046            &expected_fn,
1047        );
1048    }
1049
1050    /// Verify that we apply the module's visibility to the output.
1051    #[test]
1052    fn module_visibility() {
1053        let start = quote! {
1054            pub(super) mod foo {
1055            }
1056        };
1057        let expected = quote! {
1058            #[allow(non_snake_case)]
1059            pub(super) mod foo {
1060            }
1061        };
1062
1063        assert_tokens_eq(&parse_ok(start).to_token_stream(), &expected);
1064    }
1065}