-
Notifications
You must be signed in to change notification settings - Fork 45
Description
First, thanks a ton for creating and maintaining this library (and apologies for the mouthful of a issue title).
I'm reporting this based on user behavior observed in zizmorcore/zizmor#1065 -- we use annotate-snippets
to render static analysis findings, and a user observed a crash on an input that appears to be well-formed and is only accessed via valid UTF-8 spans within the application code itself.
The conditions for the panic are pretty subtle:
- The snippet's source must contain multiple lines, but the final line shouldn't have a trailing linefeed;
- The snippet must have multiple annotations;
- The last character in the snippet should be multibyte (e.g. an emoji).
When put together, these conditions cause a panic when rendering multiple annotations that span to the end of the snippet's source. This panic doesn't happen when the snippet's source ends in a final linefeed.
Here's my attempt at a minified reproducer for the above, with the latest release of annotated-snippets
(0.11.5):
use annotate_snippets::{Level, Renderer, Snippet};
fn main() {
let good = r#"foobar
foobar 🚀
"#;
let snippet = Snippet::source(good)
.fold(true)
.line_start(1)
.origin("whatever")
.annotation(Level::Warning.span(0..good.len()).label("blah"))
.annotation(Level::Warning.span(0..good.len()).label("blah"));
let message = Level::Warning.title("whatever").snippet(snippet);
let renderer = Renderer::styled();
println!("{}", renderer.render(message));
let bad = r#"foobar
foobar 🚀"#;
let snippet = Snippet::source(bad)
.fold(true)
.line_start(1)
.origin("whatever")
.annotation(Level::Warning.span(0..bad.len()).label("blah"))
.annotation(Level::Warning.span(0..bad.len()).label("blah"));
let message = Level::Warning.title("whatever").snippet(snippet);
let renderer = Renderer::styled();
println!("{}", renderer.render(message));
}
Observe that the only difference between good
and bad
is that good
has a trailing linefeed, while bad
does not. In both cases, the annotation spans are valid UTF-8 slices.
Here's what the panic looks like (after a successful render of good
first):
$ ./target/debug/repro
warning: whatever
--> whatever:1:1
|
1 | / foobar
2 | |
3 | | foobar 🚀
| | -
| |______________________|
| blah
| blah
|
thread 'main' panicked at /home/william/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/annotate-snippets-0.11.5/src/renderer/display_list.rs:1440:29:
byte index 22 is not a char boundary; it is inside '🚀' (bytes 19..23) of ` foobar 🚀`
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Specifically, it looks like the panicking slice happens here:
annotate-snippets-rs/src/renderer/display_list.rs
Line 1440 in 72dd8c7
[0..(start - line_start_index).min(line_length)] |
It looks like this file no longer exists on main
, so it's possible this bug has been addressed in a refactor that hasn't been put in a release yet. If so, I apologize for the noise!