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}