compiler_test_derive/
lib.rs#[cfg(not(test))]
extern crate proc_macro;
#[cfg(not(test))]
use proc_macro::TokenStream;
#[cfg(test)]
use proc_macro2::TokenStream;
use quote::quote;
use std::path::PathBuf;
#[cfg(not(test))]
use syn::parse;
#[cfg(test)]
use syn::parse2 as parse;
use syn::*;
mod ignores;
macro_rules! parse_macro_input {
(
$token_stream:ident as $T:ty
) => {
match parse::<$T>($token_stream) {
Ok(data) => data,
Err(err) => {
return TokenStream::from(err.to_compile_error());
}
}
};
(
$token_stream:ident
) => {
parse_macro_input!($token_stream as _)
};
}
#[proc_macro_attribute]
pub fn compiler_test(attrs: TokenStream, input: TokenStream) -> TokenStream {
let path: Option<ExprPath> = parse::<ExprPath>(attrs).ok();
let mut my_fn: ItemFn = parse_macro_input!(input as ItemFn);
let fn_name = my_fn.sig.ident.clone();
let mut ignores_txt_path = PathBuf::new();
ignores_txt_path.push(env!("CARGO_MANIFEST_DIR"));
ignores_txt_path.push("../../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(" ", "");
let should_ignore = ignores.should_ignore_host(&engine_name, &compiler_name, &full_path);
return should_ignore;
};
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 config_engine = ::quote::format_ident!("{}", engine_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::Engine::#config_engine, 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 dylib_engine_test = construct_engine_test(func, compiler_name, "Dylib", "dylib");
let compiler_name_lowercase = compiler_name.to_lowercase();
quote! {
#[cfg(feature = #compiler_name_lowercase)]
mod #mod_name {
use super::*;
#universal_engine_test
#dylib_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
}
};
x.into()
}
#[cfg(test)]
mod tests;