| use proc_macro2::TokenStream; | 
 | use quote::{format_ident, quote, quote_spanned, ToTokens}; | 
 | use syn::spanned::Spanned; | 
 |  | 
 | /// The prefix attached to a Gtest factory function by the RUST_GTEST_TEST_SUITE_FACTORY() macro. | 
 | const RUST_GTEST_FACTORY_PREFIX: &str = "RustGtestFactory_"; | 
 |  | 
 | /// The `gtest` macro can be placed on a function to make it into a Gtest unit test, when linked | 
 | /// into a C++ binary that invokes Gtest. | 
 | /// | 
 | /// The `gtest` macro takes two arguments, which are Rust identifiers. The first is the name of the | 
 | /// test suite and the second is the name of the test, each of which are converted to a string and | 
 | /// given to Gtest. The name of the test function itself does not matter, and need not be unique | 
 | /// (it's placed into a unique module based on the Gtest suite + test names. | 
 | /// | 
 | /// The test function must have no arguments. The return value must be either `()` or | 
 | /// `std::result::Result<(), E>`. If another return type is found, the test will fail when run. If | 
 | /// the return type is a `Result`, then an `Err` is treated as a test failure. | 
 | /// | 
 | /// # Examples | 
 | /// ``` | 
 | /// #[gtest(MathTest, Addition)] | 
 | /// fn my_test() { | 
 | ///   expect_eq!(1 + 1, 2); | 
 | /// } | 
 | /// ``` | 
 | /// | 
 | /// The above adds the function to the Gtest binary as `MathTest.Addtition`: | 
 | /// ``` | 
 | /// [ RUN      ] MathTest.Addition | 
 | /// [       OK ] MathTest.Addition (0 ms) | 
 | /// ``` | 
 | /// | 
 | /// A test with a Result return type, and which uses the `?` operator. It will fail if the test | 
 | /// returns an `Err`, and print the resulting error string: | 
 | /// ``` | 
 | /// #[gtest(ResultTest, CheckThingWithResult)] | 
 | /// fn my_test() -> std::result::Result<(), String> { | 
 | ///   call_thing_with_result()?; | 
 | /// } | 
 | /// ``` | 
 | #[proc_macro_attribute] | 
 | pub fn gtest( | 
 |     arg_stream: proc_macro::TokenStream, | 
 |     input: proc_macro::TokenStream, | 
 | ) -> proc_macro::TokenStream { | 
 |     enum GtestAttributeArgument { | 
 |         TestSuite, | 
 |         TestName, | 
 |     } | 
 |     // Returns a string representation of an identifier argument to the attribute. For example, for | 
 |     // #[gtest(Foo, Bar)], this function would return "Foo" for position 0 and "Bar" for position 1. | 
 |     // If the argument is not a Rust identifier or not present, it returns a compiler error as a | 
 |     // TokenStream to be emitted. | 
 |     fn get_arg_string( | 
 |         args: &syn::AttributeArgs, | 
 |         which: GtestAttributeArgument, | 
 |     ) -> Result<String, TokenStream> { | 
 |         let pos = match which { | 
 |             GtestAttributeArgument::TestSuite => 0, | 
 |             GtestAttributeArgument::TestName => 1, | 
 |         }; | 
 |         match &args[pos] { | 
 |             syn::NestedMeta::Meta(syn::Meta::Path(path)) if path.segments.len() == 1 => { | 
 |                 Ok(path.segments[0].ident.to_string()) | 
 |             } | 
 |             _ => { | 
 |                 let error_stream = match which { | 
 |                     GtestAttributeArgument::TestSuite => { | 
 |                         quote_spanned! { | 
 |                                 args[pos].span() => | 
 |                             compile_error!( | 
 |                                 "Expected a test suite name, written as an identifier." | 
 |                             ); | 
 |                         } | 
 |                     } | 
 |                     GtestAttributeArgument::TestName => { | 
 |                         quote_spanned! { | 
 |                                 args[pos].span() => | 
 |                             compile_error!( | 
 |                                 "Expected a test name, written as an identifier." | 
 |                             ); | 
 |                         } | 
 |                     } | 
 |                 }; | 
 |                 Err(error_stream) | 
 |             } | 
 |         } | 
 |     } | 
 |     /// Parses `#[gtest_suite(path::to::RustType)]` and returns `path::to::RustType`. | 
 |     fn parse_gtest_suite(attr: &syn::Attribute) -> Result<TokenStream, TokenStream> { | 
 |         let parsed = match attr.parse_meta() { | 
 |             Ok(syn::Meta::List(list)) if list.nested.len() == 1 => match &list.nested[0] { | 
 |                 syn::NestedMeta::Meta(syn::Meta::Path(fn_path)) => Ok(fn_path.into_token_stream()), | 
 |                 x => Err(x.span()), | 
 |             }, | 
 |             Ok(x) => Err(x.span()), | 
 |             Err(x) => Err(x.span()), | 
 |         }; | 
 |         parsed.or_else(|span| { | 
 |             Err(quote_spanned! { span => | 
 |                 compile_error!( | 
 |                     "invalid syntax for gtest_suite macro, \ | 
 |                     expected `#[gtest_suite(path::to:RustType)]`"); | 
 |             }) | 
 |         }) | 
 |     } | 
 |  | 
 |     let args = syn::parse_macro_input!(arg_stream as syn::AttributeArgs); | 
 |     let mut input_fn = syn::parse_macro_input!(input as syn::ItemFn); | 
 |  | 
 |     // Populated data from the #[gtest_suite] macro arguments. | 
 |     // | 
 |     // The Rust type wrapping a C++ TestSuite (subclass of `testing::Test`), which is created and | 
 |     // returned by a C++ factory function. If no type is specified, then this is left as None, and | 
 |     // the default C++ factory function will be used to make a `testing::Test` directly. | 
 |     let mut gtest_test_suite_wrapper_type: Option<TokenStream> = None; | 
 |  | 
 |     // Look through other attributes on the test function, parse the ones related to Gtests, and put | 
 |     // the rest back into `attrs`. | 
 |     input_fn.attrs = { | 
 |         let mut keep = Vec::new(); | 
 |         for attr in std::mem::take(&mut input_fn.attrs) { | 
 |             if attr.path.is_ident("gtest_suite") { | 
 |                 let rust_type_name = match parse_gtest_suite(&attr) { | 
 |                     Ok(tokens) => tokens, | 
 |                     Err(error_tokens) => return error_tokens.into(), | 
 |                 }; | 
 |                 gtest_test_suite_wrapper_type = Some(rust_type_name); | 
 |             } else { | 
 |                 keep.push(attr) | 
 |             } | 
 |         } | 
 |         keep | 
 |     }; | 
 |  | 
 |     // No longer mut. | 
 |     let input_fn = input_fn; | 
 |     let gtest_test_suite_wrapper_type = gtest_test_suite_wrapper_type; | 
 |  | 
 |     if let Some(asyncness) = input_fn.sig.asyncness { | 
 |         // TODO(crbug.com/1288947): We can support async functions once we have block_on() support | 
 |         // which will run a RunLoop until the async test completes. The run_test_fn just needs to be | 
 |         // generated to `block_on(|| #test_fn)` instead of calling `#test_fn` synchronously. | 
 |         return quote_spanned! { | 
 |             asyncness.span => | 
 |             compile_error!("async functions are not supported."); | 
 |         } | 
 |         .into(); | 
 |     } | 
 |  | 
 |     let (test_suite_name, test_name) = match args.len() { | 
 |         2 => { | 
 |             let suite = match get_arg_string(&args, GtestAttributeArgument::TestSuite) { | 
 |                 Ok(ok) => ok, | 
 |                 Err(error_stream) => return error_stream.into(), | 
 |             }; | 
 |             let test = match get_arg_string(&args, GtestAttributeArgument::TestName) { | 
 |                 Ok(ok) => ok, | 
 |                 Err(error_stream) => return error_stream.into(), | 
 |             }; | 
 |             (suite, test) | 
 |         } | 
 |         0 | 1 => { | 
 |             return quote! { | 
 |                 compile_error!( | 
 |                     "Expected two arguments. For example: #[gtest(TestSuite, TestName)]."); | 
 |             } | 
 |             .into(); | 
 |         } | 
 |         x => { | 
 |             return quote_spanned! { | 
 |                 args[x.min(2)].span() => | 
 |                 compile_error!( | 
 |                     "Expected two arguments. For example: #[gtest(TestSuite, TestName)]."); | 
 |             } | 
 |             .into(); | 
 |         } | 
 |     }; | 
 |  | 
 |     // We put the test function and all the code we generate around it into a submodule which is | 
 |     // uniquely named for the super module based on the Gtest suite and test names. A result of this | 
 |     // is that if two tests have the same test suite + name, a compiler error would report the | 
 |     // conflict. | 
 |     let test_mod = format_ident!("__test_{}_{}", test_suite_name, test_name); | 
 |  | 
 |     // The run_test_fn identifier is marked #[no_mangle] to work around a codegen bug where the | 
 |     // function is seen as dead and the compiler omits it from the object files. Since it's | 
 |     // #[no_mangle], the identifier must be globally unique or we have an ODR violation. To produce | 
 |     // a unique identifier, we roll our own name mangling by combining the file name and path from | 
 |     // the source tree root with the Gtest suite and test names and the function itself. | 
 |     // | 
 |     // Note that an adversary could still produce a bug here by placing two equal Gtest suite and | 
 |     // names in a single .rs file but in separate inline submodules. | 
 |     let mangled_function_name = |f: &syn::ItemFn| -> syn::Ident { | 
 |         let file_name = file!().replace(|c: char| !c.is_ascii_alphanumeric(), "_"); | 
 |         format_ident!("{}_{}_{}_{}", file_name, test_suite_name, test_name, f.sig.ident) | 
 |     }; | 
 |     let run_test_fn = format_ident!("run_test_{}", mangled_function_name(&input_fn)); | 
 |  | 
 |     // The identifier of the function which contains the body of the test. | 
 |     let test_fn = &input_fn.sig.ident; | 
 |  | 
 |     // Implements ToTokens to generate a reference to a static-lifetime, null-terminated, C-String | 
 |     // literal. It is represented as an array of type std::os::raw::c_char which can be either | 
 |     // signed or unsigned depending on the platform, and it can be passed directly to C++. This | 
 |     // differs from byte strings and CStr which work with `u8`. | 
 |     // | 
 |     // TODO(crbug.com/1298175): Would it make sense to write a c_str_literal!() macro that takes a | 
 |     // Rust string literal and produces a null-terminated array of `c_char`? Then you could write | 
 |     // `c_str_literal!(file!())` for example, or implement a `file_c_str!()` in this way. Explore | 
 |     // using https://crates.io/crates/cstr. | 
 |     // | 
 |     // TODO(danakj): Write unit tests for this, and consider pulling this out into its own crate, | 
 |     // if we don't replace it with c_str_literal!() or the "cstr" crate. | 
 |     struct CStringLiteral<'a>(&'a str); | 
 |     impl quote::ToTokens for CStringLiteral<'_> { | 
 |         fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { | 
 |             let mut c_chars = self.0.chars().map(|c| c as std::os::raw::c_char).collect::<Vec<_>>(); | 
 |             c_chars.push(0); | 
 |             // Verify there's no embedded nulls as that would be invalid if the literal were put in | 
 |             // a std::ffi::CString. | 
 |             assert_eq!(c_chars.iter().filter(|x| **x == 0).count(), 1); | 
 |             let comment = format!("\"{}\" as [c_char]", self.0); | 
 |             tokens.extend(quote! { | 
 |                 { | 
 |                     #[doc=#comment] | 
 |                     &[#(#c_chars as std::os::raw::c_char),*] | 
 |                 } | 
 |             }); | 
 |         } | 
 |     } | 
 |  | 
 |     // C-compatible string literals, that can be inserted into the quote! macro. | 
 |     let test_suite_name_c_bytes = CStringLiteral(&test_suite_name); | 
 |     let test_name_c_bytes = CStringLiteral(&test_name); | 
 |     let file_c_bytes = CStringLiteral(file!()); | 
 |  | 
 |     let gtest_factory_fn = match >est_test_suite_wrapper_type { | 
 |         Some(rust_type) => { | 
 |             // Get the Gtest factory function pointer from the the TestSuite trait. | 
 |             quote! { <#rust_type as ::rust_gtest_interop::TestSuite>::gtest_factory_fn_ptr() } | 
 |         } | 
 |         None => { | 
 |             // If the #[gtest] macros didn't specify a test suite, then we use | 
 |             // `rust_gtest_interop::rust_gtest_default_factory() which makes a TestSuite with | 
 |             // `testing::Test` directly. | 
 |             quote! { ::rust_gtest_interop::__private::rust_gtest_default_factory } | 
 |         } | 
 |     }; | 
 |     let test_fn_call = match >est_test_suite_wrapper_type { | 
 |         Some(_rust_type) => { | 
 |             // SAFETY: Our lambda casts the `suite` reference and does not move from it, and the | 
 |             // resulting type is not Unpin. | 
 |             quote! { | 
 |                 let p = unsafe { | 
 |                     suite.map_unchecked_mut(|suite: &mut ::rust_gtest_interop::OpaqueTestingTest| { | 
 |                         suite.as_mut() | 
 |                     }) | 
 |                 }; | 
 |                 #test_fn(p) | 
 |             } | 
 |         } | 
 |         None => quote! { #test_fn() }, | 
 |     }; | 
 |  | 
 |     let output = quote! { | 
 |         mod #test_mod { | 
 |             use super::*; | 
 |  | 
 |             #[::rust_gtest_interop::small_ctor::ctor] | 
 |             unsafe fn register_test() { | 
 |                 let r = ::rust_gtest_interop::__private::TestRegistration { | 
 |                     func: #run_test_fn, | 
 |                     test_suite_name: #test_suite_name_c_bytes, | 
 |                     test_name: #test_name_c_bytes, | 
 |                     file: #file_c_bytes, | 
 |                     line: line!(), | 
 |                     factory: #gtest_factory_fn, | 
 |                 }; | 
 |                 ::rust_gtest_interop::__private::register_test(r); | 
 |             } | 
 |  | 
 |             // The function is extern "C" so `register_test()` can pass this fn as a pointer to C++ | 
 |             // where it's registered with gtest. | 
 |             // | 
 |             // TODO(crbug.com/1296284): Removing #[no_mangle] makes rustc drop the symbol for the | 
 |             // test function in the generated rlib which produces linker errors. If we resolve the | 
 |             // linked bug and emit real object files from rustc for linking, then all the required | 
 |             // symbols are present and `#[no_mangle]` should go away along with the custom-mangling | 
 |             // of `run_test_fn`. We can not use `pub` to resolve this unfortunately. When `#[used]` | 
 |             // is fixed in https://github.com/rust-lang/rust/issues/47384, this may also be | 
 |             // resolved as well. | 
 |             #[no_mangle] | 
 |             extern "C" fn #run_test_fn( | 
 |                 suite: std::pin::Pin<&mut ::rust_gtest_interop::OpaqueTestingTest> | 
 |             ) { | 
 |                 let catch_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { | 
 |                     #test_fn_call | 
 |                 })); | 
 |                 use ::rust_gtest_interop::TestResult; | 
 |                 let err_message: Option<String> = match catch_result { | 
 |                     Ok(fn_result) => TestResult::into_error_message(fn_result), | 
 |                     Err(_) => Some("Test panicked".to_string()), | 
 |                 }; | 
 |                 if let Some(m) = err_message.as_ref() { | 
 |                     ::rust_gtest_interop::__private::add_failure_at(file!(), line!(), &m); | 
 |                 } | 
 |             } | 
 |  | 
 |             #input_fn | 
 |         } | 
 |     }; | 
 |     output.into() | 
 | } | 
 |  | 
 | /// The `#[extern_test_suite()]` macro is used to implement the unsafe `TestSuite` trait. | 
 | /// | 
 | /// The `TestSuite` trait is used to mark a Rust type as being a wrapper of a C++ subclass of | 
 | /// `testing::Test`. This makes it valid to cast from a `*mut testing::Test` to a pointer of the | 
 | /// marked Rust type. | 
 | /// | 
 | /// It also marks a promise that on the C++, there exists an instantiation of the | 
 | /// RUST_GTEST_TEST_SUITE_FACTORY() macro for the C++ subclass type which will be linked with the | 
 | /// Rust crate. | 
 | /// | 
 | /// The macro takes a single parameter which is the fully specified C++ typename of the C++ subclass | 
 | /// for which the implementing Rust type is a wrapper. It expects the body of the trait | 
 | /// implementation to be empty, as it will fill in the required implementation. | 
 | /// | 
 | /// # Example | 
 | /// If in C++ we have: | 
 | /// ```cpp | 
 | /// class GoatTestSuite : public testing::Test {} | 
 | /// RUST_GTEST_TEST_SUITE_FACTORY(GoatTestSuite); | 
 | /// ``` | 
 | /// | 
 | /// And in Rust we have a `ffi::GoatTestSuite` type generated to wrap the C++ type. The the type can | 
 | /// be marked as a valid TestSuite with the `#[extern_test_suite]` macro: | 
 | /// ```rs | 
 | /// #[extern_test_suite("GoatTestSuite")] | 
 | /// unsafe impl rust_gtest_interop::TestSuite for ffi::GoatTestSuite {} | 
 | /// ``` | 
 | /// | 
 | /// # Internals | 
 | /// The #[cpp_prefix("STRING_")] attribute can follow `#[extern_test_suite()]` to control the | 
 | /// path to the C++ Gtest factory function. This is used for connecting to different C++ macros | 
 | /// than the usual RUST_GTEST_TEST_SUITE_FACTORY(). | 
 | #[proc_macro_attribute] | 
 | pub fn extern_test_suite( | 
 |     arg_stream: proc_macro::TokenStream, | 
 |     input: proc_macro::TokenStream, | 
 | ) -> proc_macro::TokenStream { | 
 |     let args = syn::parse_macro_input!(arg_stream as syn::AttributeArgs); | 
 |  | 
 |     // TODO(b/229791967): With CXX it is not possible to get the C++ typename and path from the Rust | 
 |     // wrapper type, so we require specifying it by hand in the macro. It would be nice to remove | 
 |     // this opportunity for mistakes. | 
 |     let cpp_type = match if args.len() == 1 { Some(&args[0]) } else { None } { | 
 |         Some(syn::NestedMeta::Lit(syn::Lit::Str(lit_str))) => { | 
 |             // TODO(danakj): This code drops the C++ namespaces, because we can't produce a mangled | 
 |             // name and can't generate bindings involving fn pointers, so we require the C++ | 
 |             // function to be `extern "C"` which means it has no namespace. Eventually we should | 
 |             // drop the `extern "C"` on the C++ side and use the full path here. | 
 |             let string = lit_str.value(); | 
 |             let class_name = string.split("::").last(); | 
 |             match class_name { | 
 |                 Some(name) => format_ident!("{}", name).into_token_stream(), | 
 |                 None => { | 
 |                     return quote_spanned! {lit_str.span() => compile_error!( | 
 |                         "invalid C++ class name" | 
 |                     )} | 
 |                     .into(); | 
 |                 } | 
 |             } | 
 |         } | 
 |         _ => { | 
 |             return quote! {compiler_error!( | 
 |                 "expected C++ type as argument to extern_test_suite" | 
 |             )} | 
 |             .into(); | 
 |         } | 
 |     }; | 
 |  | 
 |     /// Parses `#[cpp_prefix("PREFIX_STRING_")]` and returns `"PREFIX_STRING_"`. | 
 |     fn parse_cpp_prefix(attr: &syn::Attribute) -> Result<String, TokenStream> { | 
 |         let parsed = match attr.parse_meta() { | 
 |             Ok(syn::Meta::List(list)) if list.nested.len() == 1 => match &list.nested[0] { | 
 |                 syn::NestedMeta::Lit(syn::Lit::Str(lit_str)) => Ok(lit_str.value()), | 
 |                 x => Err(x.span()), | 
 |             }, | 
 |             Ok(x) => Err(x.span()), | 
 |             Err(x) => Err(x.span()), | 
 |         }; | 
 |         parsed.map_err(|span| { | 
 |             quote_spanned! { span => | 
 |                 compile_error!( | 
 |                     "invalid syntax for extern_test_suite macro, \ | 
 |                     expected `#[cpp_prefix("PREFIX_STRING_")]`"); | 
 |             } | 
 |         }) | 
 |     } | 
 |  | 
 |     let mut trait_impl = syn::parse_macro_input!(input as syn::ItemImpl); | 
 |     if !trait_impl.items.is_empty() { | 
 |         return quote_spanned! {trait_impl.items[0].span() => compile_error!( | 
 |             "expected empty trait impl" | 
 |         )} | 
 |         .into(); | 
 |     } | 
 |  | 
 |     let mut cpp_prefix = RUST_GTEST_FACTORY_PREFIX.to_owned(); | 
 |  | 
 |     // Look through other attributes on `trait_impl`, parse the ones related to Gtests, and put | 
 |     // the rest back into `attrs`. | 
 |     trait_impl.attrs = { | 
 |         let mut keep = Vec::new(); | 
 |         for attr in std::mem::take(&mut trait_impl.attrs) { | 
 |             if attr.path.is_ident("cpp_prefix") { | 
 |                 cpp_prefix = match parse_cpp_prefix(&attr) { | 
 |                     Ok(tokens) => tokens, | 
 |                     Err(error_tokens) => return error_tokens.into(), | 
 |                 }; | 
 |             } else { | 
 |                 keep.push(attr) | 
 |             } | 
 |         } | 
 |         keep | 
 |     }; | 
 |  | 
 |     // No longer mut. | 
 |     let trait_impl = trait_impl; | 
 |     let cpp_prefix = cpp_prefix; | 
 |  | 
 |     let trait_name = match &trait_impl.trait_ { | 
 |         Some((_, path, _)) => path, | 
 |         None => { | 
 |             return quote! {compile_error!( | 
 |                 "expected impl rust_gtest_interop::TestSuite trait" | 
 |             )} | 
 |             .into(); | 
 |         } | 
 |     }; | 
 |     let rust_type = match &*trait_impl.self_ty { | 
 |         syn::Type::Path(type_path) => type_path, | 
 |         _ => { | 
 |             return quote_spanned! {trait_impl.self_ty.span() => compile_error!( | 
 |                 "expected type that wraps C++ subclass of `testing::Test`" | 
 |             )} | 
 |             .into(); | 
 |         } | 
 |     }; | 
 |  | 
 |     // TODO(danakj): We should generate a C++ mangled name here, then we don't require | 
 |     // the function to be `extern "C"` (or have the author write the mangled name | 
 |     // themselves). | 
 |     let cpp_fn_name = format_ident!("{}{}", cpp_prefix, cpp_type.into_token_stream().to_string()); | 
 |  | 
 |     let output = quote! { | 
 |         unsafe impl #trait_name for #rust_type { | 
 |             fn gtest_factory_fn_ptr() -> rust_gtest_interop::GtestFactoryFunction { | 
 |                 extern "C" { | 
 |                     fn #cpp_fn_name( | 
 |                         f: extern "C" fn( | 
 |                             test_body: ::std::pin::Pin<&mut ::rust_gtest_interop::OpaqueTestingTest> | 
 |                         ) | 
 |                     ) -> ::std::pin::Pin<&'static mut ::rust_gtest_interop::OpaqueTestingTest>; | 
 |                 } | 
 |                 #cpp_fn_name | 
 |             } | 
 |         } | 
 |     }; | 
 |     output.into() | 
 | } |