Skip to content

Commit a8d935e

Browse files
committed
Auto merge of #16226 - Veykril:lsp-server, r=Veykril
internal: Expose whether a channel has been dropped in lsp-server errors Not the best way to expose this, but this should allow us to give somewhat better errors when the initialization request is malformed, as currently that just results in a channel disconnected error instead of the deserialization error. cc rust-lang/rust-analyzer#15859
2 parents aef441a + 3c8dd9e commit a8d935e

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)