1use std::string::ToString;
53use std::iter::FromIterator;
54
55extern crate proc_macro;
56use proc_macro::TokenStream;
57
58use proc_macro2::Span;
59use proc_macro2::Ident;
60
61use quote::{quote, quote_spanned};
62
63use syn::{Expr, ExprCall, ExprPath, ExprReturn};
64use syn::{Type, TypeTuple};
65use syn::punctuated::Punctuated;
66use syn::token::Paren;
67use syn::spanned::Spanned;
68use syn::visit_mut::VisitMut;
69
70use proc_macro_error::*;
71
72
73struct ReturnTypeMapper(syn::Path, bool);
74
75impl ReturnTypeMapper {
76 fn new(p: syn::Path) -> Self {
77 Self(p, false)
78 }
79
80 fn wrap_expr_with_type(&self, e: &Expr) -> Expr {
81 Expr::Call(ExprCall {
82 attrs: vec![],
83 paren_token: Paren { span: Span::call_site() },
84 func: Box::new(Expr::Path(ExprPath {
85 attrs: vec![],
86 qself: None,
87 path: self.0.clone(),
88 })),
89 args: Punctuated::from_iter(vec![e.clone()]),
90 })
91 }
92}
93
94impl syn::visit_mut::VisitMut for ReturnTypeMapper {
95 fn visit_signature_mut(&mut self, node: &mut syn::Signature) {
96 node.output = syn::ReturnType::Type(
97 syn::Token),
98 Box::new(syn::Type::Path(syn::TypePath {
99 qself: None,
100 path: self.0.clone(),
101 })),
102 );
103 }
104
105 fn visit_expr_return_mut(&mut self, expr: &mut ExprReturn) {
106 expr.expr = Some(Box::new(
107 self.wrap_expr_with_type(&*(expr.expr.as_ref().unwrap())),
108 ));
109 }
110
111
112 fn visit_expr_mut(&mut self, node: &mut Expr) {
113 if let Expr::Closure(_) = &node {
114 return;
117 }
118
119 syn::visit_mut::visit_expr_mut(self, node);
120 }
121
122 fn visit_item_fn_mut(&mut self, node: &mut syn::ItemFn) {
123 if self.1 {
124 return;
126 }
127
128 let stmts_len = node.block.stmts.len();
131 if let syn::Stmt::Expr(e) = node.block.stmts[stmts_len-1].clone() {
132 node.block.stmts[stmts_len-1] = syn::Stmt::Expr(self.wrap_expr_with_type(&e));
133 }
134
135 self.1 = true;
136 syn::visit_mut::visit_item_fn_mut(self, node);
137 }
138}
139
140
141fn gensym(s: &str, u: &uuid::Uuid) -> Ident {
142 Ident::new(
143 &*format!("{}_mygensym_{}", s, u.to_simple()),
144 Span::call_site(),
145 )
146}
147
148
149#[proc_macro_attribute]
150#[proc_macro_error]
151pub fn log_termination(_attr: TokenStream, item: TokenStream) -> TokenStream {
152 let input_i = syn::parse_macro_input!(item as syn::Item);
153
154 let input_i = match input_i {
155 syn::Item::Fn(i) => i,
156 _ => abort!(Span::call_site(),
157 "#[log_termination] item is not a function")
158 };
159
160 if !input_i.sig.inputs.is_empty() {
161 emit_warning!(input_i.sig.inputs.span(),
162 "#[log_termination] function has parameters");
163 }
164
165 let return_tp = match &input_i.sig.output {
166 syn::ReturnType::Default => Type::Tuple(TypeTuple {
167 paren_token: syn::token::Paren(Span::call_site()),
168 elems: Punctuated::default()
169 }),
170 syn::ReturnType::Type(_, tp) => *tp.clone()
171 };
172
173 let return_tp_str = "e!(#return_tp).to_string();
174 let return_tp_str_escaped = {
175 let not_alnum = regex::Regex::new(r"[^A-Za-z0-9]").unwrap();
176 let underscore = regex::Regex::new(r"_+").unwrap();
177 let no_dangerous_chars = &*not_alnum.replace_all(return_tp_str, "_").into_owned();
178
179 underscore.replace_all(no_dangerous_chars, "_").into_owned()
180 };
181 let quit_ident = gensym(&*format!("_log_termination_{}", return_tp_str_escaped), &uuid::Uuid::new_v4());
182
183 let mut main = input_i;
184
185 let new_return_tp = syn::Path {
186 leading_colon: None,
187 segments: Punctuated::from_iter(
188 std::vec)),
193 lt_token: syn::Token),
194 args: Punctuated::from_iter(vec![
195 syn::GenericArgument::Type(return_tp)
196 ]),
197 gt_token: syn::Token)
198 },
199 ),
200 ident: quit_ident.clone()
201 }
202 ]
203 )
204 };
205 let mut visitor = ReturnTypeMapper::new(new_return_tp);
206 visitor.visit_item_fn_mut(&mut main);
207
208
209 let expanded = quote_spanned! {main.span()=>
210 struct #quit_ident<T: std::ops::Try + std::process::Termination>
211 ( T );
212
213 impl<T: std::ops::Try + std::process::Termination> std::convert::From<T> for #quit_ident<T>
214 where
215 T : std::ops::Try + std::process::Termination
216 {
217 fn from(from: T) -> Self {
218 Self(from)
219 }
220 }
221
222 impl<T: std::ops::Try + std::process::Termination> std::ops::Try for #quit_ident<T>
223 where
224 T : std::ops::Try + std::process::Termination
225 {
226 type Ok = <T as std::ops::Try>::Ok;
227 type Error = <T as std::ops::Try>::Error;
228
229 fn into_result(self) -> std::result::Result<Self::Ok, Self::Error> {
230 self.0.into_result()
231 }
232
233 fn from_error(e: Self::Error) -> Self {
234 Self(<T as std::ops::Try>::from_error(e))
235 }
236
237 fn from_ok(o: Self::Ok) -> Self {
238 Self(<T as std::ops::Try>::from_ok(o))
239 }
240 }
241
242 impl<T: std::ops::Try + std::process::Termination> std::process::Termination for #quit_ident<T>
243 where
244 T : std::ops::Try + std::process::Termination,
245 <T as std::ops::Try>::Error: std::fmt::Display
246 {
247 fn report(self) -> i32 {
248 if let std::result::Result::Err(e) = self.0.into_result() {
249 log::error!("{}", e);
250 return 1;
251 }
252
253 0
254 }
255 }
256
257 #main
258 };
259
260
261 proc_macro::TokenStream::from(expanded)
262}