safety_macro/
lib.rs

1use proc_macro::TokenStream;
2use safety_parser::{
3    proc_macro2::{Ident, TokenStream as TokenStream2},
4    property_attr::{
5        FnItem, SafetyAttrArgs, parse_inner_attr_from_tokenstream,
6        property::{Kind, PropertyName},
7    },
8    quote::{quote, quote_spanned},
9    syn::{parse::Parser, punctuated::Punctuated, *},
10};
11
12fn generate(
13    kind: Kind,
14    property: PropertyName,
15    attr: TokenStream,
16    item: TokenStream,
17) -> TokenStream {
18    let item = parse_macro_input!(item as ItemFn);
19    let attr = parse_macro_input!(attr as SafetyAttrArgs);
20
21    let named_args_set = attr.into_named_args_set(kind, property);
22    let doc_comments = named_args_set.generate_doc_comments();
23    let safety_tool_attr = named_args_set.generate_safety_tool_attribute();
24
25    let mut fn_item = FnItem::new(item);
26    fn_item.insert_attributes_to_the_back(doc_comments);
27    fn_item.insert_attributes_to_the_back(safety_tool_attr);
28    fn_item.into_token_stream().into()
29}
30
31macro_rules! kind_property {
32    ($f:ident: $kind:ident, $property:ident) => {
33        #[proc_macro_attribute]
34        #[allow(non_snake_case)]
35        pub fn $f(attr: TokenStream, item: TokenStream) -> TokenStream {
36            generate(Kind::$kind, PropertyName::$property, attr, item)
37        }
38    };
39
40    (
41      $(
42        $f:ident: $kind:ident, $property:ident
43      );+ $(;)?
44    ) => {
45        $( kind_property!($f: $kind, $property); )+
46    };
47}
48
49kind_property! {
50    Precond_Align: Precond, Align;
51    Precond_Size: Precond, Size;
52    Precond_NoPadding: Precond, NoPadding;
53    Precond_NonNull: Precond, NonNull;
54    Precond_Allocated: Precond, Allocated;
55    Precond_InBound: Precond, InBound;
56    Precond_NonOverlap: Precond, NonOverlap;
57    Precond_ValidNum: Precond, ValidNum;
58    Precond_ValidString: Precond, ValidString;
59    Precond_ValidCStr: Precond, ValidCStr;
60    Precond_Init: Precond, Init;
61    Precond_Unwrap: Precond, Unwrap;
62    Precond_Typed: Precond, Typed;
63    Precond_Owning: Precond, Owning;
64    Precond_Alias: Precond, Alias;
65    Precond_Alive: Precond, Alive;
66    Precond_Pinned: Precond, Pinned;
67    Precond_NonVolatile: Precond, NonVolatile;
68    Precond_Opened: Precond, Opened;
69    Precond_Trait: Precond, Trait;
70    Precond_Unreachable: Precond, Unreachable;
71    Precond_ValidPtr: Precond, ValidPtr;
72    Precond_Deref: Precond, Deref;
73    Precond_Ptr2Ref: Precond, Ptr2Ref;
74    Precond_Layout: Precond, Layout;
75
76    Hazard_ValidString: Hazard, ValidString;
77    Hazard_Init: Hazard, Init;
78    Hazard_Alias: Hazard, Alias;
79    Hazard_Pinned: Hazard, Pinned;
80    Hazard_Ptr2Ref: Hazard, Ptr2Ref;
81
82    Option_Size: Option, Size;
83    Option_Init: Option, Init;
84    Option_Trait: Option, Trait;
85}
86
87/// Pub use a attribute by stripping the prefix.
88#[proc_macro]
89pub fn pub_use(tokens: TokenStream) -> TokenStream {
90    Punctuated::<Ident, token::Comma>::parse_terminated
91        .parse(tokens.clone())
92        .unwrap_or_else(|err| panic!("{tokens:?} is not a comma separated idents: {err}"))
93        .iter()
94        .map(|ident| {
95            let name = ident.to_string();
96            let pos = name.find('_').unwrap_or_else(|| panic!("{name} doesn't contain `_`"));
97            let property_name = &name[pos + 1..];
98            let span = ident.span();
99            let property_ident = Ident::new(property_name, span);
100            quote_spanned! { span =>
101                pub use ::safety_macro::#ident as #property_ident;
102            }
103        })
104        .collect::<TokenStream2>()
105        .into()
106}
107
108#[proc_macro_attribute]
109#[allow(non_snake_case)]
110pub fn Memo(attr: TokenStream, item: TokenStream) -> TokenStream {
111    generate(Kind::Memo, PropertyName::Unknown, attr, item)
112}
113
114#[proc_macro_attribute]
115pub fn discharges(attr: TokenStream, item: TokenStream) -> TokenStream {
116    let property = parse_inner_attr_from_tokenstream(attr.into());
117    let discharge_attr = property.generate_discharge_attr();
118    let item = TokenStream2::from(item);
119    quote! {
120        #discharge_attr
121        #item
122    }
123    .into()
124}