1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
mod args; mod impl_block; use args::parse_args; use impl_block::create_impl_blocks; use proc_macro_error::proc_macro_error; use quote::quote; use syn::{parse_macro_input, AttributeArgs, ItemFn}; /// Run this test multiple times, replacing all references to the trait specified with a specific implementation. /// Use it like this: /// /// `#[test_impl(ExampleTrait(ExampleStruct, ExampleStruct2))]` #[proc_macro_attribute] #[proc_macro_error] pub fn test_impl( args: proc_macro::TokenStream, input: proc_macro::TokenStream, ) -> proc_macro::TokenStream { // Parse the arguments parsed to our macro and extract the trait and impl info: let args_parsed = parse_macro_input!(args as AttributeArgs); let trait_and_impls = match parse_args(&args_parsed) { // If we get None back it's because parse_args has failed and used emit_error. // In this instance we just throw the input stream straight back out again // as there's no further work to do here. None => return input, Some(t) => t, }; // Now that we've successfully parsed the arguments, parse the actual function we've // been passed. let input_parsed = syn::parse::<ItemFn>(input).unwrap(); // Grab the ident of the function. We use this in the next two calls to ensure that // the type checks we write have unique identifiers, by prepending the fn name to them. let fn_ident = &input_parsed.sig.ident; // Emit some code to check that the trait the user entered actually exists in this scope let trait_check = trait_and_impls.get_trait_check(&fn_ident); // Then emit some code to check that the structs we've been given really are implementations // of the trait in question let impl_checks = trait_and_impls.get_impl_checks(&fn_ident); // Grab some info about the function because I'm not sure how to access it inside quote! // otherwise (e.g. #input_parsed.sig doesn't work) let attrs = &input_parsed.attrs; let vis = &input_parsed.vis; let sig = &input_parsed.sig; // Take the original function body and copy it multiple times, making the necessary changes // for it to test the implementation rather than the trait itself. let impl_blocks = create_impl_blocks(&trait_and_impls, &input_parsed.block); // Finally, reconstruct the whole thing, along with the type check code: let output = quote! { #trait_check #(#impl_checks)* #(#attrs)* #vis #sig { #(#impl_blocks)* } }; output.into() }