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
#![cfg_attr(feature = "print_attributes", feature(proc_macro_diagnostic))] #![cfg_attr(feature = "print_attributes", feature(proc_macro_span))] extern crate ansi_term; extern crate hacspec_util; extern crate quote; extern crate serde; extern crate serde_json; extern crate syn; #[cfg(feature = "print_attributes")] use ansi_term::Colour::{Blue, Green, Purple, Yellow}; #[cfg(feature = "print_attributes")] use hacspec_util::{syn_sig_to_reduced, Signature}; #[cfg(feature = "print_attributes")] use proc_macro::*; #[cfg(feature = "print_attributes")] use quote::quote; #[cfg(feature = "print_attributes")] use std::collections::{HashMap, HashSet}; #[cfg(feature = "print_attributes")] use std::fs::OpenOptions; #[cfg(feature = "print_attributes")] use syn::{parse_macro_input, spanned::Spanned, ItemFn}; #[cfg(feature = "print_attributes")] const ITEM_LIST_LOCATION: &str = "./allowed_item_list.json"; macro_rules! declare_attribute { ($id:ident, $key: expr, $msg: expr, $doc:tt, $allowed_item: expr) => { #[cfg(feature="print_attributes")] #[doc=$doc] #[proc_macro_attribute] pub fn $id(attr: TokenStream, item: TokenStream) -> TokenStream { let item_copy = proc_macro2::TokenStream::from(item.clone()); let func = parse_macro_input!(item as ItemFn); let mut attr_args_iter = attr.into_iter(); let _impl_type_name: Option<String> = attr_args_iter.next().map(|arg| { format!("{}", arg) }); let _is_generic: bool = attr_args_iter.next().map_or(false, |_| { let _ = attr_args_iter.next().expect("Error 7"); true }); if cfg!(feature = "print_attributes") { if $allowed_item { let file = OpenOptions::new() .read(true) .open(ITEM_LIST_LOCATION) .expect("Error 1"); let key_s = String::from($key); let mut item_list : HashMap<String, HashSet<Signature>> = serde_json::from_reader(&file).unwrap(); let item_list_type = match item_list.get_mut(&key_s) { None => { item_list.insert(key_s.clone(), HashSet::new()); item_list.get_mut(&key_s).expect("Error 2") } Some(items) => items }; item_list_type.insert(syn_sig_to_reduced(&func.sig)); } Diagnostic::new( Level::Note, format!( "{}: {} {}", $msg, Green.paint(format!("{}", func.sig.ident)), { let file = func.sig.span().unwrap().source_file().path(); let start = func.sig.span().start(); format!( "in file {}, line {}", Yellow.paint(file.to_str().expect("Error 9")), Yellow.paint(format!("{}", start.line)) ) } ), ) .emit() } let doc_msg = format!("_{}_\n", $doc); let output = quote! { #[doc=#doc_msg] #item_copy }; output.into() } }; } declare_attribute!( in_hacspec, "in_hacspec", Blue.paint("Function in hacspec"), "This function is within the hacspec subset of Rust: its signature and body use only hacspec constructs and call functions whose signatures are in hacspec.", true ); declare_attribute!( unsafe_hacspec, "unsafe_hacspec", Purple.paint("Unsafe hacspec function"), "This function can be called from hacspec programs but its body features Rust constructs that are not part of hacspec", true ); declare_attribute!( not_hacspec, "not_hacspec", Yellow.paint("Function not in hacspec"), "Function that is not part of the language but is offered as a helper for tests, etc.", false );