sample_test_macros/
lib.rs1extern crate proc_macro;
2extern crate proc_macro2;
3extern crate quote;
4extern crate syn;
5
6use std::mem;
7
8use proc_macro::TokenStream;
9use quote::quote;
10use syn::{
11 parse::{Parse, Parser},
12 spanned::Spanned,
13};
14
15#[proc_macro_attribute]
16pub fn sample_test(_args: TokenStream, input: TokenStream) -> TokenStream {
17 let output = match syn::Item::parse.parse(input.clone()) {
18 Ok(syn::Item::Fn(mut item_fn)) => {
19 let mut inputs = syn::punctuated::Punctuated::new();
20 let mut samplers: syn::punctuated::Punctuated<_, syn::token::Comma> =
21 syn::punctuated::Punctuated::new();
22 let mut errors = Vec::new();
23
24 item_fn
25 .sig
26 .inputs
27 .iter_mut()
28 .for_each(|input| match *input {
29 syn::FnArg::Typed(syn::PatType {
30 ref mut ty,
31 ref mut attrs,
32 ..
33 }) => {
34 let ix = attrs
35 .iter()
36 .position(|a| a.path.segments.iter().map(|s| &s.ident).eq(["sample"]));
37 if let Some(ix) = ix {
38 samplers.push(attrs.remove(ix).tokens);
39 inputs.push(syn::BareFnArg {
40 attrs: attrs.clone(),
41 name: None,
42 ty: *ty.clone(),
43 })
44 }
45 }
46 _ => errors.push(syn::parse::Error::new(
47 input.span(),
48 "unsupported kind of function argument",
49 )),
50 });
51
52 if errors.is_empty() {
53 let attrs = mem::replace(&mut item_fn.attrs, Vec::new());
54 let name = &item_fn.sig.ident;
55 let fn_type = syn::TypeBareFn {
56 lifetimes: None,
57 unsafety: item_fn.sig.unsafety.clone(),
58 abi: item_fn.sig.abi.clone(),
59 fn_token: <syn::Token![fn]>::default(),
60 paren_token: syn::token::Paren::default(),
61 inputs,
62 variadic: item_fn.sig.variadic.clone(),
63 output: item_fn.sig.output.clone(),
64 };
65
66 let x = samplers.clone();
67 quote! {
68 #[test]
69 #(#attrs)*
70 fn #name() {
71 let sampler = (#x,);
72
73 #item_fn
74 ::sample_test::tester::sample_test(sampler, #name as #fn_type)
75 }
76 }
77 } else {
78 errors
79 .iter()
80 .map(syn::parse::Error::to_compile_error)
81 .collect()
82 }
83 }
84 Ok(syn::Item::Static(mut item_static)) => {
85 let attrs = mem::replace(&mut item_static.attrs, Vec::new());
86 let name = &item_static.ident;
87
88 quote! {
89 #[test]
90 #(#attrs)*
91 fn #name() {
92 #item_static
93 ::quickcheck::quickcheck(#name)
94 }
95 }
96 }
97 _ => {
98 let span = proc_macro2::TokenStream::from(input).span();
99 let msg = "#[sample_test] is only supported on statics and functions";
100
101 syn::parse::Error::new(span, msg).to_compile_error()
102 }
103 };
104
105 output.into()
106}
107
108#[cfg(test)]
109mod tests {}