const_panic/macros.rs
1#[macro_use]
2mod concat_assert;
3
4#[cfg(feature = "non_basic")]
5#[macro_use]
6pub(crate) mod concat_macro;
7
8#[cfg(feature = "non_basic")]
9#[macro_use]
10mod non_basic_macros;
11
12#[cfg(feature = "non_basic")]
13#[macro_use]
14mod macro_utils;
15
16#[cfg(feature = "non_basic")]
17#[macro_use]
18mod impl_panicfmt;
19
20pub(crate) mod unwrapping;
21
22#[doc(hidden)]
23#[macro_export]
24macro_rules! __write_array {
25 ($array:expr, $len:expr, $value:expr) => {
26 $array[$len] = $value;
27 $len += 1;
28 };
29}
30
31#[doc(hidden)]
32#[macro_export]
33macro_rules! __write_array_checked {
34 ($array:expr, $len:expr, $value:expr) => {
35 if $array.len() > $len {
36 $array[$len] = $value;
37 $len += 1;
38 }
39 };
40}
41
42/// Coerces `$reff` to a type that has a `to_panicvals` method,
43/// which is expected to return a `[PanicVal<'_>; LEN]`.
44///
45/// # Limitations
46///
47#[doc = crate::doc_macros::limitation_docs!()]
48///
49/// # Example
50///
51/// This example uses [`const_panic::ArrayString`](crate::ArrayString)
52/// to show what the values format into,
53/// which requires the `"non_basic"` crate feature (enabled by default).
54///
55#[cfg_attr(feature = "non_basic", doc = "```rust")]
56#[cfg_attr(not(feature = "non_basic"), doc = "```ignore")]
57/// use const_panic::{ArrayString, FmtArg, IsCustomType, PanicFmt, PanicVal, coerce_fmt};
58///
59/// type AS = ArrayString<100>;
60///
61/// assert_eq!(
62/// AS::from_panicvals(&coerce_fmt!(100u8).to_panicvals(FmtArg::DEBUG)).unwrap(),
63/// "100",
64/// );
65///
66/// assert_eq!(
67/// AS::from_panicvals(&coerce_fmt!("hello\n").to_panicvals(FmtArg::DEBUG)).unwrap(),
68/// r#""hello\n""#,
69/// );
70///
71/// assert_eq!(
72/// AS::from_panicvals(&coerce_fmt!(IsReal::No).to_panicvals(FmtArg::DEBUG)).unwrap(),
73/// "No",
74/// );
75///
76/// assert_eq!(
77/// AS::from_panicvals(&coerce_fmt!(IsReal::Yes).to_panicvals(FmtArg::DEBUG)).unwrap(),
78/// "Yes",
79/// );
80///
81///
82///
83/// enum IsReal{Yes, No}
84///
85/// // All the code below manually implements panic formatting for a field-less enum.
86/// // This can be written concisely with the `PanicFmt` derive or `impl_panicfmt` macro.
87/// impl PanicFmt for IsReal {
88/// type This = Self;
89/// type Kind = IsCustomType;
90/// const PV_COUNT: usize = 1;
91/// }
92///
93/// impl IsReal {
94/// pub const fn to_panicvals(&self, _f: FmtArg) -> [PanicVal<'_>; IsReal::PV_COUNT] {
95/// let x = match self {
96/// Self::Yes => "Yes",
97/// Self::No => "No",
98/// };
99/// [PanicVal::write_str(x)]
100/// }
101/// }
102///
103/// ```
104#[macro_export]
105macro_rules! coerce_fmt {
106 ($reff:expr) => {
107 match &$reff {
108 reff => $crate::__::PanicFmt::PROOF.infer(reff).coerce(reff),
109 }
110 };
111}
112
113/// Panics with the concanenation of the arguments.
114///
115/// [**Examples below**](#examples)
116///
117/// # Syntax
118///
119/// This macro uses this syntax:
120/// ```text
121/// concat_panic!(
122/// $($fmtarg:expr;)?
123/// $(
124/// $( $format_override:tt: )? $arg_to_fmt:expr
125/// ),*
126/// $(,)?
127/// )
128/// ```
129///
130/// `$fmtarg` is an optional [`FmtArg`](crate::FmtArg) argument
131/// which defaults to `FmtArg::DEBUG`,
132/// determining how non-literal `$arg_to_fmt` arguments are formatted.
133///
134/// [`$format_override`](#formatting-overrides) overrides the `$fmtarg` argument,
135/// changing how that `$arg_to_fmt` argument is formatted.
136///
137#[doc = formatting_docs!()]
138///
139/// # Limitations
140///
141#[doc = crate::doc_macros::limitation_docs!()]
142///
143/// # Examples
144///
145/// ### `Odd`-type
146///
147/// ```rust, compile_fail
148/// use const_panic::concat_panic;
149///
150/// use odd::Odd;
151///
152/// # fn main(){
153/// const _: Odd = match Odd::new(3 * 4) {
154/// Ok(x) => x,
155/// Err(x) => concat_panic!("\nexpected odd number, got `", x, "`"),
156/// };
157/// # }
158///
159/// mod odd {
160/// pub struct Odd(u32);
161///
162/// impl Odd {
163/// pub const fn new(n: u32) -> Result<Odd, Even> {
164/// if n % 2 == 1 {
165/// Ok(Odd(n))
166/// } else {
167/// Err(Even(n))
168/// }
169/// }
170/// }
171///
172/// # /*
173/// #[derive(const_panic::PanicFmt))]
174/// # */
175/// pub struct Even(u32);
176/// #
177/// # impl const_panic::PanicFmt for Even {
178/// # type This = Self;
179/// # type Kind = const_panic::IsCustomType;
180/// # const PV_COUNT: usize = 1;
181/// # }
182/// # impl Even {
183/// # pub const fn to_panicvals(
184/// # &self,
185/// # f: const_panic::FmtArg,
186/// # ) -> [const_panic::PanicVal<'static>; 1] {
187/// # const_panic::StdWrapper(&self.0).to_panicvals(f)
188/// # }
189/// # }
190/// }
191///
192/// ```
193/// produces this compile-time error:
194/// ```text
195/// error[E0080]: evaluation of constant value failed
196/// --> src/macros.rs:188:15
197/// |
198/// 10 | Err(x) => concat_panic!("\nexpected odd number, got `", x, "`"),
199/// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at '
200/// expected odd number, got `12`', src/macros.rs:10:15
201/// |
202/// = note: this error originates in the macro `concat_panic` (in Nightly builds, run with -Z macro-backtrace for more info)
203///
204/// ```
205///
206///
207/// ### All the syntax
208///
209/// This example demonstrates using all of the syntax of this macro.
210///
211/// ```compile_fail
212/// use const_panic::{FmtArg, concat_panic, fmt};
213///
214/// const _: () = concat_panic!{
215/// // the optional `$fmtarg` parameter.
216/// // If this argument isn't passed, it defaults to `FmtArg::DEBUG`
217/// FmtArg::ALT_DEBUG;
218///
219/// "\n\nshowing off literals:\n",
220/// 100u8,
221/// "hello",
222///
223/// "\n\nnon-literals with formatting determined by the $fmtarg parameter:\n",
224/// // this is considered a non-literal, because it's inside other tokens.
225/// ("a non-literal"),
226/// [100u8, 200],
227///
228/// "\n\nexplicitly debug formatted:\n",
229/// debug: "foo",
230/// // `{?}:` is The same as `debug:`
231/// {?}: "bar",
232///
233/// "\n\nalternate debug formatted:\n",
234/// alt_debug: ["foo"],
235/// // `{#?}:` is The same as `alt_debug:`
236/// {#?}: "bar",
237///
238/// "\n\ndisplay formatted:\n",
239/// display: "baz",
240/// // `{}:` is The same as `display:`
241/// {}: ["qux", "aaa"],
242///
243/// "\n\nalternate display formatted:",
244/// alt_display: ["bbb", "ccc"],
245/// // `{#}:` is The same as `alt_display:`
246/// {#}: ["bbb", "ccc"],
247///
248/// "\n\nbinary formatted:\n",
249/// bin: [3u8, 5, 8, 13],
250/// // `{b}:` is The same as `bin:`
251/// {b}: [3u8, 5, 8, 13],
252///
253/// "\n\nalternate-binary formatted:\n",
254/// alt_bin: [21u8, 34, 55, 89],
255/// // `{#b}:` is The same as `alt_bin:`
256/// {#b}: [21u8, 34, 55, 89],
257///
258/// "\n\nhexadecimal formatted:\n",
259/// hex: [3u8, 5, 8, 13],
260/// // `{X}:` is The same as `hex:`
261/// {X}: [3u8, 5, 8, 13],
262///
263/// "\n\nalternate-hexadecimal formatted:\n",
264/// alt_hex: [21u8, 34, 55, 89],
265/// // `{#X}:` is The same as `alt_hex:`
266/// {#X}: [21u8, 34, 55, 89],
267///
268/// "\n\n",
269/// };
270///
271/// ```
272/// The above code produces this compile-time error:
273/// ```text
274/// error[E0080]: evaluation of constant value failed
275/// --> src/macros.rs:186:15
276/// |
277/// 6 | const _: () = concat_panic!{
278/// | _______________^
279/// 7 | | // the optional `$fmtarg` parameter.
280/// 8 | | // If this argument isn't passed, it defaults to `FmtArg::DEBUG`
281/// 9 | | FmtArg::ALT_DEBUG;
282/// ... |
283/// 60 | | "\n\n",
284/// 61 | | };
285/// | |_^ the evaluated program panicked at '
286///
287/// showing off literals:
288/// 100hello
289///
290/// non-literals with formatting determined by the $fmtarg parameter:
291/// "a non-literal"[
292/// 100,
293/// 200,
294/// ]
295///
296/// explicitly debug formatted:
297/// "foo""bar"
298///
299/// alternate debug formatted:
300/// [
301/// "foo",
302/// ]"bar"
303///
304/// display formatted:
305/// baz[qux, aaa]
306///
307/// alternate display formatted:[
308/// bbb,
309/// ccc,
310/// ][
311/// bbb,
312/// ccc,
313/// ]
314///
315/// binary formatted:
316/// [11, 101, 1000, 1101][11, 101, 1000, 1101]
317///
318/// alternate-binary formatted:
319/// [
320/// 0b10101,
321/// 0b100010,
322/// 0b110111,
323/// 0b1011001,
324/// ][
325/// 0b10101,
326/// 0b100010,
327/// 0b110111,
328/// 0b1011001,
329/// ]
330///
331/// hexadecimal formatted:
332/// [3, 5, 8, D][3, 5, 8, D]
333///
334/// alternate-hexadecimal formatted:
335/// [
336/// 0x15,
337/// 0x22,
338/// 0x37,
339/// 0x59,
340/// ][
341/// 0x15,
342/// 0x22,
343/// 0x37,
344/// 0x59,
345/// ]
346///
347/// ', src/macros.rs:6:15
348/// |
349/// = note: this error originates in the macro `concat_panic` (in Nightly builds, run with -Z macro-backtrace for more info)
350///
351/// error: aborting due to previous error
352///
353/// ```
354///
355#[macro_export]
356macro_rules! concat_panic {
357 ($($args:tt)*) => (
358 $crate::__concat_func_setup!{
359 (|args| $crate::concat_panic(args))
360 []
361 [$($args)*,]
362 }
363 )
364}
365
366// This macro takes the optional `$fmt:expr;` argument before everything else.
367// But I had to parse the argument manually,
368// because `$fmt:expr;` fails compilation instead of trying the following branches
369// when the argument isn't valid expression syntax.
370#[doc(hidden)]
371#[macro_export]
372macro_rules! __concat_func_setup {
373 ($args:tt $prev:tt [$($fmt:tt).*; $($rem:tt)* ]) => ({
374 let mut fmt: $crate::FmtArg = $($fmt).*;
375 $crate::__concat_func!{fmt $args $prev [$($rem)*]}
376 });
377 ($args:tt $prev:tt [$(:: $(@$_dummy:tt@)?)? $($fmt:ident)::* ; $($rem:tt)* ]) => ({
378 let mut fmt: $crate::FmtArg = $(:: $($_dummy)?)? $($fmt)::*;
379 $crate::__concat_func!{fmt $args $prev [$($rem)*]}
380 });
381 ($args:tt $prev:tt $rem:tt) => ({
382 let mut fmt: $crate::FmtArg = $crate::FmtArg::DEBUG;
383 $crate::__concat_func!{fmt $args $prev $rem}
384 });
385}
386#[doc(hidden)]
387#[macro_export]
388macro_rules! __concat_func {
389 ($fmt:ident $args:tt [$($prev:tt)*] [$keyword:tt: $expr:expr, $($rem:tt)* ]) => {
390 $crate::__concat_func!{
391 $fmt
392 $args
393 [$($prev)* ($crate::__set_fmt_from_kw!($keyword, $fmt), $expr)]
394 [$($rem)*]
395 }
396 };
397 ($fmt:ident $args:tt [$($prev:tt)*] [$expr:literal, $($rem:tt)* ]) => {
398 $crate::__concat_func!{
399 $fmt
400 $args
401 [$($prev)* ($crate::__set_fmt_from_kw!(display, $fmt), $expr)]
402 [$($rem)*]
403 }
404 };
405 ($fmt:ident $args:tt [$($prev:tt)*] [$expr:expr, $($rem:tt)* ]) => {
406 $crate::__concat_func!{
407 $fmt
408 $args
409 [$($prev)* ($fmt, $expr)]
410 [$($rem)*]
411 }
412 };
413 ($fmt:ident (|$args:ident| $function_call:expr) [$(($fmt_arg:expr, $reff:expr))*] [$(,)*]) => {
414 match &[
415 $(
416 $crate::StdWrapper(
417 &$crate::coerce_fmt!($reff)
418 .to_panicvals($fmt_arg)
419 ).deref_panic_vals(),
420 )*
421 ] {
422 $args => $function_call,
423 }
424 };
425}
426
427#[doc(hidden)]
428#[macro_export]
429macro_rules! __set_fmt_from_kw {
430 (open, $fmtarg:ident) => {{
431 $fmtarg = $fmtarg.indent();
432 $fmtarg.set_display()
433 }};
434 (close, $fmtarg:ident) => {{
435 $fmtarg = $fmtarg.unindent();
436 $fmtarg.set_display()
437 }};
438 (display, $fmtarg:ident) => {
439 $fmtarg.set_display().set_alternate(false)
440 };
441 ({}, $fmtarg:ident) => {
442 $fmtarg.set_display().set_alternate(false)
443 };
444 (alt_display, $fmtarg:ident) => {
445 $fmtarg.set_display().set_alternate(true)
446 };
447 ({#}, $fmtarg:ident) => {
448 $fmtarg.set_display().set_alternate(true)
449 };
450 (debug, $fmtarg:ident) => {
451 $fmtarg.set_debug().set_alternate(false)
452 };
453 ({?}, $fmtarg:ident) => {
454 $fmtarg.set_debug().set_alternate(false)
455 };
456 (alt_debug, $fmtarg:ident) => {
457 $fmtarg.set_debug().set_alternate(true)
458 };
459 ({#?}, $fmtarg:ident) => {
460 $fmtarg.set_debug().set_alternate(true)
461 };
462 (hex, $fmtarg:ident) => {
463 $fmtarg.set_hex().set_alternate(false)
464 };
465 ({X}, $fmtarg:ident) => {
466 $fmtarg.set_hex().set_alternate(false)
467 };
468 (alt_hex, $fmtarg:ident) => {
469 $fmtarg.set_hex().set_alternate(true)
470 };
471 ({#X}, $fmtarg:ident) => {
472 $fmtarg.set_hex().set_alternate(true)
473 };
474 (bin, $fmtarg:ident) => {
475 $fmtarg.set_bin().set_alternate(false)
476 };
477 ({b}, $fmtarg:ident) => {
478 $fmtarg.set_bin().set_alternate(false)
479 };
480 (alt_bin, $fmtarg:ident) => {
481 $fmtarg.set_bin().set_alternate(true)
482 };
483 ({#b}, $fmtarg:ident) => {
484 $fmtarg.set_bin().set_alternate(true)
485 };
486 (_, $fmtarg:ident) => {
487 $fmtarg
488 };
489 ($kw:tt, $fmtarg:ident) => {
490 compile_error!(concat!(
491 "unrecognized formatting specifier: ",
492 stringify!($kw),
493 "\n",
494 "expected one of:\n",
495 "- display/{}\n",
496 "- alt_display/{#}\n",
497 "- debug/{?}\n",
498 "- alt_debug/{#?}\n",
499 "- hex/{X}\n",
500 "- alt_hex/{#X}\n",
501 "- bin/{b}\n",
502 "- alt_bin/{#b}\n",
503 ))
504 };
505}