Skip to content

Commit 3c8dd9e

Browse files
committed
Expose whether a channel has been dropped in lsp-server errors
1 parent 06be1b1 commit 3c8dd9e

File tree

9 files changed

+79
-41
lines changed

9 files changed

+79
-41
lines changed

Cargo.lock

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ test-utils = { path = "./crates/test-utils" }
8888
# In-tree crates that are published separately and follow semver. See lib/README.md
8989
line-index = { version = "0.1.1" }
9090
la-arena = { version = "0.3.1" }
91-
lsp-server = { version = "0.7.4" }
91+
lsp-server = { version = "0.7.6" }
9292

9393
# non-local crates
9494
anyhow = "1.0.75"

crates/rust-analyzer/src/bin/main.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,15 @@ fn run_server() -> anyhow::Result<()> {
172172

173173
let (connection, io_threads) = Connection::stdio();
174174

175-
let (initialize_id, initialize_params) = connection.initialize_start()?;
175+
let (initialize_id, initialize_params) = match connection.initialize_start() {
176+
Ok(it) => it,
177+
Err(e) => {
178+
if e.channel_is_disconnected() {
179+
io_threads.join()?;
180+
}
181+
return Err(e.into());
182+
}
183+
};
176184
tracing::info!("InitializeParams: {}", initialize_params);
177185
let lsp_types::InitializeParams {
178186
root_uri,
@@ -240,7 +248,12 @@ fn run_server() -> anyhow::Result<()> {
240248

241249
let initialize_result = serde_json::to_value(initialize_result).unwrap();
242250

243-
connection.initialize_finish(initialize_id, initialize_result)?;
251+
if let Err(e) = connection.initialize_finish(initialize_id, initialize_result) {
252+
if e.channel_is_disconnected() {
253+
io_threads.join()?;
254+
}
255+
return Err(e.into());
256+
}
244257

245258
if !config.has_linked_projects() && config.detached_files().is_empty() {
246259
config.rediscover_workspaces();

lib/lsp-server/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "lsp-server"
3-
version = "0.7.5"
3+
version = "0.7.6"
44
description = "Generic LSP server scaffold."
55
license = "MIT OR Apache-2.0"
66
repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/lsp-server"
@@ -10,7 +10,7 @@ edition = "2021"
1010
log = "0.4.17"
1111
serde_json = "1.0.108"
1212
serde = { version = "1.0.192", features = ["derive"] }
13-
crossbeam-channel = "0.5.6"
13+
crossbeam-channel = "0.5.8"
1414

1515
[dev-dependencies]
1616
lsp-types = "=0.95"

lib/lsp-server/examples/goto_def.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,15 @@ fn main() -> Result<(), Box<dyn Error + Sync + Send>> {
6464
..Default::default()
6565
})
6666
.unwrap();
67-
let initialization_params = connection.initialize(server_capabilities)?;
67+
let initialization_params = match connection.initialize(server_capabilities) {
68+
Ok(it) => it,
69+
Err(e) => {
70+
if e.channel_is_disconnected() {
71+
io_threads.join()?;
72+
}
73+
return Err(e.into());
74+
}
75+
};
6876
main_loop(connection, initialization_params)?;
6977
io_threads.join()?;
7078

lib/lsp-server/src/error.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,22 @@ use std::fmt;
33
use crate::{Notification, Request};
44

55
#[derive(Debug, Clone, PartialEq)]
6-
pub struct ProtocolError(pub(crate) String);
6+
pub struct ProtocolError(String, bool);
7+
8+
impl ProtocolError {
9+
pub(crate) fn new(msg: impl Into<String>) -> Self {
10+
ProtocolError(msg.into(), false)
11+
}
12+
13+
pub(crate) fn disconnected() -> ProtocolError {
14+
ProtocolError("disconnected channel".into(), true)
15+
}
16+
17+
/// Whether this error occured due to a disconnected channel.
18+
pub fn channel_is_disconnected(&self) -> bool {
19+
self.1
20+
}
21+
}
722

823
impl std::error::Error for ProtocolError {}
924

lib/lsp-server/src/lib.rs

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use std::{
1717
net::{TcpListener, TcpStream, ToSocketAddrs},
1818
};
1919

20-
use crossbeam_channel::{Receiver, RecvTimeoutError, Sender};
20+
use crossbeam_channel::{Receiver, RecvError, RecvTimeoutError, Sender};
2121

2222
pub use crate::{
2323
error::{ExtractError, ProtocolError},
@@ -158,11 +158,7 @@ impl Connection {
158158
Err(RecvTimeoutError::Timeout) => {
159159
continue;
160160
}
161-
Err(e) => {
162-
return Err(ProtocolError(format!(
163-
"expected initialize request, got error: {e}"
164-
)))
165-
}
161+
Err(RecvTimeoutError::Disconnected) => return Err(ProtocolError::disconnected()),
166162
};
167163

168164
match msg {
@@ -181,12 +177,14 @@ impl Connection {
181177
continue;
182178
}
183179
msg => {
184-
return Err(ProtocolError(format!("expected initialize request, got {msg:?}")));
180+
return Err(ProtocolError::new(format!(
181+
"expected initialize request, got {msg:?}"
182+
)));
185183
}
186184
};
187185
}
188186

189-
return Err(ProtocolError(String::from(
187+
return Err(ProtocolError::new(String::from(
190188
"Initialization has been aborted during initialization",
191189
)));
192190
}
@@ -201,12 +199,10 @@ impl Connection {
201199
self.sender.send(resp.into()).unwrap();
202200
match &self.receiver.recv() {
203201
Ok(Message::Notification(n)) if n.is_initialized() => Ok(()),
204-
Ok(msg) => {
205-
Err(ProtocolError(format!(r#"expected initialized notification, got: {msg:?}"#)))
206-
}
207-
Err(e) => {
208-
Err(ProtocolError(format!("expected initialized notification, got error: {e}",)))
209-
}
202+
Ok(msg) => Err(ProtocolError::new(format!(
203+
r#"expected initialized notification, got: {msg:?}"#
204+
))),
205+
Err(RecvError) => Err(ProtocolError::disconnected()),
210206
}
211207
}
212208

@@ -231,10 +227,8 @@ impl Connection {
231227
Err(RecvTimeoutError::Timeout) => {
232228
continue;
233229
}
234-
Err(e) => {
235-
return Err(ProtocolError(format!(
236-
"expected initialized notification, got error: {e}",
237-
)));
230+
Err(RecvTimeoutError::Disconnected) => {
231+
return Err(ProtocolError::disconnected());
238232
}
239233
};
240234

@@ -243,14 +237,14 @@ impl Connection {
243237
return Ok(());
244238
}
245239
msg => {
246-
return Err(ProtocolError(format!(
240+
return Err(ProtocolError::new(format!(
247241
r#"expected initialized notification, got: {msg:?}"#
248242
)));
249243
}
250244
}
251245
}
252246

253-
return Err(ProtocolError(String::from(
247+
return Err(ProtocolError::new(String::from(
254248
"Initialization has been aborted during initialization",
255249
)));
256250
}
@@ -359,9 +353,18 @@ impl Connection {
359353
match &self.receiver.recv_timeout(std::time::Duration::from_secs(30)) {
360354
Ok(Message::Notification(n)) if n.is_exit() => (),
361355
Ok(msg) => {
362-
return Err(ProtocolError(format!("unexpected message during shutdown: {msg:?}")))
356+
return Err(ProtocolError::new(format!(
357+
"unexpected message during shutdown: {msg:?}"
358+
)))
359+
}
360+
Err(RecvTimeoutError::Timeout) => {
361+
return Err(ProtocolError::new(format!("timed out waiting for exit notification")))
362+
}
363+
Err(RecvTimeoutError::Disconnected) => {
364+
return Err(ProtocolError::new(format!(
365+
"channel disconnected waiting for exit notification"
366+
)))
363367
}
364-
Err(e) => return Err(ProtocolError(format!("unexpected error during shutdown: {e}"))),
365368
}
366369
Ok(true)
367370
}
@@ -426,7 +429,7 @@ mod tests {
426429

427430
initialize_start_test(TestCase {
428431
test_messages: vec![notification_msg.clone()],
429-
expected_resp: Err(ProtocolError(format!(
432+
expected_resp: Err(ProtocolError::new(format!(
430433
"expected initialize request, got {:?}",
431434
notification_msg
432435
))),

lib/lsp-server/src/msg.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,12 +264,12 @@ fn read_msg_text(inp: &mut dyn BufRead) -> io::Result<Option<String>> {
264264
let mut parts = buf.splitn(2, ": ");
265265
let header_name = parts.next().unwrap();
266266
let header_value =
267-
parts.next().ok_or_else(|| invalid_data!("malformed header: {:?}", buf))?;
267+
parts.next().ok_or_else(|| invalid_data(format!("malformed header: {:?}", buf)))?;
268268
if header_name.eq_ignore_ascii_case("Content-Length") {
269269
size = Some(header_value.parse::<usize>().map_err(invalid_data)?);
270270
}
271271
}
272-
let size: usize = size.ok_or_else(|| invalid_data!("no Content-Length"))?;
272+
let size: usize = size.ok_or_else(|| invalid_data("no Content-Length".to_string()))?;
273273
let mut buf = buf.into_bytes();
274274
buf.resize(size, 0);
275275
inp.read_exact(&mut buf)?;

lib/lsp-server/src/stdio.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ pub(crate) fn stdio_transport() -> (Sender<Message>, Receiver<Message>, IoThread
1515
let writer = thread::spawn(move || {
1616
let stdout = stdout();
1717
let mut stdout = stdout.lock();
18-
writer_receiver.into_iter().try_for_each(|it| it.write(&mut stdout))?;
19-
Ok(())
18+
writer_receiver.into_iter().try_for_each(|it| it.write(&mut stdout))
2019
});
2120
let (reader_sender, reader_receiver) = bounded::<Message>(0);
2221
let reader = thread::spawn(move || {

0 commit comments

Comments
 (0)