Prevent compiler errors from terminating the language server process

This commit is contained in:
faldor20 2024-12-02 04:15:33 +10:00
parent f3f262574d
commit b01c516d59
No known key found for this signature in database
GPG key ID: F2216079B890CD57
4 changed files with 70 additions and 46 deletions

1
Cargo.lock generated
View file

@ -2683,6 +2683,7 @@ dependencies = [
"parking_lot",
"roc_can",
"roc_collections",
"roc_error_macros",
"roc_fmt",
"roc_load",
"roc_module",

View file

@ -4,6 +4,8 @@
#[cfg(any(unix, windows, target_arch = "wasm32"))]
use core::fmt;
use core::sync::atomic::{self, AtomicBool};
#[cfg(unix)]
extern "C" {
fn write(fd: i32, buf: *const u8, count: usize) -> isize;
@ -29,6 +31,12 @@ extern "C" {
#[cfg(windows)]
const STD_ERROR_HANDLE: i32 = -12;
static PANIC_NOT_EXIT: AtomicBool = AtomicBool::new(false);
/// You should only call this once to make the compiler panic instead of exiting on an error
pub fn set_panic_not_exit(inp: bool) {
PANIC_NOT_EXIT.store(inp, atomic::Ordering::Relaxed);
}
/// Print each of the given strings to stderr (if it's available; on wasm, nothing will
/// be printed) and then immediately exit the program with an error.
/// On wasm, this will trap, and on UNIX or Windows it will exit with a code of 1.
@ -36,62 +44,67 @@ const STD_ERROR_HANDLE: i32 = -12;
#[cold]
#[cfg(any(unix, windows, target_arch = "wasm32"))]
pub fn error_and_exit(args: fmt::Arguments) -> ! {
use core::panic;
use fmt::Write;
struct StderrWriter;
if PANIC_NOT_EXIT.load(atomic::Ordering::Relaxed) {
panic!("{}", args)
} else {
struct StderrWriter;
impl Write for StderrWriter {
#[cfg(unix)]
fn write_str(&mut self, s: &str) -> fmt::Result {
unsafe {
let _ = write(STDERR_FD, s.as_ptr(), s.len());
impl Write for StderrWriter {
#[cfg(unix)]
fn write_str(&mut self, s: &str) -> fmt::Result {
unsafe {
let _ = write(STDERR_FD, s.as_ptr(), s.len());
}
Ok(())
}
Ok(())
#[cfg(windows)]
fn write_str(&mut self, s: &str) -> fmt::Result {
unsafe {
let handle = GetStdHandle(STD_ERROR_HANDLE);
let mut written = 0;
let _ = WriteFile(
handle,
s.as_ptr(),
s.len() as u32,
&mut written,
core::ptr::null_mut(),
);
}
Ok(())
}
#[cfg(target_arch = "wasm32")]
fn write_str(&mut self, _s: &str) -> fmt::Result {
Ok(())
}
}
let _ = fmt::write(&mut StderrWriter, args);
// Write a newline at the end to make sure stderr gets flushed.
let _ = StderrWriter.write_str("\n");
#[cfg(unix)]
unsafe {
exit(1)
}
#[cfg(windows)]
fn write_str(&mut self, s: &str) -> fmt::Result {
unsafe {
let handle = GetStdHandle(STD_ERROR_HANDLE);
let mut written = 0;
let _ = WriteFile(
handle,
s.as_ptr(),
s.len() as u32,
&mut written,
core::ptr::null_mut(),
);
}
Ok(())
unsafe {
ExitProcess(1)
}
#[cfg(target_arch = "wasm32")]
fn write_str(&mut self, _s: &str) -> fmt::Result {
Ok(())
{
// We have no way to write to any stderr equivalent in wasm,
// so just trap to end the program immediately.
core::arch::wasm32::unreachable()
}
}
let _ = fmt::write(&mut StderrWriter, args);
// Write a newline at the end to make sure stderr gets flushed.
let _ = StderrWriter.write_str("\n");
#[cfg(unix)]
unsafe {
exit(1)
}
#[cfg(windows)]
unsafe {
ExitProcess(1)
}
#[cfg(target_arch = "wasm32")]
{
// We have no way to write to any stderr equivalent in wasm,
// so just trap to end the program immediately.
core::arch::wasm32::unreachable()
}
}
pub const INTERNAL_ERROR_MESSAGE: &str = concat!(

View file

@ -40,3 +40,5 @@ log.workspace = true
indoc.workspace = true
env_logger = "0.10.1"
futures.workspace = true
roc_error_macros.workspace = true

View file

@ -237,7 +237,10 @@ impl LanguageServer for RocServer {
let TextDocumentItem {
uri, text, version, ..
} = params.text_document;
self.change(uri, text, version).await;
let _res = unwind_async(self.change(uri, text, version)).await;
if let Err(e) = _res {
self.client.log_message(MessageType::ERROR, e.message).await
}
}
async fn did_change(&self, params: DidChangeTextDocumentParams) {
@ -251,7 +254,10 @@ impl LanguageServer for RocServer {
.last()
.expect("textDocument change event had no changes ");
self.change(uri, text, version).await;
let _res = unwind_async(self.change(uri, text, version)).await;
if let Err(e) = _res {
self.client.log_message(MessageType::ERROR, e.message).await
}
}
async fn did_close(&self, params: DidCloseTextDocumentParams) {
@ -359,6 +365,8 @@ async fn main() {
let stdout = tokio::io::stdout();
let (service, socket) = LspService::new(RocServer::new);
use roc_error_macros::set_panic_not_exit;
set_panic_not_exit(true);
Server::new(stdin, stdout, socket).serve(service).await;
}