Skip to content

Commit 337efde

Browse files
committed
Don't include backtrace in syntax errors
1 parent 492d33c commit 337efde

File tree

3 files changed

+61
-32
lines changed

3 files changed

+61
-32
lines changed

crates/amalthea/src/error.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ pub enum Error {
4343
UnknownCommName(String),
4444
UnknownCommId(String),
4545
InvalidCommMessage(String, String, String),
46-
InvalidInputRequest(String),
4746
InvalidConsoleInput(String),
4847
Anyhow(anyhow::Error),
4948
ShellErrorReply(Exception),
@@ -196,9 +195,6 @@ impl fmt::Display for Error {
196195
msg, id, err
197196
)
198197
},
199-
Error::InvalidInputRequest(message) => {
200-
write!(f, "{message}")
201-
},
202198
Error::InvalidConsoleInput(message) => {
203199
write!(f, "{message}")
204200
},

crates/ark/src/interface.rs

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -303,13 +303,18 @@ struct PendingInputs {
303303
index: isize,
304304
}
305305

306+
enum ParseResult<T> {
307+
Success(Option<T>),
308+
SyntaxError(String),
309+
}
310+
306311
impl PendingInputs {
307-
pub(crate) fn read(input: &str) -> anyhow::Result<Option<Self>> {
312+
pub(crate) fn read(input: &str) -> anyhow::Result<ParseResult<PendingInputs>> {
308313
let status = match harp::parse_status(&harp::ParseInput::Text(input)) {
309314
Err(err) => {
310315
// Failed to even attempt to parse the input, something is seriously wrong
311316
// FIXME: There are some valid syntax errors going through here, e.g. `identity |> _(1)`.
312-
return Err(anyhow!("Failed to parse input: {err:?}"));
317+
return Ok(ParseResult::SyntaxError(format!("{err}")));
313318
},
314319
Ok(status) => status,
315320
};
@@ -321,10 +326,12 @@ impl PendingInputs {
321326
let exprs = match status {
322327
harp::ParseResult::Complete(exprs) => exprs,
323328
harp::ParseResult::Incomplete => {
324-
return Err(anyhow!("Can't execute incomplete input:\n{input}"));
329+
return Ok(ParseResult::SyntaxError(format!(
330+
"Can't execute incomplete input:\n{input}"
331+
)));
325332
},
326333
harp::ParseResult::SyntaxError { message, .. } => {
327-
return Err(anyhow!("Syntax error: {message}"));
334+
return Ok(ParseResult::SyntaxError(format!("Syntax error: {message}")));
328335
},
329336
};
330337

@@ -334,15 +341,15 @@ impl PendingInputs {
334341
let index = 0;
335342

336343
if len == 0 {
337-
return Ok(None);
344+
return Ok(ParseResult::Success(None));
338345
}
339346

340-
Ok(Some(Self {
347+
Ok(ParseResult::Success(Some(Self {
341348
exprs,
342349
srcrefs,
343350
len,
344351
index,
345-
}))
352+
})))
346353
}
347354

348355
pub(crate) fn is_empty(&self) -> bool {
@@ -444,7 +451,7 @@ pub(crate) enum ConsoleResult {
444451
NewPendingInput(PendingInput),
445452
Interrupt,
446453
Disconnected,
447-
Error(amalthea::Error),
454+
Error(String),
448455
}
449456

450457
impl RMain {
@@ -1262,10 +1269,17 @@ impl RMain {
12621269
ConsoleInput::Input(code) => {
12631270
// Parse input into pending expressions
12641271
match PendingInputs::read(&code) {
1265-
Ok(inputs) => {
1272+
Ok(ParseResult::Success(inputs)) => {
12661273
self.pending_inputs = inputs;
12671274
},
1268-
Err(err) => return Some(ConsoleResult::Error(amalthea::anyhow!("{err:?}"))),
1275+
Ok(ParseResult::SyntaxError(message)) => {
1276+
return Some(ConsoleResult::Error(message))
1277+
},
1278+
Err(err) => {
1279+
return Some(ConsoleResult::Error(format!(
1280+
"Error while parsing input: {err:?}"
1281+
)))
1282+
},
12691283
}
12701284

12711285
// Evaluate first expression if there is one
@@ -1439,7 +1453,7 @@ impl RMain {
14391453
log::info!("Detected `readline()` call in renv autoloader. Returning `'{input}'`.");
14401454
match Self::on_console_input(buf, buflen, input) {
14411455
Ok(()) => return ConsoleResult::NewInput,
1442-
Err(err) => return ConsoleResult::Error(err),
1456+
Err(err) => return ConsoleResult::Error(format!("{err}")),
14431457
}
14441458
}
14451459

@@ -1450,7 +1464,7 @@ impl RMain {
14501464
"Are you calling `readline()` or `menu()` from an `.Rprofile` or `.Rprofile.site` file? If so, that is the issue and you should remove that code."
14511465
].join("\n");
14521466

1453-
return ConsoleResult::Error(Error::InvalidInputRequest(message));
1467+
return ConsoleResult::Error(message);
14541468
}
14551469

14561470
fn start_debug(&mut self, debug_preserve_focus: bool) {
@@ -1607,10 +1621,10 @@ impl RMain {
16071621
let input = convert_line_endings(&input.value, LineEnding::Posix);
16081622
match Self::on_console_input(buf, buflen, input) {
16091623
Ok(()) => ConsoleResult::NewInput,
1610-
Err(err) => ConsoleResult::Error(err),
1624+
Err(err) => ConsoleResult::Error(format!("{err:?}")),
16111625
}
16121626
},
1613-
Err(err) => ConsoleResult::Error(err),
1627+
Err(err) => ConsoleResult::Error(format!("{err:?}")),
16141628
}
16151629
}
16161630

@@ -2328,14 +2342,6 @@ impl RMain {
23282342
}
23292343
}
23302344

2331-
fn propagate_error(&mut self, message: String) -> ! {
2332-
// Save error message to `RMain`'s buffer to avoid leaking memory when `Rf_error()` jumps.
2333-
// Some gymnastics are required to deal with the possibility of `CString` conversion failure
2334-
// since the error message comes from the frontend and might be corrupted.
2335-
self.r_error_buffer = Some(new_cstring(message));
2336-
unsafe { Rf_error(self.r_error_buffer.as_ref().unwrap().as_ptr()) }
2337-
}
2338-
23392345
#[cfg(not(test))] // Avoid warnings in unit test
23402346
pub(crate) fn read_console_frame(&self) -> RObject {
23412347
self.read_console_frame.borrow().clone()
@@ -2545,8 +2551,13 @@ fn r_read_console_impl(
25452551
return 0;
25462552
},
25472553

2548-
ConsoleResult::Error(err) => {
2549-
main.propagate_error(format!("{err}"));
2554+
ConsoleResult::Error(message) => {
2555+
// Save error message in `RMain` to avoid leaking memory when
2556+
// `Rf_error()` jumps. Some gymnastics are required to deal with the
2557+
// possibility of `CString` conversion failure since the error
2558+
// message comes from the frontend and might be corrupted.
2559+
main.r_error_buffer = Some(new_cstring(message));
2560+
unsafe { Rf_error(main.r_error_buffer.as_ref().unwrap().as_ptr()) }
25502561
},
25512562
};
25522563
}

crates/ark/tests/kernel.rs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ fn test_execute_request_multiple_lines() {
8686

8787
#[test]
8888
fn test_execute_request_incomplete() {
89+
// Set RUST_BACKTRACE to ensure backtraces are captured. We used to leak
90+
// backtraces in syntax error messages, and this shouldn't happen even when
91+
// `RUST_BACKTRACE` is set.
92+
std::env::set_var("RUST_BACKTRACE", "1");
93+
8994
let frontend = DummyArkFrontend::lock();
9095

9196
let code = "1 +";
@@ -95,9 +100,10 @@ fn test_execute_request_incomplete() {
95100
let input = frontend.recv_iopub_execute_input();
96101
assert_eq!(input.code, code);
97102

98-
assert!(frontend
99-
.recv_iopub_execute_error()
100-
.contains("Can't execute incomplete input"));
103+
assert_eq!(
104+
frontend.recv_iopub_execute_error(),
105+
"Error:\nCan't execute incomplete input:\n1 +"
106+
);
101107

102108
frontend.recv_iopub_idle();
103109

@@ -132,6 +138,11 @@ fn test_execute_request_incomplete_multiple_lines() {
132138

133139
#[test]
134140
fn test_execute_request_invalid() {
141+
// Set RUST_BACKTRACE to ensure backtraces are captured. We used to leak
142+
// backtraces in syntax error messages, and this shouldn't happen even when
143+
// `RUST_BACKTRACE` is set.
144+
std::env::set_var("RUST_BACKTRACE", "1");
145+
135146
let frontend = DummyArkFrontend::lock();
136147

137148
let code = "1 + )";
@@ -141,7 +152,13 @@ fn test_execute_request_invalid() {
141152
let input = frontend.recv_iopub_execute_input();
142153
assert_eq!(input.code, code);
143154

144-
assert!(frontend.recv_iopub_execute_error().contains("Syntax error"));
155+
let error_msg = frontend.recv_iopub_execute_error();
156+
157+
// Expected error
158+
assert!(error_msg.contains("Syntax error"));
159+
160+
// Check that no Rust backtrace is injected in the error message
161+
assert!(!error_msg.contains("Stack backtrace:") && !error_msg.contains("std::backtrace"));
145162

146163
frontend.recv_iopub_idle();
147164

@@ -417,6 +434,11 @@ fn test_execute_request_browser_error() {
417434

418435
#[test]
419436
fn test_execute_request_browser_incomplete() {
437+
// Set RUST_BACKTRACE to ensure backtraces are captured. We used to leak
438+
// backtraces in syntax error messages, and this shouldn't happen even when
439+
// `RUST_BACKTRACE` is set.
440+
std::env::set_var("RUST_BACKTRACE", "1");
441+
420442
let frontend = DummyArkFrontend::lock();
421443

422444
let code = "browser()";

0 commit comments

Comments
 (0)