hacspec_attributes/
lib.rs1#![cfg_attr(feature = "print_attributes", feature(proc_macro_diagnostic))]
2#![cfg_attr(feature = "print_attributes", feature(proc_macro_span))]
3
4extern crate ansi_term;
5extern crate hacspec_util;
6extern crate quote;
7extern crate serde;
8extern crate serde_json;
9extern crate syn;
10
11#[cfg(feature = "print_attributes")]
12use ansi_term::Colour::{Blue, Green, Purple, Yellow};
13#[cfg(feature = "print_attributes")]
14use hacspec_util::{syn_sig_to_reduced, Signature};
15#[cfg(feature = "print_attributes")]
16use proc_macro::*;
17#[cfg(feature = "print_attributes")]
18use quote::quote;
19#[cfg(feature = "print_attributes")]
20use std::collections::{HashMap, HashSet};
21#[cfg(feature = "print_attributes")]
22use std::fs::OpenOptions;
23#[cfg(feature = "print_attributes")]
24use syn::{parse_macro_input, spanned::Spanned, ItemFn};
25#[cfg(feature = "print_attributes")]
26const ITEM_LIST_LOCATION: &str = "./allowed_item_list.json";
27
28macro_rules! declare_attribute {
29 ($id:ident, $key: expr, $msg: expr, $doc:tt, $allowed_item: expr) => {
30 #[cfg(feature="print_attributes")]
31 #[doc=$doc]
32 #[proc_macro_attribute]
33 pub fn $id(attr: TokenStream, item: TokenStream) -> TokenStream {
34 let item_copy = proc_macro2::TokenStream::from(item.clone());
35 let func = parse_macro_input!(item as ItemFn);
36 let mut attr_args_iter = attr.into_iter();
37 let _impl_type_name: Option<String> = attr_args_iter.next().map(|arg| {
38 format!("{}", arg)
39 });
40 let _is_generic: bool = attr_args_iter.next().map_or(false, |_| {
41 let _ = attr_args_iter.next().expect("Error 7");
42 true
43 });
44 if cfg!(feature = "print_attributes") {
45 if $allowed_item {
46 let file = OpenOptions::new()
47 .read(true)
48 .open(ITEM_LIST_LOCATION)
49 .expect("Error 1");
50 let key_s = String::from($key);
51 let mut item_list : HashMap<String, HashSet<Signature>> = serde_json::from_reader(&file).unwrap();
52 let item_list_type = match item_list.get_mut(&key_s) {
53 None => {
54 item_list.insert(key_s.clone(), HashSet::new());
55 item_list.get_mut(&key_s).expect("Error 2")
56 }
57 Some(items) => items
58 };
59 item_list_type.insert(syn_sig_to_reduced(&func.sig));
60 }
61 Diagnostic::new(
62 Level::Note,
63 format!(
64 "{}: {} {}",
65 $msg,
66 Green.paint(format!("{}", func.sig.ident)),
67 {
68 let file = func.sig.span().unwrap().source_file().path();
69 let start = func.sig.span().start();
70 format!(
71 "in file {}, line {}",
72 Yellow.paint(file.to_str().expect("Error 9")),
73 Yellow.paint(format!("{}", start.line))
74 )
75 }
76 ),
77 )
78 .emit()
79 }
80 let doc_msg = format!("_{}_\n", $doc);
81 let output = quote! {
82 #[doc=#doc_msg]
83 #item_copy
84 };
85 output.into()
86 }
87 };
88}
89
90declare_attribute!(
91 in_hacspec,
92 "in_hacspec",
93 Blue.paint("Function in hacspec"),
94 "This function is within the hacspec subset of Rust: its signature and body use only hacspec constructs and
95 call functions whose signatures are in hacspec.",
96 true
97);
98declare_attribute!(
99 unsafe_hacspec,
100 "unsafe_hacspec",
101 Purple.paint("Unsafe hacspec function"),
102 "This function can be called from hacspec programs but its body features Rust constructs that are not part of hacspec",
103 true
104);
105declare_attribute!(
106 not_hacspec,
107 "not_hacspec",
108 Yellow.paint("Function not in hacspec"),
109 "Function that is not part of the language but is offered as a helper for tests, etc.",
110 false
111);