about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-01-14 15:27:20 +0000
committerGitHub <noreply@github.com>2021-01-14 15:27:20 +0000
commit540edee3cd11d45a03abc072bb9b6f01b59bcb25 (patch)
tree7d86fca4364984808724ccc5694aef20a43d686c
parentaeacaeed4e49dd71ba0de30a21d9f3d1cc153cec (diff)
parent8dc68ecdfcc764c7c0dcf5fcedcb51b092d99620 (diff)
downloadrust-540edee3cd11d45a03abc072bb9b6f01b59bcb25.tar.gz
rust-540edee3cd11d45a03abc072bb9b6f01b59bcb25.zip
Merge #7270
7270: Introduce more appropriate assertion mechanism r=matklad a=matklad

bors r+
🤖

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
-rw-r--r--crates/completion/src/completions/qualified_path.rs3
-rw-r--r--crates/completion/src/completions/unqualified_path.rs6
-rw-r--r--crates/completion/src/item.rs4
-rw-r--r--crates/rust-analyzer/src/bin/main.rs5
-rw-r--r--crates/stdx/src/lib.rs2
-rw-r--r--crates/stdx/src/macros.rs52
-rw-r--r--crates/syntax/src/display.rs2
-rw-r--r--docs/dev/style.md5
8 files changed, 72 insertions, 7 deletions
diff --git a/crates/completion/src/completions/qualified_path.rs b/crates/completion/src/completions/qualified_path.rs
index fa9e6e81000..33df2676156 100644
--- a/crates/completion/src/completions/qualified_path.rs
+++ b/crates/completion/src/completions/qualified_path.rs
@@ -590,8 +590,7 @@ fn main() { let _ = crate::$0 }
         "#,
             expect![[r##"
                 fn main()  fn main()
-                ma foo!(…) #[macro_export]
-                macro_rules! foo
+                ma foo!(…) #[macro_export] macro_rules! foo
             "##]],
         );
     }
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index 7ba99447d7e..53e1391f34e 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -540,8 +540,7 @@ mod macros {
 "#,
             expect![[r##"
                 fn f()        fn f()
-                ma concat!(…) #[macro_export]
-                macro_rules! concat
+                ma concat!(…) #[macro_export] macro_rules! concat
                 md std
             "##]],
         );
@@ -597,8 +596,7 @@ fn main() { let v = $0 }
 "#,
             expect![[r##"
                 md m1
-                ma baz!(…) #[macro_export]
-                macro_rules! baz
+                ma baz!(…) #[macro_export] macro_rules! baz
                 fn main()  fn main()
                 md m2
                 ma bar!(…) macro_rules! bar
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs
index 35af354b0c6..0134ff219b6 100644
--- a/crates/completion/src/item.rs
+++ b/crates/completion/src/item.rs
@@ -7,6 +7,7 @@ use ide_db::helpers::{
     insert_use::{self, ImportScope, MergeBehavior},
     mod_path_to_ast, SnippetCap,
 };
+use stdx::assert_never;
 use syntax::{algo, TextRange};
 use text_edit::TextEdit;
 
@@ -396,6 +397,9 @@ 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 {
+            assert_never!(detail.contains('\n'), "multiline detail: {}", detail);
+        }
         self
     }
     #[allow(unused)]
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 3af3c59d88b..bf42654a88c 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -70,6 +70,11 @@ fn setup_logging(log_file: Option<PathBuf>) -> Result<()> {
     tracing_setup::setup_tracing()?;
 
     profile::init();
+
+    if !cfg!(debug_assertions) {
+        stdx::set_assert_hook(|loc, args| log::error!("assertion failed at {}: {}", loc, args));
+    }
+
     Ok(())
 }
 
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index d9a62e943b6..1ff2559bbda 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 f5ee3484b0c..263b938e3f2 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::<usize, AssertHook>(hook) };
+    hook(panic::Location::caller(), args)
+}
diff --git a/crates/syntax/src/display.rs b/crates/syntax/src/display.rs
index 391647fc671..cd956d950de 100644
--- a/crates/syntax/src/display.rs
+++ b/crates/syntax/src/display.rs
@@ -80,7 +80,7 @@ pub fn macro_label(node: &ast::Macro) -> String {
     let name = node.name().map(|name| name.syntax().text().to_string()).unwrap_or_default();
     match node {
         ast::Macro::MacroRules(node) => {
-            let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" };
+            let vis = if node.has_atom_attr("macro_export") { "#[macro_export] " } else { "" };
             format!("{}macro_rules! {}", vis, name)
         }
         ast::Macro::MacroDef(node) => {
diff --git a/docs/dev/style.md b/docs/dev/style.md
index 9859f614820..21330948ba5 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -215,6 +215,11 @@ if idx >= len {
 
 **Rationale:** its useful to see the invariant relied upon by the rest of the function clearly spelled out.
 
+## Assertions
+
+Assert liberally.
+Prefer `stdx::assert_never!` to standard `assert!`.
+
 ## Getters & Setters
 
 If a field can have any value without breaking invariants, make the field public.