I recently came across a very beautiful way to style source spans in compiler warnings/errors, using some unicode arrows. Might make for nice inspiration: https://twitter.com/_lrlna/status/1539349704080871425?t=7fqNjt72CgD7NkWYnq4iVw&s=09
hooked up source spans and syntax node pointers to the compiler today, and man is it going to be nice once all of the diagnostics are rewritten to use this (tysm @zkat__ for miette!!) https://twitter.com/_lrlna/status/1539349704080871425/photo/1
- ірина (@_lrlna)yeah miette is really cool
might work well for your debug IR format
perhaps after we rewrite that then start doing some proper optimizations there
A language named after a meme, that's hilarious, love it! :laughing:
oh it's a tool?
yeah it's crate for generating these pretty error messages
You run miette? You run her code like the software? Oh. Oh! Error code for coder! Error code for One Thousand Lines!
:open_mouth: I was not aware that it was a library. That's really nice!
Folkert de Vries said:
yeah miette is really cool
Turns out Miette is not the only library doing this.
Once you come across one, you suddenly find many (Baader-Meinhof Phoenomenon?)
There also is 'Ariadne' for Rust which looks like https://raw.githubusercontent.com/zesterer/ariadne/main/misc/example.png
And 'Diagnose' for Haskell which looks like https://github.com/Mesabloo/diagnose/raw/master/assets/real-world-example-unicode.png and also has a nice ASCII fallback.
the inline arrows look great to me, but the outside line + arrows feel like noise. They don't communicate anything as far as I can tell, they just look cool but are distracting :big_smile:
outside as in the long line on the left that connects to the error message above the note/hint
The main reason for that line is -- I think -- to disambugate lines in the source (that have line numbers) from lines containing error/warning information (that is not in the original source).
The flourish connecting it at the top is probably only a taste thing; I also don't really like it.
oh I don't mean the leftmost gray line, I mean the vertical line next to it (light blue in the top screenshot, red in the bottom screenshot)
the line numbers part makes great sense to me :+1:
and I agree about the flourish of connecting it at the top haha
one thing I'm not sure about is how the lines in the middle look with a big multiline type. Maybe the idea is that hopefully those wouldn't come up much?
Good question! Let's see what they do in practice when multiline messages come up
So when a source span is so long that it takes multiple lines, then that is where the extra colored arrows come in. So in the first picture, the whole match in { ... } is used as source span for one of the messages.
The flat lines instead of arrows that Diagnose uses I find nicer for this.
If the message you want to attach to a source span takes multiple lines, then Ariadne currently just breaks (the newlines are printed as-is, disrupting its formatting). Diagnose handles them gracefully:
Some more examples, taken from the Diagnose test suite:
image.png
image.png
image.png
image.png
image.png
hmm, all very interesting
I wonder what happens if you have a long message that needs to point to something that's at the end of a very long line
does the message just span a bunch of lines and get really squished?
the pathological case would be where the arrow needs to point to the rightmost character in the terminal
I guess maybe you just have to put a wrapping width on the source lines that's low enough to give yourself a reasonable buffer :thinking:
it's cool to see that the colored lines along the side are optional, and that there can be multiple colors!
knowing that, I'd be interested to experiment with these at some point
these look closer to how I'd imagine errors in the editor looking someday - more integrated inline, with arrows pointing to the relevant problems right there in the code, rather than confined to a single box off to the side
maybe we could pick an error message (or two) that we have today and experiment with hardcoding an example into this library, just to see how it would look
like taking something from one of our reporting tests, for example
Richard Feldman said:
it's cool to see that the colored lines along the side are optional, and that there can be multiple colors!
This might very well secretly be the real reason a line length of <80 characters is still recommended in many places :stuck_out_tongue_wink:
if someone wants to play around with this, working on/with morphic's debug output might be interesting
errors in there look like this today
fn "mainForHost" (val_0: type_3) -> type_3 {
let val_6 = choice {
case {
let val_1 = make_tuple ();
let val_2 = make_union<type_1, type_0> 0 (val_1);
let val_3 = unwrap_union 1 (val_2);
let val_4 = call["\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"] "UserApp"::"\x00\x00\x00\x00\x0e\x00\x00\x00_\xa8\x0c\xbc5\xf4\x81h\x00\x00\x00\x00\x00\x00\x00\x00\x00`pf..mainForHost`\x00\x00\x00\x00\x00\x00\x00\x00" (val_3);
let val_5 = unknown_with<type_2> (val_4);
val_5
},
} ();
val_6
} where {
type type_0 = ();
type type_1 = ();
type type_2 = ();
type type_3 = ();
}
}
entry_point "mainForHost" = "UserApp"::"mainForHost";
}
thread 'main' panicked at 'Error in alias analysis: error in module ModName("UserApp"), function definition FuncName("<\x00\x00\x00\x06\x00\x00\x00\xe0\xaf\xbe8\xd1\xe6\xeb\xe1\x00\x00\x00\x00\x00\x00\x00\x00\x00`List.loop`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), definition of value binding ValueId(9): expected type '((), (), ((),))', found type '(union { ((),), ((),) }, (), ((),))'', compiler/gen_llvm/src/llvm/build.rs:4166:19
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Last updated: Jun 16 2026 at 16:19 UTC