@@ -7,7 +7,14 @@ use syn::File;
77fn bridge ( cxx_bridge : TokenStream ) -> String {
88 let module = syn:: parse2 :: < Module > ( cxx_bridge) . unwrap ( ) ;
99 let tokens = expand:: bridge ( module) . unwrap ( ) ;
10- let file = syn:: parse2 :: < File > ( tokens) . unwrap ( ) ;
10+ let file = match syn:: parse2 :: < File > ( tokens. clone ( ) ) {
11+ Ok ( file) => file,
12+ Err ( err) => {
13+ eprintln ! ( "The code below is syntactically invalid: {err}:" ) ;
14+ eprintln ! ( "{tokens}" ) ;
15+ panic ! ( "`expand::bridge` should generate syntactically valid code" ) ;
16+ }
17+ } ;
1118 let pretty = prettyplease:: unparse ( & file) ;
1219 eprintln ! ( "{0:/<80}\n {pretty}{0:/<80}" , "" ) ;
1320 pretty
@@ -84,3 +91,68 @@ fn test_vec_string() {
8491 assert ! ( rs. contains( "v: &::cxx::private::RustVec<::cxx::alloc::string::String>" ) ) ;
8592 assert ! ( rs. contains( "fn __foo(v: &::cxx::alloc::vec::Vec<::cxx::alloc::string::String>)" ) ) ;
8693}
94+
95+ #[ test]
96+ fn test_mangling_covers_cpp_namespace_of_vec_elements ( ) {
97+ let rs = bridge ( quote ! {
98+ mod ffi {
99+ #[ namespace = "test_namespace" ]
100+ struct Context { x: i32 }
101+ impl Vec <Context > { }
102+ }
103+ } ) ;
104+
105+ // Mangling must include Context's C++ namespace to avoid colliding the
106+ // symbol names for two identically named structs in different namespaces.
107+ assert ! ( rs. contains( "export_name = \" cxxbridge1$rust_vec$test_namespace$Context$set_len\" " ) ) ;
108+ }
109+
110+ #[ test]
111+ fn test_struct_with_lifetime ( ) {
112+ let rs = bridge ( quote ! {
113+ mod ffi {
114+ struct StructWithLifetime <' a> {
115+ s: & ' a str ,
116+ }
117+ extern "Rust" {
118+ fn f( _: UniquePtr <StructWithLifetime <>>) ;
119+ }
120+ }
121+ } ) ;
122+
123+ // Regression test for <https://github.com/dtolnay/cxx/pull/1658#discussion_r2529463814>
124+ // which generated this invalid code:
125+ //
126+ // impl<'a> ::cxx::memory::UniquePtrTarget for StructWithLifetime < > < 'a > {
127+ //
128+ // Invalid syntax in the output code would already have caused the test
129+ // helper `bridge` to panic above. But for completeness this assertion
130+ // verifies the intended code has been generated.
131+ assert ! ( rs. contains( "impl<'a> ::cxx::memory::UniquePtrTarget for StructWithLifetime<'a> {" ) ) ;
132+
133+ // Assertions for other places that refer to `StructWithLifetime`.
134+ assert ! ( rs. contains( "pub struct StructWithLifetime<'a> {" ) ) ;
135+ assert ! ( rs. contains( "cast::<StructWithLifetime<'a>>()" ) ) ;
136+ assert ! ( rs. contains( "fn __f(arg0: ::cxx::UniquePtr<StructWithLifetime>) {" ) ) ;
137+ assert ! ( rs. contains( "impl<'a> self::Drop for super::StructWithLifetime<'a>" ) ) ;
138+ }
139+
140+ #[ test]
141+ fn test_original_lifetimes_used_in_impls ( ) {
142+ let rs = bridge ( quote ! {
143+ mod ffi {
144+ struct Context <' sess> {
145+ session: & ' sess str ,
146+ }
147+ struct Server <' srv> {
148+ ctx: UniquePtr <Context <' srv>>,
149+ }
150+ struct Client <' clt> {
151+ ctx: UniquePtr <Context <' clt>>,
152+ }
153+ }
154+ } ) ;
155+
156+ // Verify which lifetime name ('sess, 'srv, 'clt) gets used for this impl.
157+ assert ! ( rs. contains( "impl<'sess> ::cxx::memory::UniquePtrTarget for Context<'sess> {" ) ) ;
158+ }
0 commit comments