panic_custom_proc_macros/lib.rs
1#![allow(unreachable_code)]
2#![no_std]
3
4//! Small procedural macro crate for custom panic functions.
5//!
6//! This crate provides a `define_panic` procedural macro, which transforms a given function into a panic handler.
7//! No closures are allowed with this macro.
8//!
9//! # Usage
10//!
11//! To define a custom panic handler, annotate a function with `#[panic_handler]` macro.
12//! The function must adhere to the following signature:
13//! `fn _some_name_(info: &PanicInfo) -> !`.
14//!
15//! # Examples
16//!
17//! ```rust
18//! use my_panic_macro::define_panic;
19//!
20//! #[panic_handler]
21//! fn my_panic_function(info: &PanicInfo) -> ! {
22//! // Custom panic handling logic
23//! }
24//! ```
25//!
26//! # Limitations
27//!
28//! - This macro only accepts functions as input. Closures are not allowed.
29//! - The panic handler function must diverge, i.e., it must return `!`.
30//! - Ensure that the panic handler function is properly defined and handles panics safely to avoid undefined behavior.
31//!
32//! # See Also
33//!
34//! - [`core::panic::PanicInfo`](https://doc.rust-lang.org/core/panic/struct.PanicInfo.html): Struct representing information about a panic.
35//!
36//! # Reference
37//!
38//! - [The Rust Book - Panic Handling](https://doc.rust-lang.org/book/ch09-03-to-panic-or-not-to-panic.html)
39
40use proc_macro::TokenStream;
41use quote::quote;
42use syn::{parse_macro_input, FnArg, ItemFn, ReturnType, Type};
43
44/// Defines the given function as a panic handler.
45///
46/// This macro only accepts a function as an input. All functions must
47/// follow the same rule:
48/// `fn _some_name_(info: &PanicInfo) -> !;`
49///
50/// # Examples
51///
52/// ```rust
53/// use my_panic_macro::define_panic;
54///
55/// #[panic_handler]
56/// fn my_panic_function(info: &PanicInfo) -> ! {
57/// // Custom panic handling logic
58/// }
59/// ```
60#[proc_macro_attribute]
61pub fn define_panic(_attr: TokenStream, input: TokenStream) -> TokenStream {
62 let input_fn = parse_macro_input!(input as ItemFn);
63
64 // Extracting
65 let vis = &input_fn.vis;
66 let attrs = &input_fn.attrs;
67 let block = &input_fn.block;
68 let inputs = &input_fn.sig.inputs;
69 let output = &input_fn.sig.output;
70
71 // Ensuring the function has the correct signature.
72 if let FnArg::Typed(arg) = inputs.first().unwrap() {
73 if let Type::Reference(reference) = &*arg.ty {
74 if let Type::Path(type_path) = &*reference.elem {
75 if let Some(ident) = type_path.path.get_ident() {
76 if ident != "PanicInfo" {
77 return syn::Error::new_spanned(
78 &input_fn.sig,
79 "The parameter of the panic handler function must be of type `&PanicInfo`.",
80 )
81 .to_compile_error()
82 .into();
83 }
84 }
85 }
86 } else {
87 return syn::Error::new_spanned(
88 &input_fn.sig,
89 "The parameter must be a reference of type `PanicInfo` ",
90 )
91 .to_compile_error()
92 .into();
93 }
94 } else {
95 return syn::Error::new_spanned(
96 &input_fn.sig,
97 "The parameter of type `&PanicInfo` is not found.",
98 )
99 .to_compile_error()
100 .into();
101 }
102
103 if let ReturnType::Type(_, ty) = output {
104 match ty.as_ref() {
105 Type::Never(_) => (),
106 _ => {
107 return syn::Error::new_spanned(
108 output,
109 "The panic handler function must diverge (return `!`).",
110 )
111 .to_compile_error()
112 .into();
113 },
114 }
115 }
116
117
118 let new_fn = quote! {
119 #(#attrs)*
120 #vis
121 #[panic_handler]
122 fn panic(_: &::core::panic::PanicInfo) -> ! {
123 unsafe {
124 #block
125 }
126 }
127 };
128
129 new_fn.into()
130}
131