safety_macro/
lib.rs

1use proc_macro::TokenStream;
2use safety_parser::{
3    configuration::config_exists, proc_macro2::TokenStream as TokenStream2, quote::quote,
4    safety::SafetyAttrArgs as AttrArgs, split_attrs::split_attrs_and_rest, syn,
5};
6
7/// Tag SPs on an unsafe function item, or discharge SPs on an expression.
8///
9/// # Syntax Example
10///
11/// ```
12/// #![feature(stmt_expr_attributes)]
13/// #![feature(proc_macro_hygiene)]
14/// #![feature(register_tool)]
15/// #![register_tool(rapx)]
16/// # use safety_macro::safety;
17///
18/// // Tag SPs:
19/// #[safety { SP1 }] unsafe fn foo() {}
20/// #[safety { SP1, SP2 }] unsafe fn bar() {}
21///
22/// // Discharge SPs:
23/// #[safety { SP1 }] unsafe { foo() };
24/// #[safety { SP1: "reason" }] unsafe { foo() };
25/// #[safety { SP1, SP2: "shared reason" }] unsafe { bar() };
26/// #[safety { SP1: "reason1"; SP2: "reason2" }] unsafe { bar() };
27/// ```
28#[proc_macro_attribute]
29pub fn safety(attr: TokenStream, item: TokenStream) -> TokenStream {
30    let mut ts = TokenStream2::new();
31
32    // add registered tool attr
33    let tool_attr = {
34        let attr = TokenStream2::from(attr.clone());
35        quote! { #[rapx::inner(#attr)] }
36    };
37    ts.extend(tool_attr);
38
39    let input = split_attrs_and_rest(item.into());
40    if !input.gen_doc {
41        // no need to generate docs on expressions
42        ts.extend(input.attrs);
43        ts.extend(input.rest);
44        return ts.into();
45    }
46
47    // push doc attrs first
48    ts.extend(input.attrs);
49
50    let attr_args: AttrArgs = syn::parse(attr).unwrap();
51    // push generated doc if available
52    if config_exists() {
53        for tag in &attr_args.args {
54            ts.extend(tag.gen_doc());
55        }
56    }
57
58    // push rest tokens
59    ts.extend(input.rest);
60    ts.into()
61}