1extern crate proc_macro;
2extern crate proc_macro2;
3extern crate quote;
4extern crate syn;
5
6use proc_macro::TokenStream;
7use quote::quote;
8use syn::{
9 parse::{Parse, Parser},
10 parse_quote,
11 spanned::Spanned,
12};
13
14#[proc_macro_attribute]
15pub fn quickcheck(_args: TokenStream, input: TokenStream) -> TokenStream {
16 let output = match syn::Item::parse.parse(input.clone()) {
17 Ok(syn::Item::Fn(mut item_fn)) => {
18 let mut inputs = syn::punctuated::Punctuated::new();
19 let mut errors = Vec::new();
20
21 item_fn.sig.inputs.iter().for_each(|input| match *input {
22 syn::FnArg::Typed(syn::PatType { ref ty, .. }) => {
23 inputs.push(parse_quote!(_: #ty));
24 }
25 _ => errors.push(syn::parse::Error::new(
26 input.span(),
27 "unsupported kind of function argument",
28 )),
29 });
30
31 if errors.is_empty() {
32 let attrs = std::mem::take(&mut item_fn.attrs);
33 let name = &item_fn.sig.ident;
34 let fn_type = syn::TypeBareFn {
35 lifetimes: None,
36 unsafety: item_fn.sig.unsafety,
37 abi: item_fn.sig.abi.clone(),
38 fn_token: <syn::Token![fn]>::default(),
39 paren_token: syn::token::Paren::default(),
40 inputs,
41 variadic: item_fn.sig.variadic.clone(),
42 output: item_fn.sig.output.clone(),
43 };
44
45 quote! {
46 #[test]
47 #(#attrs)*
48 fn #name() {
49 #item_fn
50 ::qcheck::quickcheck(#name as #fn_type)
51 }
52 }
53 } else {
54 errors
55 .iter()
56 .map(syn::parse::Error::to_compile_error)
57 .collect()
58 }
59 }
60 Ok(syn::Item::Static(mut item_static)) => {
61 let attrs = std::mem::take(&mut item_static.attrs);
62 let name = &item_static.ident;
63
64 quote! {
65 #[test]
66 #(#attrs)*
67 fn #name() {
68 #item_static
69 ::qcheck::quickcheck(#name)
70 }
71 }
72 }
73 _ => {
74 let span = proc_macro2::TokenStream::from(input).span();
75 let msg = "#[quickcheck] is only supported on statics and functions";
76
77 syn::parse::Error::new(span, msg).to_compile_error()
78 }
79 };
80
81 output.into()
82}