Sitelet https://github.com/franfrandev/compilers/commit/7662f5c679847964411dc4f7e047f60af1c74ed7
Skip to content

Commit 7662f5c

Browse files
authored
fix: handle displaying multiline errors correctly (foundry-rs#245)
Before + after ![image](https://github.com/user-attachments/assets/fa0a5db3-2a64-4c30-9cb6-6aea30175e7c) Incredibly cursed thanks to the awesome consistent implementation in solc
1 parent 5a1f7a0 commit 7662f5c

1 file changed

Lines changed: 45 additions & 18 deletions

File tree

‎crates/artifacts/solc/src/error.rs‎

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize};
33
use std::{fmt, ops::Range, str::FromStr};
44
use yansi::{Color, Style};
55

6+
const ARROW: &str = "-->";
7+
68
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
79
pub struct SourceLocation {
810
pub file: String,
@@ -146,22 +148,30 @@ impl fmt::Display for Error {
146148

147149
let mut lines = fmtd_msg.lines();
148150

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();
151+
if let Some(l) = lines.clone().next() {
152+
if l.bytes().filter(|&b| b == b':').count() >= 3
153+
&& (l.contains(['/', '\\']) || l.contains(".sol"))
154+
{
155+
// This is an old style error message, like:
156+
// path/to/file:line:column: ErrorType: message
157+
// We want to display this as-is.
158+
} else {
159+
// Otherwise, assume that the messages are the same until we find a source
160+
// location.
161+
lines.next();
162+
while let Some(line) = lines.clone().next() {
163+
if line.contains(ARROW) {
164+
break;
165+
}
166+
lines.next();
167+
}
168+
}
159169
}
160170

161-
// format the main source location
171+
// Format the main source location.
162172
fmt_source_location(f, &mut lines)?;
163173

164-
// format remaining lines as secondary locations
174+
// Format remaining lines as secondary locations.
165175
while let Some(line) = lines.next() {
166176
f.write_str("\n")?;
167177

@@ -260,11 +270,9 @@ fn fmt_source_location(f: &mut fmt::Formatter<'_>, lines: &mut std::str::Lines<'
260270
// --> source
261271
if let Some(line) = lines.next() {
262272
f.write_str("\n")?;
263-
264-
let arrow = "-->";
265-
if let Some((left, loc)) = line.split_once(arrow) {
273+
if let Some((left, loc)) = line.split_once(ARROW) {
266274
f.write_str(left)?;
267-
styled(f, Error::frame_style(), |f| f.write_str(arrow))?;
275+
styled(f, Error::frame_style(), |f| f.write_str(ARROW))?;
268276
f.write_str(loc)?;
269277
} else {
270278
f.write_str(line)?;
@@ -404,7 +412,7 @@ mod tests {
404412
}
405413

406414
#[test]
407-
fn solc_not_formatting_the_message1() {
415+
fn no_source_location() {
408416
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"}"#;
409417
let error = serde_json::from_str::<Error>(error).unwrap();
410418
let s = error.to_string();
@@ -414,12 +422,31 @@ mod tests {
414422
}
415423

416424
#[test]
417-
fn solc_not_formatting_the_message2() {
425+
fn no_source_location2() {
418426
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"}"#;
419427
let error = serde_json::from_str::<Error>(error).unwrap();
420428
let s = error.to_string();
421429
eprintln!("{s}");
422430
assert!(s.contains("Warning (5667)"), "\n{s}");
423431
assert!(s.contains("Unused function parameter. Remove or comment out the variable name to silence this warning."), "\n{s}");
424432
}
433+
434+
#[test]
435+
fn stack_too_deep_multiline() {
436+
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"}"#;
437+
let error = serde_json::from_str::<Error>(error).unwrap();
438+
let s = error.to_string();
439+
eprintln!("{s}");
440+
assert_eq!(s.match_indices("Cannot swap Variable _23").count(), 1, "\n{s}");
441+
assert!(s.contains("-->"), "\n{s}");
442+
}
443+
444+
#[test]
445+
fn stack_too_deep_no_source_location() {
446+
let error = r#"{"type":"CompilerError","component":"general","severity":"error","errorCode":null,"message":"Compiler error (/solidity/libyul/backends/evm/AsmCodeGen.cpp:63):Stack too deep. Try compiling with `--via-ir` (cli) or the equivalent `viaIR: true` (standard JSON) while enabling the optimizer. Otherwise, try removing local variables. When compiling inline assembly: Variable key_ is 2 slot(s) too deep inside the stack. Stack too deep. Try compiling with `--via-ir` (cli) or the equivalent `viaIR: true` (standard JSON) while enabling the optimizer. Otherwise, try removing local variables.","formattedMessage":"CompilerError: Stack too deep. Try compiling with `--via-ir` (cli) or the equivalent `viaIR: true` (standard JSON) while enabling the optimizer. Otherwise, try removing local variables. When compiling inline assembly: Variable key_ is 2 slot(s) too deep inside the stack. Stack too deep. Try compiling with `--via-ir` (cli) or the equivalent `viaIR: true` (standard JSON) while enabling the optimizer. Otherwise, try removing local variables.\n\n"}"#;
447+
let error = serde_json::from_str::<Error>(error).unwrap();
448+
let s = error.to_string();
449+
eprintln!("{s}");
450+
assert_eq!(s.match_indices("too deep inside the stack.").count(), 1, "\n{s}");
451+
}
425452
}

0 commit comments

Comments
 (0)