aboutsummaryrefslogtreecommitdiff
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
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.
-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 fa9e6e810..33df26761 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 }
590 "#, 590 "#,
591 expect![[r##" 591 expect![[r##"
592 fn main() fn main() 592 fn main() fn main()
593 ma foo!(…) #[macro_export] 593 ma foo!(…) #[macro_export] macro_rules! foo
594 macro_rules! foo
595 "##]], 594 "##]],
596 ); 595 );
597 } 596 }
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs
index 7ba99447d..53e1391f3 100644
--- a/crates/completion/src/completions/unqualified_path.rs
+++ b/crates/completion/src/completions/unqualified_path.rs
@@ -540,8 +540,7 @@ mod macros {
540"#, 540"#,
541 expect![[r##" 541 expect![[r##"
542 fn f() fn f() 542 fn f() fn f()
543 ma concat!(…) #[macro_export] 543 ma concat!(…) #[macro_export] macro_rules! concat
544 macro_rules! concat
545 md std 544 md std
546 "##]], 545 "##]],
547 ); 546 );
@@ -597,8 +596,7 @@ fn main() { let v = $0 }
597"#, 596"#,
598 expect![[r##" 597 expect![[r##"
599 md m1 598 md m1
600 ma baz!(…) #[macro_export] 599 ma baz!(…) #[macro_export] macro_rules! baz
601 macro_rules! baz
602 fn main() fn main() 600 fn main() fn main()
603 md m2 601 md m2
604 ma bar!(…) macro_rules! bar 602 ma bar!(…) macro_rules! bar
diff --git a/crates/completion/src/item.rs b/crates/completion/src/item.rs
index 35af354b0..0134ff219 100644
--- a/crates/completion/src/item.rs
+++ b/crates/completion/src/item.rs
@@ -7,6 +7,7 @@ use ide_db::helpers::{
7 insert_use::{self, ImportScope, MergeBehavior}, 7 insert_use::{self, ImportScope, MergeBehavior},
8 mod_path_to_ast, SnippetCap, 8 mod_path_to_ast, SnippetCap,
9}; 9};
10use stdx::assert_never;
10use syntax::{algo, TextRange}; 11use syntax::{algo, TextRange};
11use text_edit::TextEdit; 12use text_edit::TextEdit;
12 13
@@ -396,6 +397,9 @@ impl Builder {
396 } 397 }
397 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder { 398 pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder {
398 self.detail = detail.map(Into::into); 399 self.detail = detail.map(Into::into);
400 if let Some(detail) = &self.detail {
401 assert_never!(detail.contains('\n'), "multiline detail: {}", detail);
402 }
399 self 403 self
400 } 404 }
401 #[allow(unused)] 405 #[allow(unused)]
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 3af3c59d8..bf42654a8 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<()> {
70 tracing_setup::setup_tracing()?; 70 tracing_setup::setup_tracing()?;
71 71
72 profile::init(); 72 profile::init();
73
74 if !cfg!(debug_assertions) {
75 stdx::set_assert_hook(|loc, args| log::error!("assertion failed at {}: {}", loc, args));
76 }
77
73 Ok(()) 78 Ok(())
74} 79}
75 80
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}
diff --git a/crates/syntax/src/display.rs b/crates/syntax/src/display.rs
index 391647fc6..cd956d950 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 {
80 let name = node.name().map(|name| name.syntax().text().to_string()).unwrap_or_default(); 80 let name = node.name().map(|name| name.syntax().text().to_string()).unwrap_or_default();
81 match node { 81 match node {
82 ast::Macro::MacroRules(node) => { 82 ast::Macro::MacroRules(node) => {
83 let vis = if node.has_atom_attr("macro_export") { "#[macro_export]\n" } else { "" }; 83 let vis = if node.has_atom_attr("macro_export") { "#[macro_export] " } else { "" };
84 format!("{}macro_rules! {}", vis, name) 84 format!("{}macro_rules! {}", vis, name)
85 } 85 }
86 ast::Macro::MacroDef(node) => { 86 ast::Macro::MacroDef(node) => {
diff --git a/docs/dev/style.md b/docs/dev/style.md
index 9859f6148..21330948b 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -215,6 +215,11 @@ if idx >= len {
215 215
216**Rationale:** its useful to see the invariant relied upon by the rest of the function clearly spelled out. 216**Rationale:** its useful to see the invariant relied upon by the rest of the function clearly spelled out.
217 217
218## Assertions
219
220Assert liberally.
221Prefer `stdx::assert_never!` to standard `assert!`.
222
218## Getters & Setters 223## Getters & Setters
219 224
220If a field can have any value without breaking invariants, make the field public. 225If a field can have any value without breaking invariants, make the field public.