hacspec_attributes/
lib.rs

1#![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);