7270: Introduce more appropriate assertion mechanism r=matklad a=matklad

bors r+
🤖

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2021-01-14 15:27:20 +00:00 committed by GitHub
commit 540edee3cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 72 additions and 7 deletions

View file

@ -590,8 +590,7 @@ fn main() { let _ = crate::$0 }
"#, "#,
expect![[r##" expect![[r##"
fn main() fn main() fn main() fn main()
ma foo!() #[macro_export] ma foo!() #[macro_export] macro_rules! foo
macro_rules! foo
"##]], "##]],
); );
} }

View file

@ -540,8 +540,7 @@ mod macros {
"#, "#,
expect![[r##" expect![[r##"
fn f() fn f() fn f() fn f()
ma concat!() #[macro_export] ma concat!() #[macro_export] macro_rules! concat
macro_rules! concat
md std md std
"##]], "##]],
); );
@ -597,8 +596,7 @@ fn main() { let v = $0 }
"#, "#,
expect![[r##" expect![[r##"
md m1 md m1
ma baz!() #[macro_export] ma baz!() #[macro_export] macro_rules! baz
macro_rules! baz
fn main() fn main() fn main() fn main()
md m2 md m2
ma bar!() macro_rules! bar ma bar!() macro_rules! bar

View file

@ -7,6 +7,7 @@ use ide_db::helpers::{
insert_use::{self, ImportScope, MergeBehavior}, insert_use::{self, ImportScope, MergeBehavior},
mod_path_to_ast, SnippetCap, mod_path_to_ast, SnippetCap,
}; };
use stdx::assert_never;
use syntax::{algo, TextRange}; use syntax::{algo, TextRange};
use text_edit::TextEdit; use text_edit::TextEdit;
@ -396,6 +397,9 @@ impl Builder {
} }
pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder { pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder {
self.detail = detail.map(Into::into); self.detail = detail.map(Into::into);
if let Some(detail) = &self.detail {
assert_never!(detail.contains('\n'), "multiline detail: {}", detail);
}
self self
} }
#[allow(unused)] #[allow(unused)]

View file

@ -70,6 +70,11 @@ fn setup_logging(log_file: Option<PathBuf>) -> Result<()> {
tracing_setup::setup_tracing()?; tracing_setup::setup_tracing()?;
profile::init(); profile::init();
if !cfg!(debug_assertions) {
stdx::set_assert_hook(|loc, args| log::error!("assertion failed at {}: {}", loc, args));
}
Ok(()) Ok(())
} }

View file

@ -4,6 +4,8 @@ use std::{cmp::Ordering, ops, process, time::Instant};
mod macros; mod macros;
pub mod panic_context; pub mod panic_context;
pub use crate::macros::{on_assert_failure, set_assert_hook};
#[inline(always)] #[inline(always)]
pub fn is_ci() -> bool { pub fn is_ci() -> bool {
option_env!("CI").is_some() option_env!("CI").is_some()

View file

@ -1,4 +1,9 @@
//! Convenience macros. //! Convenience macros.
use std::{
fmt, mem, panic,
sync::atomic::{AtomicUsize, Ordering::SeqCst},
};
#[macro_export] #[macro_export]
macro_rules! eprintln { macro_rules! eprintln {
($($tt:tt)*) => {{ ($($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)
}

View file

@ -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(); let name = node.name().map(|name| name.syntax().text().to_string()).unwrap_or_default();
match node { match node {
ast::Macro::MacroRules(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) format!("{}macro_rules! {}", vis, name)
} }
ast::Macro::MacroDef(node) => { ast::Macro::MacroDef(node) => {

View file

@ -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. **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 ## Getters & Setters
If a field can have any value without breaking invariants, make the field public. If a field can have any value without breaking invariants, make the field public.