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#[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}