mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-31 17:17:26 +00:00
Prevent compiler errors from terminating the language server process
This commit is contained in:
parent
f3f262574d
commit
b01c516d59
4 changed files with 70 additions and 46 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2683,6 +2683,7 @@ dependencies = [
|
|||
"parking_lot",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
"roc_error_macros",
|
||||
"roc_fmt",
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -40,3 +40,5 @@ log.workspace = true
|
|||
indoc.workspace = true
|
||||
env_logger = "0.10.1"
|
||||
futures.workspace = true
|
||||
roc_error_macros.workspace = true
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue