#[cfg(proc_macro)]
extern crate proc_macro;
use proc_macro2::TokenStream;
use quote::quote;
use std::path::Path;
use syn::*;
mod ignores;
#[cfg(proc_macro)]
#[proc_macro_attribute]
pub fn compiler_test(
attrs: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
compiler_test_impl(attrs.into(), input.into()).into()
}
fn compiler_test_impl(attrs: TokenStream, input: TokenStream) -> TokenStream {
let path: Option<ExprPath> = parse2::<ExprPath>(attrs).ok();
let mut my_fn: ItemFn = match syn::parse2(input) {
Ok(f) => f,
Err(e) => return e.into_compile_error(),
};
let fn_name = &my_fn.sig.ident;
let tests_dir = Path::new(env!("CARGO_MANIFEST_DIR"))
.ancestors()
.nth(2)
.unwrap();
let ignores_txt_path = tests_dir.join("ignores.txt");
let ignores = crate::ignores::Ignores::build_from_path(ignores_txt_path);
let should_ignore = |test_name: &str, compiler_name: &str, engine_name: &str| {
let compiler_name = compiler_name.to_lowercase();
let engine_name = engine_name.to_lowercase();
let full_path = format!(
"{}::{}::{}::{}",
quote! { #path },
test_name,
compiler_name,
engine_name
)
.replace(' ', "");
ignores.should_ignore_host(&engine_name, &compiler_name, &full_path)
};
let construct_engine_test = |func: &::syn::ItemFn,
compiler_name: &str,
engine_name: &str,
engine_feature_name: &str|
-> ::proc_macro2::TokenStream {
let config_compiler = ::quote::format_ident!("{}", compiler_name);
let test_name = ::quote::format_ident!("{}", engine_name.to_lowercase());
let mut new_sig = func.sig.clone();
let attrs = func
.attrs
.clone()
.iter()
.fold(quote! {}, |acc, new| quote! {#acc #new});
new_sig.ident = test_name;
new_sig.inputs = ::syn::punctuated::Punctuated::new();
let f = quote! {
#[test_log::test]
#attrs
#[cfg(feature = #engine_feature_name)]
#new_sig {
#fn_name(crate::Config::new(crate::Compiler::#config_compiler))
}
};
if should_ignore(
&func.sig.ident.to_string().replace("r#", ""),
compiler_name,
engine_name,
) && !cfg!(test)
{
quote! {
#[ignore]
#f
}
} else {
f
}
};
let construct_compiler_test =
|func: &::syn::ItemFn, compiler_name: &str| -> ::proc_macro2::TokenStream {
let mod_name = ::quote::format_ident!("{}", compiler_name.to_lowercase());
let universal_engine_test =
construct_engine_test(func, compiler_name, "Universal", "universal");
let compiler_name_lowercase = compiler_name.to_lowercase();
quote! {
#[cfg(feature = #compiler_name_lowercase)]
mod #mod_name {
use super::*;
#universal_engine_test
}
}
};
let singlepass_compiler_test = construct_compiler_test(&my_fn, "Singlepass");
let cranelift_compiler_test = construct_compiler_test(&my_fn, "Cranelift");
let llvm_compiler_test = construct_compiler_test(&my_fn, "LLVM");
my_fn.attrs = vec![];
let x = quote! {
#[cfg(test)]
mod #fn_name {
use super::*;
#[allow(unused)]
#my_fn
#singlepass_compiler_test
#cranelift_compiler_test
#llvm_compiler_test
}
};
#[allow(clippy::useless_conversion)]
x.into()
}
#[cfg(test)]
mod tests;