aboutsummaryrefslogtreecommitdiff
path: root/crates/stdx
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2021-01-14 15:25:19 +0000
committerAleksey Kladov <[email protected]>2021-01-14 15:25:19 +0000
commit8dc68ecdfcc764c7c0dcf5fcedcb51b092d99620 (patch)
treef0f93d744416a1114b34472b91574afe3be14147 /crates/stdx
parent865e05b5b47d3e27e8fe4458662e751e492a0f51 (diff)
Introduce more appropriate assertion mechanism
rust-analyzer is a long-running program, so we *should* handle assertion failures. See also https://www.sqlite.org/assert.html.
Diffstat (limited to 'crates/stdx')
-rw-r--r--crates/stdx/src/lib.rs2
-rw-r--r--crates/stdx/src/macros.rs52
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};
4mod macros; 4mod macros;
5pub mod panic_context; 5pub mod panic_context;
6 6
7pub use crate::macros::{on_assert_failure, set_assert_hook};
8
7#[inline(always)] 9#[inline(always)]
8pub fn is_ci() -> bool { 10pub 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
3use std::{
4 fmt, mem, panic,
5 sync::atomic::{AtomicUsize, Ordering::SeqCst},
6};
2#[macro_export] 7#[macro_export]
3macro_rules! eprintln { 8macro_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]
68macro_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
81type AssertHook = fn(&panic::Location<'_>, fmt::Arguments<'_>);
82static HOOK: AtomicUsize = AtomicUsize::new(0);
83
84pub fn set_assert_hook(hook: AssertHook) {
85 HOOK.store(hook as usize, SeqCst);
86}
87
88#[cold]
89#[track_caller]
90pub 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}