From 8dc68ecdfcc764c7c0dcf5fcedcb51b092d99620 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 14 Jan 2021 18:25:19 +0300 Subject: 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. --- crates/stdx/src/lib.rs | 2 ++ crates/stdx/src/macros.rs | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) (limited to 'crates/stdx') 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}; mod macros; pub mod panic_context; +pub use crate::macros::{on_assert_failure, set_assert_hook}; + #[inline(always)] pub fn is_ci() -> bool { 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 @@ //! 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::(hook) }; + hook(panic::Location::caller(), args) +} -- cgit v1.2.3