diff options
Diffstat (limited to 'crates/stdx')
-rw-r--r-- | crates/stdx/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/stdx/src/macros.rs | 52 |
2 files changed, 54 insertions, 0 deletions
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index d9a62e943..1ff2559bb 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs | |||
@@ -4,6 +4,8 @@ use std::{cmp::Ordering, ops, process, time::Instant}; | |||
4 | mod macros; | 4 | mod macros; |
5 | pub mod panic_context; | 5 | pub mod panic_context; |
6 | 6 | ||
7 | pub use crate::macros::{on_assert_failure, set_assert_hook}; | ||
8 | |||
7 | #[inline(always)] | 9 | #[inline(always)] |
8 | pub fn is_ci() -> bool { | 10 | pub fn is_ci() -> bool { |
9 | option_env!("CI").is_some() | 11 | option_env!("CI").is_some() |
diff --git a/crates/stdx/src/macros.rs b/crates/stdx/src/macros.rs index f5ee3484b..263b938e3 100644 --- a/crates/stdx/src/macros.rs +++ b/crates/stdx/src/macros.rs | |||
@@ -1,4 +1,9 @@ | |||
1 | //! Convenience macros. | 1 | //! Convenience macros. |
2 | |||
3 | use std::{ | ||
4 | fmt, mem, panic, | ||
5 | sync::atomic::{AtomicUsize, Ordering::SeqCst}, | ||
6 | }; | ||
2 | #[macro_export] | 7 | #[macro_export] |
3 | macro_rules! eprintln { | 8 | macro_rules! eprintln { |
4 | ($($tt:tt)*) => {{ | 9 | ($($tt:tt)*) => {{ |
@@ -44,3 +49,50 @@ macro_rules! impl_from { | |||
44 | )* | 49 | )* |
45 | } | 50 | } |
46 | } | 51 | } |
52 | |||
53 | /// A version of `assert!` macro which allows to handle an assertion failure. | ||
54 | /// | ||
55 | /// In release mode, it returns the condition and logs an error. | ||
56 | /// | ||
57 | /// ``` | ||
58 | /// if assert_never!(impossible) { | ||
59 | /// // Heh, this shouldn't have happened, but lets try to soldier on... | ||
60 | /// return None; | ||
61 | /// } | ||
62 | /// ``` | ||
63 | /// | ||
64 | /// Rust analyzer is a long-running process, and crashing really isn't an option. | ||
65 | /// | ||
66 | /// Shamelessly stolen from: https://www.sqlite.org/assert.html | ||
67 | #[macro_export] | ||
68 | macro_rules! assert_never { | ||
69 | ($cond:expr) => { $crate::assert_always!($cond, "") }; | ||
70 | ($cond:expr, $($fmt:tt)*) => {{ | ||
71 | let value = $cond; | ||
72 | if value { | ||
73 | $crate::on_assert_failure( | ||
74 | format_args!($($fmt)*) | ||
75 | ); | ||
76 | } | ||
77 | value | ||
78 | }}; | ||
79 | } | ||
80 | |||
81 | type AssertHook = fn(&panic::Location<'_>, fmt::Arguments<'_>); | ||
82 | static HOOK: AtomicUsize = AtomicUsize::new(0); | ||
83 | |||
84 | pub fn set_assert_hook(hook: AssertHook) { | ||
85 | HOOK.store(hook as usize, SeqCst); | ||
86 | } | ||
87 | |||
88 | #[cold] | ||
89 | #[track_caller] | ||
90 | pub fn on_assert_failure(args: fmt::Arguments) { | ||
91 | let hook: usize = HOOK.load(SeqCst); | ||
92 | if hook == 0 { | ||
93 | panic!("\n assertion failed: {}\n", args); | ||
94 | } | ||
95 | |||
96 | let hook: AssertHook = unsafe { mem::transmute::<usize, AssertHook>(hook) }; | ||
97 | hook(panic::Location::caller(), args) | ||
98 | } | ||