1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#![crate_name = "quickcheck_macros"]
#![crate_type = "dylib"]
#![doc(html_root_url = "http://burntsushi.net/rustdoc/quickcheck")]
#![feature(plugin_registrar, rustc_private)]
extern crate syntax;
extern crate rustc_plugin;
use syntax::ast;
use syntax::ast::{Ident, ItemKind, PatKind, StmtKind, Stmt, TyKind};
use syntax::codemap;
use syntax::ext::base::{ExtCtxt, MultiModifier, Annotatable};
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;
use syntax::symbol::Symbol;
use rustc_plugin::Registry;
#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_syntax_extension(Symbol::intern("quickcheck"),
MultiModifier(Box::new(expand_meta_quickcheck)));
}
fn expand_meta_quickcheck(cx: &mut ExtCtxt,
span: codemap::Span,
_: &ast::MetaItem,
annot_item: Annotatable) -> Annotatable {
let item = annot_item.expect_item();
match item.node {
ItemKind::Fn(ref decl, unsafety, _constness, abi, _, _) => {
let prop_ident = cx.expr_ident(span, item.ident);
let prop_ty = cx.ty(span, TyKind::BareFn(P(ast::BareFnTy {
unsafety: unsafety,
abi: abi,
lifetimes: vec![],
decl: decl.clone().map(|mut decl| {
for arg in decl.inputs.iter_mut() {
arg.pat = arg.pat.clone().map(|mut pat| {
pat.node = PatKind::Wild;
pat
});
}
decl
}),
})));
let inner_ident = cx.expr_cast(span, prop_ident, prop_ty);
return wrap_item(cx, span, &*item, inner_ident);
},
ItemKind::Static(..) => {
let inner_ident = cx.expr_ident(span, item.ident);
return wrap_item(cx, span, &*item, inner_ident);
},
_ => {
cx.span_err(
span, "#[quickcheck] only supported on statics and functions");
}
}
Annotatable::Item(item)
}
fn wrap_item(cx: &mut ExtCtxt,
span: codemap::Span,
item: &ast::Item,
inner_ident: P<ast::Expr>) -> Annotatable {
let prop = P(ast::Item {attrs: Vec::new(), ..item.clone()});
let check_ident = Ident::from_str("quickcheck");
let check_path = vec!(check_ident, check_ident);
let fn_decl = Stmt {
id: ast::DUMMY_NODE_ID,
node: StmtKind::Item(prop),
span: span,
};
let check_call = Stmt {
id: ast::DUMMY_NODE_ID,
node: StmtKind::Expr(cx.expr_call_global(span, check_path, vec![inner_ident])),
span: span,
};
let body = cx.block(span, vec![fn_decl, check_call]);
let test = cx.item_fn(span, item.ident, vec![], cx.ty(span, TyKind::Tup(vec![])), body);
let mut attrs = item.attrs.clone();
attrs.push(cx.attribute(
span, cx.meta_word(span, Symbol::intern("test"))));
Annotatable::Item(P(ast::Item {attrs: attrs, ..(*test).clone()}))
}