From d35bda6429b0a3a758ddcd899e954043584f1071 Mon Sep 17 00:00:00 2001
From: Aleksey Kladov <aleksey.kladov@gmail.com>
Date: Tue, 26 Jan 2021 22:11:12 +0300
Subject: [PATCH] Make always-assert crate reusable

---
 Cargo.lock                           | 11 ++++++
 crates/completion/src/item.rs        |  4 +--
 crates/ide/src/references/rename.rs  |  6 ++--
 crates/ide_db/src/source_change.rs   |  7 ++--
 crates/rust-analyzer/Cargo.toml      |  2 ++
 crates/rust-analyzer/src/bin/main.rs |  9 -----
 crates/stdx/Cargo.toml               |  1 +
 crates/stdx/src/lib.rs               |  2 +-
 crates/stdx/src/macros.rs            | 51 ----------------------------
 docs/dev/style.md                    |  2 +-
 xtask/src/install.rs                 |  2 +-
 11 files changed, 24 insertions(+), 73 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index efd1c6d330a..45a0211ffb8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -15,6 +15,15 @@ version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
 
+[[package]]
+name = "always-assert"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "727786f78c5bc0cda8011831616589f72084cb16b7df4213a997b78749b55a60"
+dependencies = [
+ "log",
+]
+
 [[package]]
 name = "ansi_term"
 version = "0.12.1"
