Skip to content

Commit 0960d4e

Browse files
bors[bot]matkladsteffahn
authored
Merge #9937 #9985
9937: internal: incentivize rust-analyzed developers to fix panics r=matklad a=matklad 9985: minor: Fix another “a”/“an” typo r=Veykril a=steffahn Follow-up to #9984. Co-authored-by: Aleksey Kladov <[email protected]> Co-authored-by: Frank Steffahn <[email protected]>
3 parents 107b2f1 + 85fbbc5 + ab23af4 commit 0960d4e

File tree

5 files changed

+69
-27
lines changed

5 files changed

+69
-27
lines changed

crates/ide/src/references.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! This module implements a reference search.
22
//! First, the element at the cursor position must be either an `ast::Name`
3-
//! or `ast::NameRef`. If it's a `ast::NameRef`, at the classification step we
3+
//! or `ast::NameRef`. If it's an `ast::NameRef`, at the classification step we
44
//! try to resolve the direct tree parent of this element, otherwise we
55
//! already have a definition and just need to get its HIR together with
66
//! some information that is needed for further steps of searching.

crates/rust-analyzer/src/dispatch.rs

+36-19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//! A visitor for downcasting arbitrary request (JSON) into a specific type.
2-
use std::{fmt, panic};
2+
use std::{fmt, panic, thread};
33

44
use serde::{de::DeserializeOwned, Serialize};
55

@@ -32,7 +32,7 @@ impl<'a> RequestDispatcher<'a> {
3232
};
3333
let global_state = panic::AssertUnwindSafe(&mut *self.global_state);
3434

