Skip to content

Commit b4839c2

Browse files
committed
Fix backtraces in special syntax errors
1 parent 337efde commit b4839c2

File tree

3 files changed

+77
-15
lines changed

3 files changed

+77
-15
lines changed

crates/ark/tests/kernel.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,54 @@ fn test_execute_request_invalid() {
165165
assert_eq!(
166166
frontend.recv_shell_execute_reply_exception(),
167167
input.execution_count
168-
)
168+
);
169+
170+
// https://github.com/posit-dev/ark/issues/598
171+
let code = "``";
172+
frontend.send_execute_request(code, ExecuteRequestOptions::default());
173+
frontend.recv_iopub_busy();
174+
175+
let input = frontend.recv_iopub_execute_input();
176+
assert_eq!(input.code, code);
177+
178+
let error_msg = frontend.recv_iopub_execute_error();
179+
180+
// Expected error
181+
assert!(error_msg.contains("Syntax error"));
182+
183+
// Check that no Rust backtrace is injected in the error message
184+
assert!(!error_msg.contains("Stack backtrace:") && !error_msg.contains("std::backtrace"));
185+
186+
frontend.recv_iopub_idle();
187+
188+
assert_eq!(
189+
frontend.recv_shell_execute_reply_exception(),
190+
input.execution_count
191+
);
192+
193+
// https://github.com/posit-dev/ark/issues/722
194+
195+
let code = "_ + _()";
196+
frontend.send_execute_request(code, ExecuteRequestOptions::default());
197+
frontend.recv_iopub_busy();
198+
199+
let input = frontend.recv_iopub_execute_input();
200+
assert_eq!(input.code, code);
201+
202+
let error_msg = frontend.recv_iopub_execute_error();
203+
204+
// Expected error
205+
assert!(error_msg.contains("Syntax error"));
206+
207+
// Check that no Rust backtrace is injected in the error message
208+
assert!(!error_msg.contains("Stack backtrace:") && !error_msg.contains("std::backtrace"));
209+
210+
frontend.recv_iopub_idle();
211+
212+
assert_eq!(
213+
frontend.recv_shell_execute_reply_exception(),
214+
input.execution_count
215+
);
169216
}
170217

171218
#[test]

crates/harp/src/error.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ pub enum Error {
4646
InvalidUtf8(Utf8Error),
4747
ParseSyntaxError {
4848
message: String,
49-
line: i32,
5049
},
5150
MissingValueError,
5251
MissingColumnError {
@@ -200,8 +199,8 @@ impl fmt::Display for Error {
200199
write!(f, "Invalid UTF-8 in string: {}", error)
201200
},
202201

203-
Error::ParseSyntaxError { message, line } => {
204-
write!(f, "Syntax error on line {} when parsing: {}", line, message)
202+
Error::ParseSyntaxError { message } => {
203+
write!(f, "Syntax error: {}", message)
205204
},
206205

207206
Error::MissingValueError => {

crates/harp/src/parse.rs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub struct RParseOptions {
2828
pub enum ParseResult {
2929
Complete(RObject),
3030
Incomplete,
31-
SyntaxError { message: String, line: i32 },
31+
SyntaxError { message: String },
3232
}
3333

3434
pub enum ParseInput<'a> {
@@ -79,9 +79,7 @@ pub fn parse_exprs_ext<'a>(input: &ParseInput<'a>) -> crate::Result<RObject> {
7979
code: parse_input_as_string(input).unwrap_or(String::from("Conversion error")),
8080
message: String::from("Incomplete code"),
8181
}),
82-
ParseResult::SyntaxError { message, line } => {
83-
Err(crate::Error::ParseSyntaxError { message, line })
84-
},
82+
ParseResult::SyntaxError { message } => Err(crate::Error::ParseSyntaxError { message }),
8583
}
8684
}
8785

@@ -109,17 +107,34 @@ pub fn parse_status<'a>(input: &ParseInput<'a>) -> crate::Result<ParseResult> {
109107
ParseInput::SrcFile(srcfile) => (srcfile.lines()?, srcfile.inner.clone()),
110108
};
111109

112-
let result: RObject =
113-
try_catch(|| libr::R_ParseVector(text.sexp, -1, &mut status, srcfile.sexp).into())?;
110+
let result =
111+
try_catch(|| libr::R_ParseVector(text.sexp, -1, &mut status, srcfile.sexp).into());
112+
113+
let value = match result {
114+
Ok(value) => value,
115+
Err(err) => match err {
116+
// The parser sometimes throws errors instead of returning an
117+
// error flag. Convert these errors to proper syntax errors so
118+
// we don't leak a backtrace making it seem like an internal
119+
// error.
120+
// https://github.com/posit-dev/ark/issues/598
121+
// https://github.com/posit-dev/ark/issues/722
122+
crate::Error::TryCatchError { message, .. } => {
123+
return Ok(ParseResult::SyntaxError { message });
124+
},
125+
_ => {
126+
return Err(err);
127+
},
128+
},
129+
};
114130

115131
match status {
116-
libr::ParseStatus_PARSE_OK => Ok(ParseResult::Complete(result)),
132+
libr::ParseStatus_PARSE_OK => Ok(ParseResult::Complete(value)),
117133
libr::ParseStatus_PARSE_INCOMPLETE => Ok(ParseResult::Incomplete),
118134
libr::ParseStatus_PARSE_ERROR => Ok(ParseResult::SyntaxError {
119135
message: CStr::from_ptr(libr::get(libr::R_ParseErrorMsg).as_ptr())
120136
.to_string_lossy()
121137
.to_string(),
122-
line: libr::get(libr::R_ParseError) as i32,
123138
}),
124139
_ => {
125140
// Should not get here
@@ -207,15 +222,16 @@ mod tests {
207222
// Error
208223
assert_match!(
209224
parse_status(&ParseInput::Text("42 + _")),
210-
Err(_) => {}
225+
Ok(ParseResult::SyntaxError { message }) => {
226+
assert!(message.contains("invalid use of pipe placeholder"));
227+
}
211228
);
212229

213230
// "normal" syntax error
214231
assert_match!(
215232
parse_status(&ParseInput::Text("1+1\n*42")),
216-
Ok(ParseResult::SyntaxError {message, line}) => {
233+
Ok(ParseResult::SyntaxError { message }) => {
217234
assert!(message.contains("unexpected"));
218-
assert_eq!(line, 2);
219235
}
220236
);
221237

0 commit comments

Comments
 (0)