@@ -1415,6 +1424,7 @@ dependencies = [
 name = "rust-analyzer"
 version = "0.0.0"
 dependencies = [
+ "always-assert",
  "anyhow",
  "cfg",
  "crossbeam-channel 0.5.0",
@@ -1658,6 +1668,7 @@ dependencies = [
 name = "stdx"
 version = "0.0.0"
 dependencies = [
+ "always-assert",
  "backtrace",
 ]
 
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs
index eeb952ec3c5..8ec4ac65e29 100644
--- a/crates/completion/src/item.rs
+++ b/crates/completion/src/item.rs
@@ -10,7 +10,7 @@ use ide_db::{
     },
     SymbolKind,
 };
-use stdx::{assert_never, impl_from};
+use stdx::{impl_from, never};
 use syntax::{algo, TextRange};
 use text_edit::TextEdit;
 
@@ -404,7 +404,7 @@ impl Builder {
     pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder {
         self.detail = detail.map(Into::into);
         if let Some(detail) = &self.detail {
-            if assert_never!(detail.contains('\n'), "multiline detail:\n{}", detail) {
+            if never!(detail.contains('\n'), "multiline detail:\n{}", detail) {
                 self.detail = Some(detail.splitn(2, '\n').next().unwrap().to_string());
             }
         }
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index c25bcce50eb..99a55853287 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -9,7 +9,7 @@ use ide_db::{
     search::FileReference,
     RootDatabase,
 };
-use stdx::assert_never;
+use stdx::never;
 use syntax::{
     ast::{self, NameOwner},
     lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T,
@@ -285,7 +285,7 @@ fn rename_mod(
 }
 
 fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> {
-    if assert_never!(local.is_self(sema.db)) {
+    if never!(local.is_self(sema.db)) {
         bail!("rename_to_self invoked on self");
     }
 
@@ -388,7 +388,7 @@ fn rename_self_to_param(
     let (file_id, self_param) = match local.source(sema.db) {
         InFile { file_id, value: Either::Right(self_param) } => (file_id, self_param),
         _ => {
-            assert_never!(true, "rename_self_to_param invoked on a non-self local");
+            never!(true, "rename_self_to_param invoked on a non-self local");
             bail!("rename_self_to_param invoked on a non-self local");
         }
     };
diff --git a/crates/ide_db/src/source_change.rs b/crates/ide_db/src/source_change.rs
index b1f87731bfd..f76bac151c2 100644
--- a/crates/ide_db/src/source_change.rs
+++ b/crates/ide_db/src/source_change.rs
@@ -10,7 +10,7 @@ use std::{
 
 use base_db::{AnchoredPathBuf, FileId};
 use rustc_hash::FxHashMap;
-use stdx::assert_never;
+use stdx::never;
 use text_edit::TextEdit;
 
 #[derive(Default, Debug, Clone)]
@@ -40,10 +40,7 @@ impl SourceChange {
     pub fn insert_source_edit(&mut self, file_id: FileId, edit: TextEdit) {
         match self.source_file_edits.entry(file_id) {
             Entry::Occupied(mut entry) => {
-                assert_never!(
-                    entry.get_mut().union(edit).is_err(),
-                    "overlapping edits for same file"
-                );
+                never!(entry.get_mut().union(edit).is_err(), "overlapping edits for same file");
             }
             Entry::Vacant(entry) => {
                 entry.insert(edit);
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 268c009424f..82ea76666a7 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -37,6 +37,7 @@ lsp-server = "0.5.0"
 tracing = "0.1"
 tracing-subscriber = { version = "0.2", default-features = false, features = ["env-filter", "registry"] }
 tracing-tree = { version = "0.1.4" }
+always-assert = "0.1"
 
 stdx = { path = "../stdx", version = "0.0.0" }
 flycheck = { path = "../flycheck", version = "0.0.0" }
@@ -72,3 +73,4 @@ tt = { path = "../tt" }
 
 [features]
 jemalloc = ["jemallocator", "profile/jemalloc"]
+force-always-assert = ["always-assert/force"]
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 1d6e5478bc4..bee2eedbccb 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -75,15 +75,6 @@ fn setup_logging(log_file: Option<PathBuf>) -> Result<()> {
 
     profile::init();
 
-    if !cfg!(debug_assertions) {
-        stdx::set_assert_hook(|loc, args| {
-            if env::var("RA_PROFILE").is_ok() {
-                panic!("assertion failed at {}: {}", loc, args)
-            }
-            log::error!("assertion failed at {}: {}", loc, args)
-        });
-    }
-
     Ok(())
 }
 
diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml
index c47e8d0a86b..5866c0a280b 100644
--- a/crates/stdx/Cargo.toml
+++ b/crates/stdx/Cargo.toml
@@ -11,6 +11,7 @@ doctest = false
 
 [dependencies]
 backtrace = { version = "0.3.44", optional = true }
+always-assert = { version = "0.1.1", features = ["log"] }
 # Think twice before adding anything here
 
 [features]
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index d42817078d7..d26be485344 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -4,7 +4,7 @@ use std::{cmp::Ordering, ops, process, time::Instant};
 mod macros;
 pub mod panic_context;
 
-pub use crate::macros::{on_assert_failure, set_assert_hook};
+pub use always_assert::{always, never};
 
 #[inline(always)]
 pub fn is_ci() -> bool {
diff --git a/crates/stdx/src/macros.rs b/crates/stdx/src/macros.rs
index 4f5c6100d2a..d91fc690cb5 100644
--- a/crates/stdx/src/macros.rs
+++ b/crates/stdx/src/macros.rs
@@ -1,9 +1,5 @@
 //! Convenience macros.
 
-use std::{
-    fmt, mem, panic,
-    sync::atomic::{AtomicUsize, Ordering::SeqCst},
-};
 #[macro_export]
 macro_rules! eprintln {
     ($($tt:tt)*) => {{
@@ -49,50 +45,3 @@ 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_never!($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)
-}
diff --git a/docs/dev/style.md b/docs/dev/style.md
index 6dc6868c2cc..0c5e2ad33cb 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -232,7 +232,7 @@ if idx >= len {
 ## Assertions
 
 Assert liberally.
-Prefer `stdx::assert_never!` to standard `assert!`.
+Prefer `stdx::never!` to standard `assert!`.
 
 ## Getters & Setters
 
diff --git a/xtask/src/install.rs b/xtask/src/install.rs
index 202c7442668..81b9956b8f0 100644
--- a/xtask/src/install.rs
+++ b/xtask/src/install.rs
@@ -180,7 +180,7 @@ fn install_server(opts: ServerOpt) -> Result<()> {
         Malloc::Jemalloc => &["--features", "jemalloc"],
     };
 
-    let cmd = cmd!("cargo install --path crates/rust-analyzer --locked --force {features...}");
+    let cmd = cmd!("cargo install --path crates/rust-analyzer --locked --force --features force-always-assert {features...}");
     let res = cmd.run();
 
     if res.is_err() && old_rust {