35-
let response = panic::catch_unwind(move || {
35+
let result = panic::catch_unwind(move || {
3636
let _ = &global_state;
3737
let panic::AssertUnwindSafe(global_state) = global_state;
3838
let _pctx = stdx::panic_context::enter(format!(
@@ -41,10 +41,10 @@ impl<'a> RequestDispatcher<'a> {
4141
R::METHOD,
4242
params
4343
));
44-
let result = f(global_state, params);
45-
result_to_response::<R>(id, result)
46-
})
47-
.map_err(|_err| format!("sync task {:?} panicked", R::METHOD))?;
44+
f(global_state, params)
45+
});
46+
let response = result_to_response::<R>(id, result);
47+
4848
self.global_state.respond(response);
4949
Ok(self)
5050
}
@@ -56,7 +56,7 @@ impl<'a> RequestDispatcher<'a> {
5656
) -> &mut Self
5757
where
5858
R: lsp_types::request::Request + 'static,
59-
R::Params: DeserializeOwned + Send + fmt::Debug + 'static,
59+
R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug + 'static,
6060
R::Result: Serialize + 'static,
6161
{
6262
let (id, params) = match self.parse::<R>() {
@@ -66,16 +66,18 @@ impl<'a> RequestDispatcher<'a> {
6666

6767
self.global_state.task_pool.handle.spawn({
6868
let world = self.global_state.snapshot();
69-
7069
move || {
71-
let _pctx = stdx::panic_context::enter(format!(
72-
"\nversion: {}\nrequest: {} {:#?}",
73-
env!("REV"),
74-
R::METHOD,
75-
params
76-
));
77-
let result = f(world, params);
78-
Task::Response(result_to_response::<R>(id, result))
70+
let result = panic::catch_unwind(move || {
71+
let _pctx = stdx::panic_context::enter(format!(
72+
"\nversion: {}\nrequest: {} {:#?}",
73+
env!("REV"),
74+
R::METHOD,
75+
params
76+
));
77+
f(world, params)
78+
});
79+
let response = result_to_response::<R>(id, result);
80+
Task::Response(response)
7981
}
8082
});
8183

@@ -122,16 +124,16 @@ impl<'a> RequestDispatcher<'a> {
122124

123125
fn result_to_response<R>(
124126
id: lsp_server::RequestId,
125-
result: Result<R::Result>,
127+
result: thread::Result<Result<R::Result>>,
126128
) -> lsp_server::Response
127129
where
128130
R: lsp_types::request::Request + 'static,
129131
R::Params: DeserializeOwned + 'static,
130132
R::Result: Serialize + 'static,
131133
{
132134
match result {
133-
Ok(resp) => lsp_server::Response::new_ok(id, &resp),
134-
Err(e) => match e.downcast::<LspError>() {
135+
Ok(Ok(resp)) => lsp_server::Response::new_ok(id, &resp),
136+
Ok(Err(e)) => match e.downcast::<LspError>() {
135137
Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message),
136138
Err(e) => {
137139
if is_cancelled(&*e) {
@@ -149,6 +151,21 @@ where
149151
}
150152
}
151153
},
154+
Err(panic) => {
155+
let mut message = "server panicked".to_string();
156+
157+
let panic_message = panic
158+
.downcast_ref::<String>()
159+
.map(String::as_str)
160+
.or_else(|| panic.downcast_ref::<&str>().copied());
161+
162+
if let Some(panic_message) = panic_message {
163+
message.push_str(": ");
164+
message.push_str(panic_message)
165+
};
166+
167+
lsp_server::Response::new_err(id, lsp_server::ErrorCode::InternalError as i32, message)
168+
}
152169
}
153170
}
154171

crates/rust-analyzer/src/global_state.rs

+8
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ pub(crate) struct GlobalStateSnapshot {
116116
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
117117
}
118118

119+
impl std::panic::UnwindSafe for GlobalStateSnapshot {}
120+
119121
impl GlobalState {
120122
pub(crate) fn new(sender: Sender<lsp_server::Message>, config: Config) -> GlobalState {
121123
let loader = {
@@ -262,6 +264,12 @@ impl GlobalState {
262264
}
263265
pub(crate) fn respond(&mut self, response: lsp_server::Response) {
264266
if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) {
267+
if let Some(err) = &response.error {
268+
if err.message.starts_with("server panicked") {
269+
self.poke_rust_analyzer_developer(format!("{}, check the log", err.message))
270+
}
271+
}
272+
265273
let duration = start.elapsed();
266274
log::info!("handled {} - ({}) in {:0.2?}", method, response.id, duration);
267275
self.send(response.into());

crates/rust-analyzer/src/lsp_utils.rs

+19
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,25 @@ impl GlobalState {
4242
)
4343
}
4444

45+
/// rust-analyzer is resilient -- if it fails, this doesn't usually affect
46+
/// the user experience. Part of that is that we deliberately hide panics
47+
/// from the user.
48+
///
49+
/// We do however want to pester rust-analyzer developers with panics and
50+
/// other "you really gotta fix that" messages. The current strategy is to
51+
/// be noisy for "from source" builds or when profiling is enabled.
52+
///
53+
/// It's unclear if making from source `cargo xtask install` builds more
54+
/// panicky is a good idea, let's see if we can keep our awesome bleeding
55+
/// edge users from being upset!
56+
pub(crate) fn poke_rust_analyzer_developer(&mut self, message: String) {
57+
let from_source_build = env!("REV").contains("dev");
58+
let profiling_enabled = std::env::var("RA_PROFILE").is_ok();
59+
if from_source_build || profiling_enabled {
60+
self.show_message(lsp_types::MessageType::Error, message)
61+
}
62+
}
63+
4564
pub(crate) fn report_progress(
4665
&mut self,
4766
title: &str,

crates/rust-analyzer/src/main_loop.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! The main loop of `rust-analyzer` responsible for dispatching LSP
22
//! requests/replies and notifications back to the client.
33
use std::{
4-
env, fmt,
4+
fmt,
55
sync::Arc,
66
time::{Duration, Instant},
77
};
@@ -487,12 +487,10 @@ impl GlobalState {
487487
let loop_duration = loop_start.elapsed();
488488
if loop_duration > Duration::from_millis(100) {
489489
log::warn!("overly long loop turn: {:?}", loop_duration);
490-
if env::var("RA_PROFILE").is_ok() {
491-
self.show_message(
492-
lsp_types::MessageType::Error,
493-
format!("overly long loop turn: {:?}", loop_duration),
494-
)
495-
}
490+
self.poke_rust_analyzer_developer(format!(
491+
"overly long loop turn: {:?}",
492+
loop_duration
493+
));
496494
}
497495
Ok(())
498496
}

0 commit comments

Comments
 (0)