1use proc_macro::TokenStream;
2use proc_macro2::Span;
3use quote::quote;
4
5use syn::{
6 Attribute,
7 Ident,
8 Item,
9 ItemFn,
10 ItemMod,
11 ItemUse,
12 Meta,
13 Path,
14 PathArguments,
15 PathSegment,
16 punctuated::Punctuated,
17 Stmt,
18 Token,
19 UseTree,
20};
21use syn::ext::IdentExt;
22use syn::parse::Parser;
23
24use std::collections::VecDeque;
25
26#[derive(Clone, Debug, Eq, PartialEq)]
27enum RvnItemAttr {
28 Annotate(String),
29 AnnotateMulti,
30 Assume,
31 AssumeFor(String),
32 Declare,
33 Define,
34 Falsify,
35 ForCall(String),
36 ForValues(String),
37 Import,
38 InstRule(String),
39 Phantom,
40 Recursive,
41 Verify,
42}
43
44fn path_to_one_str(p: &Path) -> Option<String> {
45 if p.segments.len() == 1 {
46 Some(p.segments.first().unwrap().ident.to_string())
47 } else {
48 None
49 }
50}
51
52impl RvnItemAttr {
53 fn try_from_attr(attr: &Attribute) -> Option<RvnItemAttr> {
54 match &attr.meta {
55 Meta::Path(p) if p.segments.len() == 1 => {
56 match path_to_one_str(p).as_deref() {
57 Some("annotate") => panic!("#[annotate] needs arguments"),
58 Some("annotate_multi") => Some(RvnItemAttr::AnnotateMulti),
59 Some("assume") => Some(RvnItemAttr::Assume),
60 Some("declare") => Some(RvnItemAttr::Declare),
61 Some("define") => Some(RvnItemAttr::Define),
62 Some("define_rec") => panic!("#[define_rec] has been replaced by #[define] followed by #[recursive]"),
63 Some("falsify") => Some(RvnItemAttr::Falsify),
64 Some("import") => Some(RvnItemAttr::Import),
65 Some("phantom") => Some(RvnItemAttr::Phantom),
66 Some("recursive") => Some(RvnItemAttr::Recursive),
67 Some("verify") => Some(RvnItemAttr::Verify),
68 _ => None,
69 }
70 }
71 Meta::List(l) if l.path.segments.len() == 1 => {
72 match path_to_one_str(&l.path).as_deref() {
73 Some("annotate") =>
74 Some(RvnItemAttr::Annotate(l.tokens.to_string())),
75 Some("assume") =>
76 Some(RvnItemAttr::AssumeFor(l.tokens.to_string())),
77 Some("assume_for") => {
78 panic!("#[assume_for(..)] has been replaced by #[assume(..)]")
79 }
82 Some("for_call") =>
83 Some(RvnItemAttr::ForCall(l.tokens.to_string())),
84 Some("for_type") => Some(RvnItemAttr::InstRule(l.tokens.to_string())),
85 Some("for_values") =>
86 Some(RvnItemAttr::ForValues(l.tokens.to_string())),
87 _ => None,
88 }
89 }
90 _ => None,
91 }
92 }
93
94 fn extract_from_attrs(attrs: &mut Vec<Attribute>) -> Vec<RvnItemAttr> {
97 let mut out = Vec::new();
98 attrs.retain_mut(|attr| {
99 match RvnItemAttr::try_from_attr(attr) {
100 Some(a) => {
101 out.push(a);
102 false
103 }
104 None => true,
105 }
106 });
107 out
108 }
109
110 fn extract_from_item(item: &mut Item) -> Vec<RvnItemAttr> {
111 match item {
112 Item::Const(i) => Self::extract_from_attrs(&mut i.attrs),
113 Item::Enum(i) => Self::extract_from_attrs(&mut i.attrs),
114 Item::Fn(i) => Self::extract_from_attrs(&mut i.attrs),
115 Item::Impl(_) => Vec::new(),
116 Item::Mod(_) => Vec::new(),
117 Item::Struct(i) => Self::extract_from_attrs(&mut i.attrs),
118 Item::Type(i) => Self::extract_from_attrs(&mut i.attrs),
119 Item::Use(i) => Self::extract_from_attrs(&mut i.attrs),
120 _item => Vec::new(),
123 }
124 }
125}
126
127#[derive(Clone, Debug, Eq, PartialEq)]
128enum RccCommand {
129 Annotate(String, ItemFn),
130 AnnotateMulti(Vec<String>, Vec<String>, ItemFn),
131 Assume(Vec<String>, ItemFn),
132 AssumeFor(String, ItemFn),
133 Declare(Item, bool),
135 DeclareType(Ident, usize),
136 Define(Item, bool, bool),
140 Import(ItemUse),
141 Goal(bool, ItemFn),
144}
145
146impl RccCommand {
147 fn mk_declare_define(
148 ras: Vec<RvnItemAttr>,
149 item: Item,
150 define: bool,
151 ) -> (Option<Self>, Option<Item>) {
152 let mut phantom = false;
153 let mut recursive = false;
154 for a in ras { match a {
155 RvnItemAttr::Phantom => { phantom = true; },
156 RvnItemAttr::Recursive if define => { recursive = true; },
157 a => panic!(
158 "Unexpected {:?} under #[{}]",
159 a,
160 if define { "define"} else { "declare" },
161 ),
162 }}
163 let ret_item = if !phantom {
164 Some(item.clone())
165 } else {
166 None
167 };
168 if define {
169 (Some(Self::Define(item, phantom, recursive)), ret_item)
170 } else {
171 (Some(Self::Declare(item, phantom)), ret_item)
172 }
173 }
174
175 fn mk_goal(
176 ras: Vec<RvnItemAttr>,
177 item: Item,
178 should_be_valid: bool,
179 ) -> (Option<Self>, Option<Item>) {
180 assert!(
181 ras.len() == 0,
182 "#[verify] and #[falsify] should not have further attributes beneath them",
183 );
184 let item = match item {
185 Item::Fn(i) => i,
186 item => panic!(
187 "should not use #[verify] or #[falsify] on {:?}",
188 item,
189 ),
190 };
191 (Some(Self::Goal(should_be_valid, item)), None)
192 }
193
194 fn from_item(mut item: Item) -> (Option<RccCommand>, Option<Item>) {
199 use RvnItemAttr::*;
200
201 let ras = RvnItemAttr::extract_from_item(&mut item);
202
203 if ras.len() == 0 {
206 return (None, Some(item));
207 }
208 let mut ras = VecDeque::from(ras);
209 match ras.pop_front().unwrap() {
210 Annotate(call) => match item {
211 Item::Fn(i) => {
212 let c = RccCommand::Annotate(call, i);
213 (Some(c), None)
214 }
215 item => panic!("Can't use #[annotate(..)] on {:?}", item),
216 }
217 AnnotateMulti => match item {
218 Item::Fn(i) => {
219 let mut call_lines = Vec::new();
220 let mut value_lines = Vec::new();
221 for a in ras { match a {
222 RvnItemAttr::ForCall(c) => { call_lines.push(c); },
223 RvnItemAttr::ForValues(l) => { value_lines.push(l); },
224 a => panic!(
225 "Unexpected {:?} on '{}'",
226 a,
227 i.sig.ident
228 ),
229 }}
230 let c = RccCommand::AnnotateMulti(value_lines, call_lines, i);
231 (Some(c), None)
232 }
233 item => panic!("Can't use #[annotate_multi(..)] on {:?}", item),
234 }
235 Assume => match item {
236 Item::Fn(i) => {
237 let mut rules = Vec::new();
238 for a in ras { match a {
239 RvnItemAttr::InstRule(r) => { rules.push(r); },
240 a => panic!(
241 "Unexpected {:?} on '{}'",
242 a,
243 i.sig.ident
244 ),
245 }}
246 let c = RccCommand::Assume(rules, i);
247 (Some(c), None)
248 }
249 item => panic!("Can't use #[assume] on {:?}", item),
250 }
251 AssumeFor(call) => match item {
252 Item::Fn(i) => {
253 let c = RccCommand::AssumeFor(call, i);
254 (Some(c), None)
255 }
256 item => panic!("Can't use #[assume(..)] on {:?}", item),
257 }
258 Declare => Self::mk_declare_define(Vec::from(ras), item, false),
259 Define => Self::mk_declare_define(Vec::from(ras), item, true),
260 Falsify => Self::mk_goal(Vec::from(ras), item, false),
261 Import => match item {
262 Item::Use(i) => {
263 let c = RccCommand::Import(i.clone());
264 (Some(c), Some(Item::Use(i)))
265 }
266 item => panic!("Can't use #[import] on {:?}", item),
267 }
268 InstRule(_) =>
269 panic!("#[for_type(..)] should only appear under #[assume]"),
270 Phantom =>
271 panic!("#[phantom] should only appear under #[declare] or #[define]"),
272 Verify => Self::mk_goal(Vec::from(ras), item, true),
273 a => todo!("other attrs for from_item: {:?}", a),
274 }
275 }
276
277 fn from_toplevel_attr(attr: &Attribute) -> Vec<Self> {
278 let mut out = Vec::new();
279 match &attr.meta {
280 Meta::List(l) if l.path.segments.len() == 1 => {
281 match path_to_one_str(&l.path).as_deref() {
282 Some("declare_types") => {
283 let parser =
284 Punctuated
285 ::<Path,syn::Token![,]>
286 ::parse_separated_nonempty;
287 let types = parser
288 .parse2(l.tokens.clone())
289 .expect("the #[declare_types(..)] attribute expects one or more type names as arguments");
290
291 for mut p in types.into_iter() {
292 let seg = p.segments.pop().unwrap().into_value();
293 let arity = match seg.arguments {
294 PathArguments::None => 0,
295 PathArguments::AngleBracketed(a) => a.args.len(),
296 PathArguments::Parenthesized(..) => panic!("declared types should get angle-bracketed arguments <..>, but {} got parenthesized arguments", seg.ident),
297 };
298
299 out.push(RccCommand::DeclareType(seg.ident, arity));
300 }
301 }
302 _ => {}
303 }
304 }
305 _ => {}
306 }
307 out
308 }
309
310 fn extract_from_toplevel_attrs(attrs: &mut Vec<Attribute>) -> Vec<Self> {
311 let mut out = Vec::new();
312 attrs.retain_mut(|attr| {
313 let mut cs = Self::from_toplevel_attr(attr);
314 if cs.len() > 0 {
315 out.append(&mut cs);
316 false
317 } else {
318 true
319 }
320 });
321 out
322 }
323
324 fn extract_from_items(items: &mut Vec<Item>) -> Vec<Self> {
325 let mut commands: Vec<Self> = Vec::new();
326 let mut items_out: Vec<Item> = Vec::new();
327 for item in items.clone() {
328 let (c,i) = Self::from_item(item);
329 if let Some(c) = c {
330 commands.push(c);
331 }
332 if let Some(i) = i {
333 items_out.push(i);
334 }
335 }
336 *items = items_out;
337 commands
338 }
339}
340
341#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
342enum GenMode {
343 Check,
344 Export,
345}
346
347fn generate_stmts(commands: &Vec<RccCommand>, mode: GenMode) -> Vec<Stmt> {
348 let mut out = Vec::new();
349 for c in commands {
350 match c {
351 RccCommand::DeclareType(ident, arity) => {
352 let ident_str = quote!{#ident}.to_string();
353 let s: Stmt = syn::parse2(quote! {
354 rcc.reg_toplevel_type(#ident_str, #arity);
355 }).unwrap();
356 out.push(s);
357 }
358 RccCommand::Annotate(call_str, item_fn) => {
359 let item_str = quote!{ #item_fn }.to_string();
360 let s: Stmt = syn::parse2(quote! {
361 rcc.reg_fn_annotate(#call_str, #item_str).unwrap();
362 }).unwrap();
363 out.push(s);
364 }
365 RccCommand::AnnotateMulti(value_strs, call_strs, item_fn) => {
366 let item_str = quote!{ #item_fn }.to_string();
367 let s: Stmt = syn::parse2(quote! {
368 rcc.reg_fn_annotate_multi(
369 [#(#value_strs),*],
370 [#(#call_strs),*],
371 #item_str
372 ).unwrap();
373 }).unwrap();
374 out.push(s);
375 }
376 RccCommand::Assume(rules, item_fn) => {
377 let item_str = quote!{ #item_fn }.to_string();
378 let s: Stmt = syn::parse2(quote! {
379 rcc.reg_fn_assume([#(#rules),*], #item_str);
380 }).unwrap();
381 out.push(s);
382 }
383 RccCommand::AssumeFor(call_str, item_fn) => {
384 let item_str = quote!{ #item_fn }.to_string();
385 let s: Stmt = syn::parse2(quote! {
386 rcc.reg_fn_assume_for(#call_str, #item_str);
387 }).unwrap();
388 out.push(s);
389 }
390 RccCommand::Declare(item, _is_phantom) => {
391 let item_str = quote!{ #item }.to_string();
392 let s: Stmt = syn::parse2(quote! {
393 rcc.reg_item_declare(#item_str);
394 }).unwrap();
395 out.push(s)
396 }
397 RccCommand::Define(item, _is_phantom, is_rec) => {
398 let item_str = quote!{ #item }.to_string();
399 let s: Stmt = syn::parse2(quote! {
400 rcc.reg_item_define(#item_str, #is_rec);
401 }).unwrap();
402 out.push(s)
403 }
404 RccCommand::Import(item) => {
405 let segs = use_tree_to_path(item.tree.clone());
406 let mut punct = Punctuated::<PathSegment, Token![::]>::new();
407 for s in segs {
408 punct.push(s);
409 }
410 let path = Path { leading_colon: None, segments: punct };
411 let path_str = quote!{ #path }.to_string();
412 let s: Stmt = syn::parse2(quote! {
413 if rcc.touch_new_path(#path_str) {
414 #path::ravencheck_exports::apply_exports(&mut rcc);
415 }
416 }).unwrap();
417 out.push(s);
418 }
419 RccCommand::Goal(should_be_valid, item_fn) => {
420 match mode {
421 GenMode::Check => {
422 let item_fn_str = quote!{ #item_fn }.to_string();
423 let s: Stmt = syn::parse2(quote! {
424 rcc.reg_fn_goal(#should_be_valid, #item_fn_str);
425 }).unwrap();
426 out.push(s);
427 }
428 GenMode::Export => {}
429 }
430 }
431 }
432 }
433 out
434}
435
436fn use_tree_to_path(t: UseTree) -> Vec<PathSegment> {
437 match t {
438 UseTree::Path(p) => {
439 let i = p.ident.clone();
440 let mut rest = use_tree_to_path(*p.tree);
441 rest.insert(0, PathSegment{
442 ident: i,
443 arguments: PathArguments::None,
444 });
445 rest
446 },
447 UseTree::Glob(..) => Vec::new(),
448 UseTree::Group(..) => Vec::new(),
449 UseTree::Name(..) => Vec::new(),
450 t => panic!("cannot #[import] use-tree with {:?} node in it", t),
451 }
452}
453
454#[proc_macro_attribute]
455pub fn export_module(attrs: TokenStream, input: TokenStream) -> TokenStream {
456 process_module(attrs, input, true)
457}
458
459#[proc_macro_attribute]
460pub fn check_module(attrs: TokenStream, input: TokenStream) -> TokenStream {
461 process_module(attrs, input, false)
462}
463
464fn process_module(
465 attrs: TokenStream,
466 input: TokenStream,
467 export: bool,
468) -> TokenStream {
469
470 let cratename: Ident = if attrs.is_empty() {
474 Ident::new("ravencheck", Span::call_site())
475 } else {
476 let parser = Ident::parse_any;
479 parser.parse(attrs).expect("parse crate name")
480 };
481
482 let mut m: ItemMod = match syn::parse(input).expect("parse input") {
483 Item::Mod(m) => m,
484 i => panic!(
485 "'check_module' macro should only be applied to a module, but it was applied to {:?}",
486 i,
487 ),
488 };
489
490 let mut commands =
492 RccCommand::extract_from_toplevel_attrs(&mut m.attrs);
493 match &mut m.content {
497 Some((_,items)) => {
498
499 commands.append(
501 &mut RccCommand::extract_from_items(items)
502 );
503
504 let mut test_stmts: Vec<Stmt> =
505 generate_stmts(&commands, GenMode::Check);
506 test_stmts.push(
507 syn::parse2(quote!{
508 rcc.check_goals();
509 }).unwrap()
510 );
511
512 let test: ItemFn = syn::parse((quote! {
513 #[test]
514 fn check_properties() {
515 let mut rcc = Rcc::new();
516
517 #(#test_stmts)*
519 }
520 }).into()).unwrap();
521
522 let test_s = Item::Fn(test);
523
524 let test_mod = quote! {
525 #[cfg(test)]
526 mod ravencheck_tests {
527 use #cratename::Rcc;
528
529 #test_s
530 }
531 };
532
533 items.push(syn::parse(test_mod.into()).expect("parse test mod"));
537
538 if export {
539 let export_stmts: Vec<Stmt> =
540 generate_stmts(&commands, GenMode::Export);
541
542 let export_mod = quote! {
543 pub mod ravencheck_exports {
544 use #cratename::Rcc;
545
546 pub fn apply_exports(rcc: &mut Rcc) {
547 #(#export_stmts)*
548 }
549 }
550 };
551
552 items.push(syn::parse(export_mod.into()).expect("parse export mod"));
555 }
556 }
557 None => {}
558 }
559 let out = quote! {
560 #m
561 };
562 out.into()
563}