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