zesterer / ariadne

A fancy diagnostics & error reporting crate
https://crates.io/crates/ariadne
MIT License
1.74k stars 77 forks source link

Labels fail to print correctly with newlines #4

Open gamma-delta opened 3 years ago

gamma-delta commented 3 years ago

Hello, I am trying to use ariadne for error reporting for a crate. I am getting a problem with printing Reports that span multiple lines.

Here is some example code:

#[test]
fn ariadne() {
    let source = "(\r\n\r\n}";

    Report::build(ReportKind::Error, (), 0)
        .with_config(Config::default().with_char_set(CharSet::Ascii))
        .with_label(Label::new(0..1).with_message("opens here"))
        .with_label(Label::new(5..6).with_message("closes here"))
        .finish()
        .eprint(Source::from(source))
        .unwrap();
}

and it produces this:

Error: 
   ,-[<unknown>:1:1]
   |
 1 | (
   : |  
   : `-- opens here
---'

What should happen: The start and end lines are displayed along with both labels

What actually happens: Only the start line is displayed; the second label is completely absent.


I cannot figure out what causes it to go wrong but my hunch is that the span from 5..6 is the problem because 6 is "out of bounds" of the string (even though it's used as the exclusive endpoint).

gamma-delta commented 3 years ago

I should note that it displays perfectly fine if there aren't carriage returns (even if the range's end is equal to the length of the string.) Replacing the \r in the string with r makes it print totally fine.

zesterer commented 3 years ago

I will try to find the time to look into this tomorrow. I think it should probably be quite a simple fix.

gamma-delta commented 3 years ago

I have done some more investigating and this appears to only happen with carriage returns in the source code. Here's an example:

#[test]
fn ariadne_borked() {
    let test_str = r#"(my 
(complex 
    lisp 123
    with 
    (a bad token here))!
(oh the horror)"#;

    let lf = test_str.replace("\r\n", "\n");
    let crlf = lf.replace("\n", "\r\n");

    for (test_name, source) in [("lf", lf), ("crlf", crlf)] {
        let bad_pos = source.as_str().find('!').unwrap();
        println!("{}:", test_name);
        let error = Report::build(ReportKind::Error, (), bad_pos)
            .with_label(Label::new(bad_pos..bad_pos + 1).with_message("this token is invalid"))
            .finish();
        error.eprint(Source::from(source)).unwrap();
    }
}

and here's its output:

lf:
Error:
   ╭─[<unknown>:5:24]
   │
 5 │     (a bad token here))!
   ·                        ┬
   ·                        ╰── this token is invalid
───╯
crlf:
Error:
   ╭─[<unknown>:6:3]
   │
 6 │ (oh the horror)
   ·   ┬  
   ·   ╰── this token is invalid
───╯
zesterer commented 3 years ago

Thanks for the further investigation. Unfortunately, I'm away for the next 10 days so am unable to fix this (unless I can find time to do so). Is this a high-priority issue for you? If so, you could temporarily resolve it by replacing carriage return characters in your source files with spaces or some other invisible character. I believe that this should work.

gamma-delta commented 3 years ago

Yes .replace('\r', ""); is working great for me.

zesterer commented 3 years ago

Great, although it's worth mentioning that removing the character entirely will result in the wrong character offset being used for later lines! You'll probably need to replace it with an actual character.

gamma-delta commented 3 years ago

Sorry I meant i am stripping it out of the text before I parse it at all. my parser/error handler never gets a string with an \r in it at all.