1extern crate proc_macro;
2#[macro_use]
3extern crate quote;
4extern crate core;
5extern crate proc_macro2;
6extern crate syn;
7
8use proc_macro::TokenStream;
9use syn::{Item, ItemMod, parse, spanned::Spanned};
10
11#[proc_macro]
12pub fn build_test_setup(_input: TokenStream) -> TokenStream {
13 quote! {
14 println!("cargo::rustc-link-arg-tests=-Tlink-test.x");
15 println!("cargo::rustc-link-arg-tests=-znostart-stop-gc");
16 }
17 .into()
18}
19
20#[proc_macro_attribute]
21pub fn tests(args: TokenStream, input: TokenStream) -> TokenStream {
22 match tests_impl(args, input) {
23 Ok(tokens) => tokens,
24 Err(e) => e.to_compile_error().into(),
25 }
26}
27
28fn tests_impl(_args: TokenStream, input: TokenStream) -> Result<TokenStream, parse::Error> {
29 let krate = format_ident!("bare_test");
30 let module: ItemMod = syn::parse(input)?;
31
32 let ident = &module.ident;
33
34 let mut untouched_tokens = vec![];
35 let mut test_functions = vec![];
36 let span = module.span();
37
38 let items = &module
39 .content
40 .ok_or(parse::Error::new(
41 span,
42 "module must be inline (e.g. `mod foo {}`)",
43 ))?
44 .1;
45
46 for item in items {
47 match item {
48 Item::Fn(f) if f.attrs.iter().any(|attr| attr.path().is_ident("test")) => {
49 let f_name = &f.sig.ident;
50 let _f_name = format_ident!("__{}", f.sig.ident);
51 let block = &f.block;
52 let static_name = format_ident!("__TEST_{}", f_name.to_string().to_uppercase());
53 let f_name_str = f_name.to_string();
54 let f_atters = &f.attrs;
55
56 let mut timeout = 0u64;
57 if let Some(attr) = f_atters.iter().find(|attr| attr.path().is_ident("timeout")) {
58 if let syn::Meta::NameValue(nv) = &attr.meta
59 && let syn::Expr::Lit(lit_int) = &nv.value
60 && let syn::Lit::Int(int_lit) = &lit_int.lit
61 {
62 timeout = int_lit.base10_parse::<u64>()?;
63 } else {
64 return Err(syn::Error::new(attr.span(), "invalid timeout attribute"));
65 }
66 }
67
68 test_functions.push(quote! {
69 #(#f_atters)*
70 fn #f_name() {
71 #_f_name()
72 }
73
74 fn #_f_name() {
75 #block
76 }
77
78 #[used(linker)]
79 #[unsafe(link_section = ".test_case")]
80 static #static_name: #krate::TestCase = #krate::TestCase {
81 name: #f_name_str,
82 timeout_ms: #timeout,
83 test_fn: #_f_name,
84 };
85 });
86 }
87 _ => {
88 untouched_tokens.push(item);
89 }
90 }
91 }
92
93 Ok(quote!(
94 #[cfg(test)]
95 mod #ident{
96 #(#untouched_tokens)*
97 #(#test_functions)*
98
99 }
100 )
101 .into())
102}