const_panic/lib.rs
1//! For panicking with formatting in const contexts.
2//!
3//! This library exists because the panic macro was stabilized for use in const contexts
4//! in Rust 1.57.0, without formatting support.
5//!
6//! All of the types that implement the [`PanicFmt`] trait can be formatted in panics.
7//!
8//! # Examples
9//!
10//! - [Basic](#basic)
11//! - [Custom Types](#custom-types)
12//!
13//! ### Basic
14//!
15//! ```compile_fail
16//! use const_panic::concat_assert;
17//!
18//! const FOO: u32 = 10;
19//! const BAR: u32 = 0;
20//! const _: () = assert_non_zero(FOO, BAR);
21//!
22//! #[track_caller]
23//! const fn assert_non_zero(foo: u32, bar: u32) {
24//! concat_assert!{
25//! foo != 0 && bar != 0,
26//! "\nneither foo nor bar can be zero!\nfoo: ", foo, "\nbar: ", bar
27//! }
28//! }
29//! ```
30//! The above code fails to compile with this error:
31//! ```text
32//! error[E0080]: evaluation of constant value failed
33//! --> src/lib.rs:20:15
34//! |
35//! 8 | const _: () = assert_non_zero(FOO, BAR);
36//! | ^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at '
37//! neither foo nor bar can be zero!
38//! foo: 10
39//! bar: 0', src/lib.rs:8:15
40//! ```
41//!
42//! When called at runtime
43//! ```should_panic
44//! use const_panic::concat_assert;
45//!
46//! assert_non_zero(10, 0);
47//!
48//! #[track_caller]
49//! const fn assert_non_zero(foo: u32, bar: u32) {
50//! concat_assert!{
51//! foo != 0 && bar != 0,
52//! "\nneither foo nor bar can be zero!\nfoo: ", foo, "\nbar: ", bar
53//! }
54//! }
55//! ```
56//! it prints this:
57//! ```text
58//! thread 'main' panicked at '
59//! neither foo nor bar can be zero!
60//! foo: 10
61//! bar: 0', src/lib.rs:6:1
62//! note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
63//!
64//! ```
65//!
66//! ### Custom types
67//!
68//! Panic formatting for custom types can be done in these ways
69//! (in increasing order of verbosity):
70//! - Using the [`PanicFmt` derive] macro
71//! (requires the opt-in `"derive"` feature)
72//! - Using the [`impl_panicfmt`] macro
73//! (requires the default-enabled `"non_basic"` feature)
74//! - Using the [`flatten_panicvals`] macro
75//! (requires the default-enabled `"non_basic"` feature)
76//! - Manually implementing the [`PanicFmt`] trait as described in its docs.
77//!
78//! This example uses the [`PanicFmt` derive] approach.
79//!
80//! ```compile_fail
81//! use const_panic::{PanicFmt, concat_panic};
82//!
83//! const LAST: u8 = {
84//! Foo{
85//! x: &[],
86//! y: Bar(false, true),
87//! z: Qux::Left(23),
88//! }.pop().1
89//! };
90//!
91//! impl Foo<'_> {
92//! /// Pops the last element
93//! ///
94//! /// # Panics
95//! ///
96//! /// Panics if `self.x` is empty
97//! #[track_caller]
98//! const fn pop(mut self) -> (Self, u8) {
99//! if let [rem @ .., last] = self.x {
100//! self.x = rem;
101//! (self, *last)
102//! } else {
103//! concat_panic!(
104//! "\nexpected a non-empty Foo, found: \n",
105//! // uses alternative Debug formatting for `self`,
106//! // otherwise this would use regular Debug formatting.
107//! alt_debug: self
108//! )
109//! }
110//! }
111//! }
112//!
113//! #[derive(PanicFmt)]
114//! struct Foo<'a> {
115//! x: &'a [u8],
116//! y: Bar,
117//! z: Qux,
118//! }
119//!
120//! #[derive(PanicFmt)]
121//! struct Bar(bool, bool);
122//!
123//! #[derive(PanicFmt)]
124//! enum Qux {
125//! Up,
126//! Down { x: u32, y: u32 },
127//! Left(u64),
128//! }
129//!
130//! ```
131//! The above code fails to compile with this error:
132//! ```text
133//! error[E0080]: evaluation of constant value failed
134//! --> src/lib.rs:57:5
135//! |
136//! 7 | / Foo{
137//! 8 | | x: &[],
138//! 9 | | y: Bar(false, true),
139//! 10 | | z: Qux::Left(23),
140//! 11 | | }.pop().1
141//! | |___________^ the evaluated program panicked at '
142//! expected a non-empty Foo, found:
143//! Foo {
144//! x: [],
145//! y: Bar(
146//! false,
147//! true,
148//! ),
149//! z: Left(
150//! 23,
151//! ),
152//! }', src/lib.rs:11:7
153//!
154//!
155//! ```
156//!
157//! # Limitations
158#![doc = crate::doc_macros::limitation_docs!()]
159//!
160//! ### Panic message length
161//!
162//! The panic message can only be up to [`MAX_PANIC_MSG_LEN`] long,
163//! after which it is truncated.
164//! The length can be configured by users,
165//! as described in [the docs for the constant][`MAX_PANIC_MSG_LEN`].
166//!
167//! # Cargo features
168//!
169//! - `"non_basic"`(enabled by default):
170//! Enables support for formatting structs, enums, and arrays.
171//! <br>
172//! Without this feature, you can effectively only format primitive types
173//! (custom types can manually implement formatting with more difficulty).
174//!
175//! - `"rust_latest_stable"`(disabled by default):
176//! Enables all the `"rust_1_*"` features.
177//!
178//! - `"rust_1_64"`(disabled by default):
179//! Enables formatting of additional items that require Rust 1.64.0 to do so.
180//!
181//! - `"rust_1_82"`(disabled by default):
182//! Enables formatting of additional items that require Rust 1.82.0 to do so.
183//!
184//! - `"rust_1_88"`(disabled by default):
185//! Enables formatting of additional items that require Rust 1.88.0 to do so.
186//!
187//! - `"derive"`(disabled by default):
188//! Enables the [`PanicFmt` derive] macro.
189//!
190//! # Plans
191//!
192//! None for now
193//!
194//! # No-std support
195//!
196//! `const_panic` is `#![no_std]`, it can be used anywhere Rust can be used.
197//!
198//! # Minimum Supported Rust Version
199//!
200//! This requires Rust 1.57.0, because it uses the `panic` macro in a const context.
201//!
202//!
203//! [`PanicFmt` derive]: derive@crate::PanicFmt
204//! [`PanicFmt`]: trait@crate::PanicFmt
205//! [`impl_panicfmt`]: crate::impl_panicfmt
206//! [`flatten_panicvals`]: crate::flatten_panicvals
207//! [`MAX_PANIC_MSG_LEN`]: crate::MAX_PANIC_MSG_LEN
208#![no_std]
209#![cfg_attr(feature = "docsrs", feature(doc_cfg))]
210#![warn(missing_docs)]
211#![deny(clippy::missing_safety_doc)]
212#![deny(clippy::shadow_unrelated)]
213#![deny(clippy::wildcard_imports)]
214// while this could be useful, this crate already tests for version compat in CI.
215// Adding a bunch of `#[clippy::msrv = ...]` everywhere would just be noise,
216// too bad there's no global way of setting clippy's msrv checker.
217#![allow(clippy::incompatible_msrv)]
218
219extern crate self as const_panic;
220
221#[macro_use]
222mod doc_macros;
223
224#[macro_use]
225mod macros;
226
227mod concat_panic_;
228
229mod debug_str_fmt;
230
231mod int_formatting;
232
233pub mod fmt;
234
235#[cfg(all(doctest, feature = "non_basic"))]
236pub mod doctests;
237
238mod panic_val;
239
240#[cfg(feature = "non_basic")]
241mod const_default;
242
243pub mod utils;
244
245/// tests doc lints with `cargo doc --features="test rust_latest_stable derive"`
246#[cfg(all(feature = "test", feature = "non_basic"))]
247pub mod test_doc_lints;
248
249#[cfg(all(test, not(feature = "test")))]
250compile_error! {r##"please use cargo test --features "test""##}
251
252#[cfg(feature = "non_basic")]
253mod slice_stuff;
254
255#[cfg(feature = "non_basic")]
256mod array_string;
257
258#[cfg(feature = "non_basic")]
259pub use crate::array_string::ArrayString;
260
261mod wrapper;
262
263mod fmt_impls {
264 #[macro_use]
265 pub(crate) mod basic_fmt_impls;
266
267 #[cfg(feature = "rust_1_64")]
268 mod rust_1_64_fmt_impls;
269
270 #[cfg(feature = "rust_1_82")]
271 mod rust_1_82_fmt_impls;
272
273 #[macro_use]
274 #[cfg(feature = "non_basic")]
275 mod option_fmt_impls;
276
277 #[cfg(feature = "non_basic")]
278 mod nonzero_impls;
279
280 #[cfg(feature = "non_basic")]
281 mod other_impls;
282
283 #[cfg(feature = "non_basic")]
284 mod fmt_range;
285
286 #[cfg(all(feature = "non_basic", feature = "rust_1_88"))]
287 mod rust_1_88_nonbasic_fmt_impls;
288}
289
290pub use crate::{
291 concat_panic_::{concat_panic, MAX_PANIC_MSG_LEN},
292 panic_val::PanicVal,
293 wrapper::StdWrapper,
294};
295
296#[doc(no_inline)]
297pub use crate::fmt::{FmtArg, IsCustomType, PanicFmt};
298
299#[cfg(feature = "non_basic")]
300#[doc(no_inline)]
301pub use crate::fmt::{ComputePvCount, TypeDelim};
302
303#[doc(hidden)]
304pub mod __ {
305 pub use core::{
306 assert, compile_error, concat,
307 option::Option::{None, Some},
308 primitive::usize,
309 result::Result::{Err, Ok},
310 stringify,
311 };
312
313 pub use crate::*;
314
315 #[cfg(feature = "non_basic")]
316 pub use crate::reexported_non_basic::*;
317}
318
319#[cfg(feature = "non_basic")]
320#[doc(hidden)]
321mod reexported_non_basic {
322 pub use core::{mem::forget, option::Option, primitive::str, unreachable};
323
324 pub use typewit::MakeTypeWitness;
325
326 pub use crate::{
327 concat_panic_::{compute_length, make_panic_string_unwrapped},
328 const_default::ConstDefault,
329 macros::concat_macro::ConcatCmd,
330 utils::{assert_flatten_panicvals_length, flatten_panicvals, panicvals_id},
331 };
332
333 pub const EPV: crate::PanicVal<'_> = crate::PanicVal::EMPTY;
334}
335
336#[cfg(feature = "derive")]
337include! {"./proc_macro_reexports/panicfmt_derive.rs"}
338
339#[doc(hidden)]
340#[cfg(feature = "test")]
341pub mod test_utils;
342
343#[doc(hidden)]
344#[cfg(feature = "test")]
345pub mod for_tests {
346 pub use crate::concat_panic_::{format_panic_message, NotEnoughSpace};
347}
348
349#[cfg(doctest)]
350#[doc = include_str!("../README.md")]
351pub struct ReadmeTest;