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