|
3 | 3 | mod change;
|
4 | 4 | mod input;
|
5 | 5 |
|
6 |
| -use std::hash::BuildHasherDefault; |
| 6 | +use std::{cell::RefCell, hash::BuildHasherDefault, panic, sync::Once}; |
7 | 7 |
|
8 | 8 | pub use crate::{
|
9 | 9 | change::FileChange,
|
@@ -60,7 +60,7 @@ impl Files {
|
60 | 60 | match self.files.get(&file_id) {
|
61 | 61 | Some(text) => *text,
|
62 | 62 | None => {
|
63 |
| - panic!("Unable to fetch file text for `vfs::FileId`: {:?}; this is a bug", file_id) |
| 63 | + panic!("Unable to fetch file text for `vfs::FileId`: {file_id:?}; this is a bug") |
64 | 64 | }
|
65 | 65 | }
|
66 | 66 | }
|
@@ -101,8 +101,7 @@ impl Files {
|
101 | 101 | let source_root = match self.source_roots.get(&source_root_id) {
|
102 | 102 | Some(source_root) => source_root,
|
103 | 103 | None => panic!(
|
104 |
| - "Unable to fetch `SourceRootInput` with `SourceRootId` ({:?}); this is a bug", |
105 |
| - source_root_id |
| 104 | + "Unable to fetch `SourceRootInput` with `SourceRootId` ({source_root_id:?}); this is a bug" |
106 | 105 | ),
|
107 | 106 | };
|
108 | 107 |
|
@@ -132,8 +131,7 @@ impl Files {
|
132 | 131 | let file_source_root = match self.file_source_roots.get(&id) {
|
133 | 132 | Some(file_source_root) => file_source_root,
|
134 | 133 | None => panic!(
|
135 |
| - "Unable to get `FileSourceRootInput` with `vfs::FileId` ({:?}); this is a bug", |
136 |
| - id |
| 134 | + "Unable to get `FileSourceRootInput` with `vfs::FileId` ({id:?}); this is a bug", |
137 | 135 | ),
|
138 | 136 | };
|
139 | 137 | *file_source_root
|
@@ -384,3 +382,51 @@ fn relevant_crates(db: &dyn RootQueryDb, file_id: FileId) -> Arc<[Crate]> {
|
384 | 382 | let source_root = db.file_source_root(file_id);
|
385 | 383 | db.source_root_crates(source_root.source_root_id(db))
|
386 | 384 | }
|
| 385 | + |
| 386 | +#[must_use] |
| 387 | +pub struct DbPanicContext { |
| 388 | + // prevent arbitrary construction |
| 389 | + _priv: (), |
| 390 | +} |
| 391 | + |
| 392 | +impl Drop for DbPanicContext { |
| 393 | + fn drop(&mut self) { |
| 394 | + Self::with_ctx(|ctx| assert!(ctx.pop().is_some())); |
| 395 | + } |
| 396 | +} |
| 397 | + |
| 398 | +impl DbPanicContext { |
| 399 | + pub fn enter(frame: String) -> DbPanicContext { |
| 400 | + #[expect(clippy::print_stderr, reason = "already panicking anyway")] |
| 401 | + fn set_hook() { |
| 402 | + let default_hook = panic::take_hook(); |
| 403 | + panic::set_hook(Box::new(move |panic_info| { |
| 404 | + DbPanicContext::with_ctx(|ctx| { |
| 405 | + if !ctx.is_empty() { |
| 406 | + eprintln!("Panic context:"); |
| 407 | + for frame in ctx.iter() { |
| 408 | + eprintln!("> {frame}\n"); |
| 409 | + } |
| 410 | + } |
| 411 | + }); |
| 412 | + if let Some(backtrace) = salsa::Backtrace::capture() { |
| 413 | + eprintln!("{backtrace}"); |
| 414 | + } |
| 415 | + default_hook(panic_info); |
| 416 | + })); |
| 417 | + } |
| 418 | + |
| 419 | + static SET_HOOK: Once = Once::new(); |
| 420 | + SET_HOOK.call_once(set_hook); |
| 421 | + |
| 422 | + Self::with_ctx(|ctx| ctx.push(frame)); |
| 423 | + DbPanicContext { _priv: () } |
| 424 | + } |
| 425 | + |
| 426 | + fn with_ctx(f: impl FnOnce(&mut Vec<String>)) { |
| 427 | + thread_local! { |
| 428 | + static CTX: RefCell<Vec<String>> = const { RefCell::new(Vec::new()) }; |
| 429 | + } |
| 430 | + CTX.with(|ctx| f(&mut ctx.borrow_mut())); |
| 431 | + } |
| 432 | +} |
0 commit comments