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