panic_custom/
lib.rs

1
2#![allow(unreachable_code, internal_features, improper_ctypes)]
3#![feature(used_with_arg, core_intrinsics, linkage, tuple_trait, doc_cfg)]
4#![no_std]
5
6//! Small crate for custom panicking behavior, primarily designed for embedded or `no_std` projects.
7//!
8//! By default, it's behavior for panics is to halt in both release and debug mode.
9//! This crate, `panic_custom`, allows developers to customize this behavior by providing a custom
10//! panic handler function.
11//!
12//! # Usage
13//!
14//! The crate provides two main ways to define custom panicking behavior:
15//!
16//! - Using the `define_panic!` macro with a closure argument.
17//! - Using the `#[define_panic]` procedural macro.
18//!
19//! ## Using `define_panic!` Macro
20//!
21//! The `define_panic!` macro allows you to define custom panicking behavior by passing a closure as an argument.
22//!
23//! ```rust
24//! use panic_custom::define_panic;
25//!
26//! const MY_CUSTOM_CONSTANT: usize = 0;
27//!
28//! define_panic!(|info| {
29//!     let a = &MY_CUSTOM_CONSTANT;
30//!     let b = MY_CUSTOM_CONSTANT;
31//!
32//!     42 // The return type is not important
33//! });
34//! ```
35//!
36//! ## Using `#[define_panic]` Procedural Macro
37//!
38//! The `#[define_panic]` procedural macro allows you to define a custom panic handler function.
39//! To use this macro, enable the `proc_macros` feature and include `features = "proc_macros"` in your `Cargo.toml`.
40//!
41//! ```toml
42//! [dependencies]
43//! panic_custom = { version = "0.1", features = ["proc_macros"] }
44//! ```
45//!
46//! ```rust
47//! use panic_custom::define_panic;
48//! use core::panic::PanicInfo;
49//!
50//! #[define_panic]
51//! fn my_panic(info: &PanicInfo) -> ! {
52//!     loop {}
53//! }
54//! ```
55//!
56//! # Features
57//!
58//! - `proc_macros`: Enables procedural macros for custom panic handling.
59//!
60//! - `abort_on_debug`: Sets the default behavior to abort on panic in debug mode. By default, the crate halts on panic in debug mode.
61//!
62//! - `abort_on_release`: Sets the default behavior to abort on panic in release mode. By default, the crate halts on panic in release mode.
63//!
64//! # Note
65//!
66//! Ensure that custom panic handlers are implemented safely to avoid undefined behavior. Incorrect panic handling logic may lead to unexpected program behavior.
67//!
68//! # See Also
69//!
70//! - [`core::panic::PanicInfo`](https://doc.rust-lang.org/core/panic/struct.PanicInfo.html): Struct representing information about a panic.
71//!
72//! # Reference
73//!
74//! - [The Rust Book - Panic Handling](https://doc.rust-lang.org/book/ch09-03-to-panic-or-not-to-panic.html)
75//!
76//! This crate provides flexibility in defining custom panic handling behavior, empowering developers to tailor their applications' panic behavior to their specific 
77//! requirements, especially in embedded or `no_std` projects.
78
79#[cfg(feature = "proc_macros")]
80#[doc(cfg(feature = "proc_macros"))]
81pub use panic_custom_proc_macros::define_panic;
82
83#[cfg(not(feature = "proc_macros"))]
84#[doc(hidden)]
85pub mod no_macro {
86    use core::sync::atomic::{self, Ordering};
87
88    /// This macro defines the behavior of the panic handler when procedural macros are not enabled.
89    ///
90    /// To define a custom panic handler, provide the custom panic closure or function as an argument to the macro.
91    /// The function can return anything and can be defined as any closure.
92    ///
93    /// ```rust
94    /// use panic_custom::define_panic;
95    ///
96    /// const MY_CUSTOM_CONSTANT: usize = 0;
97    ///
98    /// define_panic!(|info| {
99    ///     let a = &MY_CUSTOM_CONSTANT;
100    ///     let b = MY_CUSTOM_CONSTANT;
101    ///
102    ///     loop {}
103    /// });
104    /// ```
105    ///
106    /// It is not neccesary for a handler closure to loop, the macro will make it return ! for you.
107    /// This is a regular macro not a procedural one. For using the procedural macro with the same
108    /// name you should add a feature 'proc_macros' to your crate.
109    ///
110     /// ```rust
111    /// use panic_custom::define_panic;
112    ///
113    /// define_panic!(|info| {
114    ///     let a = 42;
115    ///
116    ///     42 // Still works + will be optimized by a compiler.
117    /// });
118    /// ```
119    #[macro_export]
120    #[doc(cfg(not(feature = "proc_macros")))]
121    macro_rules! define_panic {
122        ($panic_fn:expr) => {
123            #[inline(never)]
124            #[panic_handler]
125            fn panic(info: &core::panic::PanicInfo) -> ! {
126                // Custom defined function
127                unsafe { $panic_fn(info); }
128
129                $crate::no_macro::__default_panic()
130            }
131        };
132        () => {
133            #[inline(never)]
134            #[panic_handler]
135            fn panic(_: &core::panic::PanicInfo) -> ! {
136                $crate::no_macro::__default_panic()
137            }
138        }
139    }
140
141    #[doc(hidden)]
142    #[inline(always)]
143    pub fn __default_panic() -> ! {
144        #[cfg(not(debug_assertions))]
145        {
146            #[cfg(not(feature = "abort_on_release"))] // Aborts.
147            {
148                loop {
149                    atomic::compiler_fence(Ordering::SeqCst); // Halting on debug.
150                }
151            }
152
153            #[cfg(feature = "abort_on_release")] // Halts.
154            {
155                core::intrinsics::abort();
156            }
157        } 
158
159        #[cfg(debug_assertions)]
160        {
161            #[cfg(not(feature = "abort_on_debug"))] // Aborts.
162            {
163                loop {
164                    atomic::compiler_fence(Ordering::SeqCst); // Halting on debug.
165                }
166            }
167
168            #[cfg(feature = "abort_on_debug")] // Halts.
169            {
170                core::intrinsics::abort();
171            }
172        }
173    }
174}
175
176#[test]
177fn some_panic() {
178    use crate::define_panic;
179
180    define_panic!(|_| {
181        loop {}
182    });
183
184    panic!();
185}