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.
This commit is contained in:
Aleksey Kladov 2021-01-14 18:25:19 +03:00
parent 865e05b5b4
commit 8dc68ecdfc
8 changed files with 72 additions and 7 deletions

View file

@ -1,4 +1,9 @@
//! Convenience macros.
use std::{
fmt, mem, panic,
sync::atomic::{AtomicUsize, Ordering::SeqCst},
};
#[macro_export]
macro_rules! eprintln {
($($tt:tt)*) => {{
@ -44,3 +49,50 @@ macro_rules! impl_from {
)*
}
}
/// A version of `assert!` macro which allows to handle an assertion failure.
///
/// In release mode, it returns the condition and logs an error.
///
/// ```
/// if assert_never!(impossible) {
/// // Heh, this shouldn't have happened, but lets try to soldier on...
/// return None;
/// }
/// ```
///
/// Rust analyzer is a long-running process, and crashing really isn't an option.
///
/// Shamelessly stolen from: https://www.sqlite.org/assert.html
#[macro_export]
macro_rules! assert_never {
($cond:expr) => { $crate::assert_always!($cond, "") };
($cond:expr, $($fmt:tt)*) => {{
let value = $cond;
if value {
$crate::on_assert_failure(
format_args!($($fmt)*)
);
}
value
}};
}
type AssertHook = fn(&panic::Location<'_>, fmt::Arguments<'_>);
static HOOK: AtomicUsize = AtomicUsize::new(0);
pub fn set_assert_hook(hook: AssertHook) {
HOOK.store(hook as usize, SeqCst);
}
#[cold]
#[track_caller]
pub fn on_assert_failure(args: fmt::Arguments) {
let hook: usize = HOOK.load(SeqCst);
if hook == 0 {
panic!("\n assertion failed: {}\n", args);
}
let hook: AssertHook = unsafe { mem::transmute::<usize, AssertHook>(hook) };
hook(panic::Location::caller(), args)
}