Skip to content

Commit 61f86fe

Browse files
committed
fix: handle displaying multiline errors correctly
1 parent 5a1f7a0 commit 61f86fe

File tree

1 file changed

+40
-14
lines changed

1 file changed

+40
-14
lines changed

crates/artifacts/solc/src/error.rs

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -146,22 +146,38 @@ impl fmt::Display for Error {
146146

147147
let mut lines = fmtd_msg.lines();
148148

149-
// skip the first line if it contains the same message as the one we just formatted,
150-
// unless it also contains a source location, in which case the entire error message is an
151-
// old style error message, like:
152-
// path/to/file:line:column: ErrorType: message
153-
if lines
154-
.clone()
155-
.next()
156-
.is_some_and(|l| l.contains(short_msg) && l.bytes().filter(|b| *b == b':').count() < 3)
157-
{
158-
let _ = lines.next();
149+
if let Some(l) = lines.clone().next() {
150+
// For whatever reason, exceptions are formatted differently in `message` and
151+
// `formattedMessage`:
152+
// YulException:Cannot swap...
153+
// vs:
154+
// YulException: Cannot swap...
155+
fn after_colon(s: &str) -> &str {
156+
s.find(':').map(|i| s[i + 1..].trim()).unwrap_or(s)
157+
}
158+
159+
let sl = short_msg.lines().next().unwrap_or("");
160+
if l.contains(sl) || after_colon(l).contains(after_colon(sl)) {
161+
// `fmtd_msg` contains `short_msg`.
162+
if l.bytes().filter(|b| *b == b':').count() >= 3 {
163+
// This is an old style error message, like:
164+
// path/to/file:line:column: ErrorType: message
165+
// We want to display this as-is.
166+
} else {
167+
// Otherwise, assume that the messages are the same until we find a source
168+
// location.
169+
lines.next();
170+
while lines.clone().next().is_some_and(|l| !l.contains("-->")) {
171+
lines.next();
172+
}
173+
}
174+
}
159175
}
160176

161-
// format the main source location
177+
// Format the main source location.
162178
fmt_source_location(f, &mut lines)?;
163179

164-
// format remaining lines as secondary locations
180+
// Format remaining lines as secondary locations.
165181
while let Some(line) = lines.next() {
166182
f.write_str("\n")?;
167183

@@ -404,7 +420,7 @@ mod tests {
404420
}
405421

406422
#[test]
407-
fn solc_not_formatting_the_message1() {
423+
fn no_source_location() {
408424
let error = r#"{"component":"general","errorCode":"6553","formattedMessage":"SyntaxError: The msize instruction cannot be used when the Yul optimizer is activated because it can change its semantics. Either disable the Yul optimizer or do not use the instruction.\n\n","message":"The msize instruction cannot be used when the Yul optimizer is activated because it can change its semantics. Either disable the Yul optimizer or do not use the instruction.","severity":"error","sourceLocation":{"end":173,"file":"","start":114},"type":"SyntaxError"}"#;
409425
let error = serde_json::from_str::<Error>(error).unwrap();
410426
let s = error.to_string();
@@ -414,12 +430,22 @@ mod tests {
414430
}
415431

416432
#[test]
417-
fn solc_not_formatting_the_message2() {
433+
fn no_source_location2() {
418434
let error = r#"{"component":"general","errorCode":"5667","formattedMessage":"Warning: Unused function parameter. Remove or comment out the variable name to silence this warning.\n\n","message":"Unused function parameter. Remove or comment out the variable name to silence this warning.","severity":"warning","sourceLocation":{"end":104,"file":"","start":95},"type":"Warning"}"#;
419435
let error = serde_json::from_str::<Error>(error).unwrap();
420436
let s = error.to_string();
421437
eprintln!("{s}");
422438
assert!(s.contains("Warning (5667)"), "\n{s}");
423439
assert!(s.contains("Unused function parameter. Remove or comment out the variable name to silence this warning."), "\n{s}");
424440
}
441+
442+
#[test]
443+
fn stack_too_deep_multiline() {
444+
let error = r#"{"sourceLocation":{"file":"test/LibMap.t.sol","start":15084,"end":15113},"type":"YulException","component":"general","severity":"error","errorCode":null,"message":"Yul exception:Cannot swap Variable _23 with Slot RET[fun_assertEq]: too deep in the stack by 1 slots in [ var_136614_mpos RET _23 _21 _23 var_map_136608_slot _34 _34 _29 _33 _33 _39 expr_48 var_bitWidth var_map_136608_slot _26 _29 var_bitWidth TMP[eq, 0] RET[fun_assertEq] ]\nmemoryguard was present.","formattedMessage":"YulException: Cannot swap Variable _23 with Slot RET[fun_assertEq]: too deep in the stack by 1 slots in [ var_136614_mpos RET _23 _21 _23 var_map_136608_slot _34 _34 _29 _33 _33 _39 expr_48 var_bitWidth var_map_136608_slot _26 _29 var_bitWidth TMP[eq, 0] RET[fun_assertEq] ]\nmemoryguard was present.\n --> test/LibMap.t.sol:461:34:\n |\n461 | uint256 end = t.o - (t.o > 0 ? _random() % t.o : 0);\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n"}"#;
445+
let error = serde_json::from_str::<Error>(error).unwrap();
446+
let s = error.to_string();
447+
eprintln!("{s}");
448+
assert_eq!(s.match_indices("Cannot swap Variable _23").count(), 1, "\n{s}");
449+
assert!(s.contains("-->"), "\n{s}");
450+
}
425451
}

0 commit comments

Comments
 (0)