Skip to content

Commit 1b283db

Browse files
committed
Auto merge of #17526 - Veykril:proc-server-errors, r=Veykril
internal: Improve error message when the proc-macro server unexpectedly exits
2 parents e6fd485 + c6709ff commit 1b283db

File tree

4 files changed

+79
-37
lines changed

4 files changed

+79
-37
lines changed

crates/proc-macro-api/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ impl ProcMacroServer {
130130
Err(message) => Err(ServerError { message, io: None }),
131131
}
132132
}
133+
134+
pub fn exited(&self) -> Option<&ServerError> {
135+
self.process.exited()
136+
}
133137
}
134138

135139
impl ProcMacro {

crates/proc-macro-api/src/process.rs

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
33
use std::{
44
io::{self, BufRead, BufReader, Read, Write},
5+
panic::AssertUnwindSafe,
56
process::{Child, ChildStdin, ChildStdout, Command, Stdio},
6-
sync::{Arc, Mutex},
7+
sync::{Arc, Mutex, OnceLock},
78
};
89

910
use paths::AbsPath;
@@ -22,15 +23,15 @@ pub(crate) struct ProcMacroProcessSrv {
2223
state: Mutex<ProcessSrvState>,
2324
version: u32,
2425
mode: SpanMode,
26+
/// Populated when the server exits.
27+
exited: OnceLock<AssertUnwindSafe<ServerError>>,
2528
}
2629

2730
#[derive(Debug)]
2831
struct ProcessSrvState {
2932
process: Process,
3033
stdin: ChildStdin,
3134
stdout: BufReader<ChildStdout>,
32-
/// Populated when the server exits.
33-
server_exited: Option<ServerError>,
3435
}
3536

3637
impl ProcMacroProcessSrv {
@@ -44,9 +45,10 @@ impl ProcMacroProcessSrv {
4445
let (stdin, stdout) = process.stdio().expect("couldn't access child stdio");
4546

4647
io::Result::Ok(ProcMacroProcessSrv {
47-
state: Mutex::new(ProcessSrvState { process, stdin, stdout, server_exited: None }),
48+
state: Mutex::new(ProcessSrvState { process, stdin, stdout }),
4849
version: 0,
4950
mode: SpanMode::Id,
51+
exited: OnceLock::new(),
5052
})
5153
};
5254
let mut srv = create_srv(true)?;
@@ -77,6 +79,10 @@ impl ProcMacroProcessSrv {
7779
}
7880
}
7981

82+
pub(crate) fn exited(&self) -> Option<&ServerError> {
83+
self.exited.get().map(|it| &it.0)
84+
}
85+
8086
pub(crate) fn version(&self) -> u32 {
8187
self.version
8288
}
@@ -118,36 +124,52 @@ impl ProcMacroProcessSrv {
118124
}
119125

120126
pub(crate) fn send_task(&self, req: Request) -> Result<Response, ServerError> {
121-
let state = &mut *self.state.lock().unwrap();
122-
if let Some(server_error) = &state.server_exited {
123-
return Err(server_error.clone());
127+
if let Some(server_error) = self.exited.get() {
128+
return Err(server_error.0.clone());
124129
}
125130

131+
let state = &mut *self.state.lock().unwrap();
126132
let mut buf = String::new();
127-
send_request(&mut state.stdin, &mut state.stdout, req, &mut buf).map_err(|e| {
128-
if e.io.as_ref().map(|it| it.kind()) == Some(io::ErrorKind::BrokenPipe) {
129-
match state.process.child.try_wait() {
130-
Ok(None) => e,
131-
Ok(Some(status)) => {
132-
let mut msg = String::new();
133-
if !status.success() {
134-
if let Some(stderr) = state.process.child.stderr.as_mut() {
135-
_ = stderr.read_to_string(&mut msg);
133+
send_request(&mut state.stdin, &mut state.stdout, req, &mut buf)
134+
.and_then(|res| {
135+
res.ok_or_else(|| {
136+
let message = "proc-macro server did not respond with data".to_owned();
137+
ServerError {
138+
io: Some(Arc::new(io::Error::new(
139+
io::ErrorKind::BrokenPipe,
140+
message.clone(),
141+
))),
142+
message,
143+
}
144+
})
145+
})
146+
.map_err(|e| {
147+
if e.io.as_ref().map(|it| it.kind()) == Some(io::ErrorKind::BrokenPipe) {
148+
match state.process.child.try_wait() {
149+
Ok(None) | Err(_) => e,
150+
Ok(Some(status)) => {
151+
let mut msg = String::new();
152+
if !status.success() {
153+
if let Some(stderr) = state.process.child.stderr.as_mut() {
154+
_ = stderr.read_to_string(&mut msg);
155+
}
136156
}
157+
let server_error = ServerError {
158+
message: format!(
159+
"proc-macro server exited with {status}{}{msg}",
160+
if msg.is_empty() { "" } else { ": " }
161+
),
162+
io: None,
163+
};
164+
// `AssertUnwindSafe` is fine here, we already correct initialized
165+
// server_error at this point.
166+
self.exited.get_or_init(|| AssertUnwindSafe(server_error)).0.clone()
137167
}
138-
let server_error = ServerError {
139-
message: format!("server exited with {status}: {msg}"),
140-
io: None,
141-
};
142-
state.server_exited = Some(server_error.clone());
143-
server_error
144168
}
145-
Err(_) => e,
169+
} else {
170+
e
146171
}
147-
} else {
148-
e
149-
}
150-
})
172+
})
151173
}
152174
}
153175

@@ -201,7 +223,7 @@ fn send_request(
201223
mut reader: &mut impl BufRead,
202224
req: Request,
203225
buf: &mut String,
204-
) -> Result<Response, ServerError> {
226+
) -> Result<Option<Response>, ServerError> {
205227
req.write(write_json, &mut writer).map_err(|err| ServerError {
206228
message: "failed to write request".into(),
207229
io: Some(Arc::new(err)),
@@ -210,5 +232,5 @@ fn send_request(
210232
message: "failed to read response".into(),
211233
io: Some(Arc::new(err)),
212234
})?;
213-
res.ok_or_else(|| ServerError { message: "server exited".into(), io: None })
235+
Ok(res)
214236
}

crates/proc-macro-srv/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ fn expand_ra_span(
204204
let macro_body = macro_body.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table);
205205
let attributes =
206206
attributes.map(|it| it.to_subtree_resolved(CURRENT_API_VERSION, &span_data_table));
207+
// Note, we spawn a new thread here so that thread locals allocation don't accumulate (this
208+
// includes the proc-macro symbol interner)
207209
let result = thread::scope(|s| {
208210
let thread = thread::Builder::new()
209211
.stack_size(EXPANDER_STACK_SIZE)

crates/rust-analyzer/src/reload.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -184,14 +184,28 @@ impl GlobalState {
184184
message.push_str(err);
185185
message.push_str("\n\n");
186186
};
187-
if let Some(Err(err)) = proc_macro_client {
188-
status.health |= lsp_ext::Health::Warning;
189-
format_to!(
190-
message,
191-
"Failed spawning proc-macro server for workspace `{}`: {err}",
192-
ws.manifest_or_root()
193-
);
194-
message.push_str("\n\n");
187+
match proc_macro_client {
188+
Some(Err(err)) => {
189+
status.health |= lsp_ext::Health::Warning;
190+
format_to!(
191+
message,
192+
"Failed spawning proc-macro server for workspace `{}`: {err}",
193+
ws.manifest_or_root()
194+
);
195+
message.push_str("\n\n");
196+
}
197+
Some(Ok(client)) => {
198+
if let Some(err) = client.exited() {
199+
status.health |= lsp_ext::Health::Warning;
200+
format_to!(
201+
message,
202+
"proc-macro server for workspace `{}` exited: {err}",
203+
ws.manifest_or_root()
204+
);
205+
message.push_str("\n\n");
206+
}
207+
}
208+
_ => (),
195209
}
196210
}
197211
}

0 commit comments

Comments
 (0)