1use proc_macro::TokenStream;
2
3use syn::{parse_macro_input, ItemFn};
4
5#[proc_macro_attribute]
13pub fn catch(_attr: TokenStream, item: TokenStream) -> TokenStream {
14 let input = parse_macro_input!(item as ItemFn);
15 let sig = input.sig;
16 let block = input.block;
17
18 TokenStream::from(quote::quote! {
19 #sig {
20 let (__errata_tx, __errata_rx) = std::sync::mpsc::channel();
21
22 let __errata_old_hook = std::panic::take_hook();
23 std::panic::set_hook(Box::new(move |info| {
24 let location = match info.location() {
25 Some(l) => format!(" (at {}:{}:{})", l.file(), l.line(), l.column()),
26 None => String::new(),
27 };
28
29 let _ = __errata_tx.send(location);
30 }));
31
32 let __errata_result = std::panic::catch_unwind(|| {
33 #block
34 });
35
36 match __errata_result {
37 Ok(_) => return,
38 Err(e) => {
39 let location = match __errata_rx.recv() {
40 Ok(l) => l,
41 Err(e) => {
42 eprintln!("internal errata error (failed to recieve location): {e}");
43 String::new()
44 }
45 };
46
47 if let Some(e) = e.downcast_ref::<errata::ErrataPanic>() {
48 eprintln!("{e}");
49 } else if let Some(e) = e.downcast_ref::<&str>() {
50 eprintln!("error{location}: {e}");
51
52 let bt = std::backtrace::Backtrace::capture();
53
54 use std::backtrace::BacktraceStatus as BtS;
55 match bt.status() {
56 BtS::Captured => eprintln!("{bt}"),
57 BtS::Disabled => eprintln!("run with `RUST_BACKTRACE=1` environment variable to display a backtrace"),
58 _ => {}
59 }
60 } else if let Some(e) = e.downcast_ref::<String>() {
61 eprintln!("error{location}: {e}");
62
63 let bt = std::backtrace::Backtrace::capture();
64
65 use std::backtrace::BacktraceStatus as BtS;
66 match bt.status() {
67 BtS::Captured => eprintln!("{bt}"),
68 BtS::Disabled => eprintln!("run with `RUST_BACKTRACE=1` environment variable to display a backtrace"),
69 _ => {}
70 }
71 } else {
72 eprintln!("Unhandled error(location)");
73 let bt = std::backtrace::Backtrace::capture();
74
75 use std::backtrace::BacktraceStatus as BtS;
76 match bt.status() {
77 BtS::Captured => eprintln!("{bt}"),
78 BtS::Disabled => eprintln!("run with `RUST_BACKTRACE=1` environment variable to display a backtrace"),
79 _ => {}
80 }
81 }
82
83 std::process::exit(1);
84 }
85 }
86 }
87 })
88}