mirror of
https://github.com/astral-sh/ruff.git
synced 2025-07-27 23:14:28 +00:00

This is a tiny change that, perhaps slightly shady, permits us to use the `annotate-snippets` renderer without its mandatory header (which wasn't there in `annotate-snippets 0.9`). Specifically, we can now do this: Level::None.title("") The combination of a "none" level and an empty label results in the `annotate-snippets` header being skipped entirely. (Not even an empty line is written.) This is maybe not the right API for upstream `annotate-snippets`, but it's very easy for us to do and unblocks the upgrade (albeit relying on a vendored copy). Ref https://github.com/rust-lang/annotate-snippets-rs/issues/167
992 lines
25 KiB
Rust
992 lines
25 KiB
Rust
// Since this is a vendored copy of `annotate-snippets`, we squash Clippy
|
||
// warnings from upstream in order to the reduce the diff. If our copy drifts
|
||
// far from upstream such that patches become impractical to apply in both
|
||
// places, then we can get rid of these suppressions and fix the lints.
|
||
#![allow(clippy::redundant_clone, clippy::should_panic_without_expect)]
|
||
|
||
use ruff_annotate_snippets::{Level, Renderer, Snippet};
|
||
|
||
use snapbox::{assert_data_eq, str};
|
||
|
||
#[test]
|
||
fn test_i_29() {
|
||
let snippets = Level::Error.title("oops").snippet(
|
||
Snippet::source("First line\r\nSecond oops line")
|
||
.origin("<current file>")
|
||
.annotation(Level::Error.span(19..23).label("oops"))
|
||
.fold(true),
|
||
);
|
||
let expected = str![[r#"
|
||
error: oops
|
||
--> <current file>:2:8
|
||
|
|
||
2 | Second oops line
|
||
| ^^^^ oops
|
||
|
|
||
"#]];
|
||
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(snippets).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn test_point_to_double_width_characters() {
|
||
let snippets = Level::Error.title("").snippet(
|
||
Snippet::source("こんにちは、世界")
|
||
.origin("<current file>")
|
||
.annotation(Level::Error.span(18..24).label("world")),
|
||
);
|
||
|
||
let expected = str![[r#"
|
||
error
|
||
--> <current file>:1:7
|
||
|
|
||
1 | こんにちは、世界
|
||
| ^^^^ world
|
||
|
|
||
"#]];
|
||
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(snippets).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn test_point_to_double_width_characters_across_lines() {
|
||
let snippets = Level::Error.title("").snippet(
|
||
Snippet::source("おはよう\nございます")
|
||
.origin("<current file>")
|
||
.annotation(Level::Error.span(6..22).label("Good morning")),
|
||
);
|
||
|
||
let expected = str![[r#"
|
||
error
|
||
--> <current file>:1:3
|
||
|
|
||
1 | おはよう
|
||
| _____^
|
||
2 | | ございます
|
||
| |______^ Good morning
|
||
|
|
||
"#]];
|
||
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(snippets).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn test_point_to_double_width_characters_multiple() {
|
||
let snippets = Level::Error.title("").snippet(
|
||
Snippet::source("お寿司\n食べたい🍣")
|
||
.origin("<current file>")
|
||
.annotation(Level::Error.span(0..9).label("Sushi1"))
|
||
.annotation(Level::Note.span(16..22).label("Sushi2")),
|
||
);
|
||
|
||
let expected = str![[r#"
|
||
error
|
||
--> <current file>:1:1
|
||
|
|
||
1 | お寿司
|
||
| ^^^^^^ Sushi1
|
||
2 | 食べたい🍣
|
||
| ---- note: Sushi2
|
||
|
|
||
"#]];
|
||
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(snippets).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn test_point_to_double_width_characters_mixed() {
|
||
let snippets = Level::Error.title("").snippet(
|
||
Snippet::source("こんにちは、新しいWorld!")
|
||
.origin("<current file>")
|
||
.annotation(Level::Error.span(18..32).label("New world")),
|
||
);
|
||
|
||
let expected = str![[r#"
|
||
error
|
||
--> <current file>:1:7
|
||
|
|
||
1 | こんにちは、新しいWorld!
|
||
| ^^^^^^^^^^^ New world
|
||
|
|
||
"#]];
|
||
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(snippets).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn test_format_title() {
|
||
let input = Level::Error.title("This is a title").id("E0001");
|
||
|
||
let expected = str![r#"error[E0001]: This is a title"#];
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
/// Tests that we can format a message *without* a header.
|
||
///
|
||
/// This uses `Level::None`, which is somewhat of a hacky API addition I made
|
||
/// to our vendored copy of `annotate-snippets` in order to do exactly what
|
||
/// this test asserts: skip the header.
|
||
#[test]
|
||
fn test_format_skip_title() {
|
||
let source =
|
||
"# Docstring followed by a newline\n\ndef foobar(foot, bar={}):\n \"\"\"\n \"\"\"\n";
|
||
let src_annotation = Level::Error.span(56..58).label("B006");
|
||
let snippet = Snippet::source(source)
|
||
.line_start(1)
|
||
.annotation(src_annotation)
|
||
.fold(false);
|
||
let message = Level::None.title("").snippet(snippet);
|
||
|
||
let expected = str![[r#"
|
||
|
|
||
1 | # Docstring followed by a newline
|
||
2 |
|
||
3 | def foobar(foot, bar={}):
|
||
| ^^ B006
|
||
4 | """
|
||
5 | """
|
||
|
|
||
"#]];
|
||
assert_data_eq!(Renderer::plain().render(message).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn test_format_snippet_only() {
|
||
let source = "This is line 1\nThis is line 2";
|
||
let input = Level::Error
|
||
.title("")
|
||
.snippet(Snippet::source(source).line_start(5402));
|
||
|
||
let expected = str![[r#"
|
||
error
|
||
|
|
||
5402 | This is line 1
|
||
5403 | This is line 2
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn test_format_snippets_continuation() {
|
||
let src_0 = "This is slice 1";
|
||
let src_1 = "This is slice 2";
|
||
let input = Level::Error
|
||
.title("")
|
||
.snippet(Snippet::source(src_0).line_start(5402).origin("file1.rs"))
|
||
.snippet(Snippet::source(src_1).line_start(2).origin("file2.rs"));
|
||
let expected = str![[r#"
|
||
error
|
||
--> file1.rs
|
||
|
|
||
5402 | This is slice 1
|
||
|
|
||
::: file2.rs
|
||
|
|
||
2 | This is slice 2
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn test_format_snippet_annotation_standalone() {
|
||
let line_1 = "This is line 1";
|
||
let line_2 = "This is line 2";
|
||
let source = [line_1, line_2].join("\n");
|
||
// In line 2
|
||
let range = 22..24;
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(&source)
|
||
.line_start(5402)
|
||
.annotation(Level::Info.span(range.clone()).label("Test annotation")),
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
|
|
||
5402 | This is line 1
|
||
5403 | This is line 2
|
||
| -- info: Test annotation
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn test_format_footer_title() {
|
||
let input = Level::Error
|
||
.title("")
|
||
.footer(Level::Error.title("This __is__ a title"));
|
||
let expected = str![[r#"
|
||
error
|
||
= error: This __is__ a title
|
||
"#]];
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
#[should_panic]
|
||
fn test_i26() {
|
||
let source = "short";
|
||
let label = "label";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.line_start(0)
|
||
.annotation(Level::Error.span(0..source.len() + 2).label(label)),
|
||
);
|
||
let renderer = Renderer::plain();
|
||
let _ = renderer.render(input).to_string();
|
||
}
|
||
|
||
#[test]
|
||
fn test_source_content() {
|
||
let source = "This is an example\nof content lines";
|
||
let input = Level::Error
|
||
.title("")
|
||
.snippet(Snippet::source(source).line_start(56));
|
||
let expected = str![[r#"
|
||
error
|
||
|
|
||
56 | This is an example
|
||
57 | of content lines
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn test_source_annotation_standalone_singleline() {
|
||
let source = "tests";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.line_start(1)
|
||
.annotation(Level::Help.span(0..5).label("Example string")),
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
|
|
||
1 | tests
|
||
| ----- help: Example string
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn test_source_annotation_standalone_multiline() {
|
||
let source = "tests";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.line_start(1)
|
||
.annotation(Level::Help.span(0..5).label("Example string"))
|
||
.annotation(Level::Help.span(0..5).label("Second line")),
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
|
|
||
1 | tests
|
||
| -----
|
||
| |
|
||
| help: Example string
|
||
| help: Second line
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn test_only_source() {
|
||
let input = Level::Error
|
||
.title("")
|
||
.snippet(Snippet::source("").origin("file.rs"));
|
||
let expected = str![[r#"
|
||
error
|
||
--> file.rs
|
||
|
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn test_anon_lines() {
|
||
let source = "This is an example\nof content lines\n\nabc";
|
||
let input = Level::Error
|
||
.title("")
|
||
.snippet(Snippet::source(source).line_start(56));
|
||
let expected = str![[r#"
|
||
error
|
||
|
|
||
LL | This is an example
|
||
LL | of content lines
|
||
LL |
|
||
LL | abc
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain().anonymized_line_numbers(true);
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn issue_130() {
|
||
let input = Level::Error.title("dummy").snippet(
|
||
Snippet::source("foo\nbar\nbaz")
|
||
.origin("file/path")
|
||
.line_start(3)
|
||
.fold(true)
|
||
.annotation(Level::Error.span(4..11)), // bar\nbaz
|
||
);
|
||
|
||
let expected = str![[r#"
|
||
error: dummy
|
||
--> file/path:4:1
|
||
|
|
||
4 | / bar
|
||
5 | | baz
|
||
| |___^
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn unterminated_string_multiline() {
|
||
let source = "\
|
||
a\"
|
||
// ...
|
||
";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.origin("file/path")
|
||
.line_start(3)
|
||
.fold(true)
|
||
.annotation(Level::Error.span(0..10)), // 1..10 works
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
--> file/path:3:1
|
||
|
|
||
3 | / a"
|
||
4 | | // ...
|
||
| |_______^
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain().anonymized_line_numbers(false);
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn char_and_nl_annotate_char() {
|
||
let source = "a\r\nb";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.origin("file/path")
|
||
.line_start(3)
|
||
.annotation(Level::Error.span(0..2)), // a\r
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
--> file/path:3:1
|
||
|
|
||
3 | a
|
||
| ^
|
||
4 | b
|
||
|"#]];
|
||
let renderer = Renderer::plain().anonymized_line_numbers(false);
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn char_eol_annotate_char() {
|
||
let source = "a\r\nb";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.origin("file/path")
|
||
.line_start(3)
|
||
.annotation(Level::Error.span(0..3)), // a\r\n
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
--> file/path:3:1
|
||
|
|
||
3 | a
|
||
| ^
|
||
4 | b
|
||
|"#]];
|
||
let renderer = Renderer::plain().anonymized_line_numbers(false);
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn char_eol_annotate_char_double_width() {
|
||
let snippets = Level::Error.title("").snippet(
|
||
Snippet::source("こん\r\nにちは\r\n世界")
|
||
.origin("<current file>")
|
||
.annotation(Level::Error.span(3..8)), // ん\r\n
|
||
);
|
||
|
||
let expected = str![[r#"
|
||
error
|
||
--> <current file>:1:2
|
||
|
|
||
1 | こん
|
||
| ^^
|
||
2 | にちは
|
||
3 | 世界
|
||
|
|
||
"#]];
|
||
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(snippets).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn annotate_eol() {
|
||
let source = "a\r\nb";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.origin("file/path")
|
||
.line_start(3)
|
||
.annotation(Level::Error.span(1..2)), // \r
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
--> file/path:3:2
|
||
|
|
||
3 | a
|
||
| ^
|
||
4 | b
|
||
|"#]];
|
||
let renderer = Renderer::plain().anonymized_line_numbers(false);
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn annotate_eol2() {
|
||
let source = "a\r\nb";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.origin("file/path")
|
||
.line_start(3)
|
||
.annotation(Level::Error.span(1..3)), // \r\n
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
--> file/path:3:2
|
||
|
|
||
3 | a
|
||
| ^
|
||
4 | b
|
||
|"#]];
|
||
let renderer = Renderer::plain().anonymized_line_numbers(false);
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn annotate_eol3() {
|
||
let source = "a\r\nb";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.origin("file/path")
|
||
.line_start(3)
|
||
.annotation(Level::Error.span(2..3)), // \n
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
--> file/path:3:2
|
||
|
|
||
3 | a
|
||
| ^
|
||
4 | b
|
||
|"#]];
|
||
let renderer = Renderer::plain().anonymized_line_numbers(false);
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn annotate_eol4() {
|
||
let source = "a\r\nb";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.origin("file/path")
|
||
.line_start(3)
|
||
.annotation(Level::Error.span(2..2)), // \n
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
--> file/path:3:2
|
||
|
|
||
3 | a
|
||
| ^
|
||
4 | b
|
||
|"#]];
|
||
let renderer = Renderer::plain().anonymized_line_numbers(false);
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn annotate_eol_double_width() {
|
||
let snippets = Level::Error.title("").snippet(
|
||
Snippet::source("こん\r\nにちは\r\n世界")
|
||
.origin("<current file>")
|
||
.annotation(Level::Error.span(7..8)), // \n
|
||
);
|
||
|
||
let expected = str![[r#"
|
||
error
|
||
--> <current file>:1:3
|
||
|
|
||
1 | こん
|
||
| ^
|
||
2 | にちは
|
||
3 | 世界
|
||
|
|
||
"#]];
|
||
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(snippets).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn multiline_eol_start() {
|
||
let source = "a\r\nb";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.origin("file/path")
|
||
.line_start(3)
|
||
.annotation(Level::Error.span(1..4)), // \r\nb
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
--> file/path:3:2
|
||
|
|
||
3 | a
|
||
| __^
|
||
4 | | b
|
||
| |_^
|
||
|"#]];
|
||
let renderer = Renderer::plain().anonymized_line_numbers(false);
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn multiline_eol_start2() {
|
||
let source = "a\r\nb";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.origin("file/path")
|
||
.line_start(3)
|
||
.annotation(Level::Error.span(2..4)), // \nb
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
--> file/path:3:2
|
||
|
|
||
3 | a
|
||
| __^
|
||
4 | | b
|
||
| |_^
|
||
|"#]];
|
||
let renderer = Renderer::plain().anonymized_line_numbers(false);
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn multiline_eol_start3() {
|
||
let source = "a\nb";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.origin("file/path")
|
||
.line_start(3)
|
||
.annotation(Level::Error.span(1..3)), // \nb
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
--> file/path:3:2
|
||
|
|
||
3 | a
|
||
| __^
|
||
4 | | b
|
||
| |_^
|
||
|"#]];
|
||
let renderer = Renderer::plain().anonymized_line_numbers(false);
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn multiline_eol_start_double_width() {
|
||
let snippets = Level::Error.title("").snippet(
|
||
Snippet::source("こん\r\nにちは\r\n世界")
|
||
.origin("<current file>")
|
||
.annotation(Level::Error.span(7..11)), // \r\nに
|
||
);
|
||
|
||
let expected = str![[r#"
|
||
error
|
||
--> <current file>:1:3
|
||
|
|
||
1 | こん
|
||
| _____^
|
||
2 | | にちは
|
||
| |__^
|
||
3 | 世界
|
||
|
|
||
"#]];
|
||
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(snippets).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn multiline_eol_start_eol_end() {
|
||
let source = "a\nb\nc";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.origin("file/path")
|
||
.line_start(3)
|
||
.annotation(Level::Error.span(1..4)), // \nb\n
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
--> file/path:3:2
|
||
|
|
||
3 | a
|
||
| __^
|
||
4 | | b
|
||
| |__^
|
||
5 | c
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain().anonymized_line_numbers(false);
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn multiline_eol_start_eol_end2() {
|
||
let source = "a\r\nb\r\nc";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.origin("file/path")
|
||
.line_start(3)
|
||
.annotation(Level::Error.span(2..5)), // \nb\r
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
--> file/path:3:2
|
||
|
|
||
3 | a
|
||
| __^
|
||
4 | | b
|
||
| |__^
|
||
5 | c
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain().anonymized_line_numbers(false);
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn multiline_eol_start_eol_end3() {
|
||
let source = "a\r\nb\r\nc";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.origin("file/path")
|
||
.line_start(3)
|
||
.annotation(Level::Error.span(2..6)), // \nb\r\n
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
--> file/path:3:2
|
||
|
|
||
3 | a
|
||
| __^
|
||
4 | | b
|
||
| |__^
|
||
5 | c
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain().anonymized_line_numbers(false);
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn multiline_eol_start_eof_end() {
|
||
let source = "a\r\nb";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.origin("file/path")
|
||
.line_start(3)
|
||
.annotation(Level::Error.span(1..5)), // \r\nb(EOF)
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
--> file/path:3:2
|
||
|
|
||
3 | a
|
||
| __^
|
||
4 | | b
|
||
| |__^
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain().anonymized_line_numbers(false);
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn multiline_eol_start_eof_end_double_width() {
|
||
let source = "ん\r\nに";
|
||
let input = Level::Error.title("").snippet(
|
||
Snippet::source(source)
|
||
.origin("file/path")
|
||
.line_start(3)
|
||
.annotation(Level::Error.span(3..9)), // \r\nに(EOF)
|
||
);
|
||
let expected = str![[r#"
|
||
error
|
||
--> file/path:3:2
|
||
|
|
||
3 | ん
|
||
| ___^
|
||
4 | | に
|
||
| |___^
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain().anonymized_line_numbers(false);
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn two_single_line_same_line() {
|
||
let source = r#"bar = { version = "0.1.0", optional = true }"#;
|
||
let input = Level::Error.title("unused optional dependency").snippet(
|
||
Snippet::source(source)
|
||
.origin("Cargo.toml")
|
||
.line_start(4)
|
||
.annotation(
|
||
Level::Error
|
||
.span(0..3)
|
||
.label("I need this to be really long so I can test overlaps"),
|
||
)
|
||
.annotation(
|
||
Level::Info
|
||
.span(27..42)
|
||
.label("This should also be long but not too long"),
|
||
),
|
||
);
|
||
let expected = str![[r#"
|
||
error: unused optional dependency
|
||
--> Cargo.toml:4:1
|
||
|
|
||
4 | bar = { version = "0.1.0", optional = true }
|
||
| ^^^ --------------- info: This should also be long but not too long
|
||
| |
|
||
| I need this to be really long so I can test overlaps
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain().anonymized_line_numbers(false);
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn multi_and_single() {
|
||
let source = r#"bar = { version = "0.1.0", optional = true }
|
||
this is another line
|
||
so is this
|
||
bar = { version = "0.1.0", optional = true }
|
||
"#;
|
||
let input = Level::Error.title("unused optional dependency").snippet(
|
||
Snippet::source(source)
|
||
.line_start(4)
|
||
.annotation(
|
||
Level::Error
|
||
.span(41..119)
|
||
.label("I need this to be really long so I can test overlaps"),
|
||
)
|
||
.annotation(
|
||
Level::Info
|
||
.span(27..42)
|
||
.label("This should also be long but not too long"),
|
||
),
|
||
);
|
||
let expected = str![[r#"
|
||
error: unused optional dependency
|
||
|
|
||
4 | bar = { version = "0.1.0", optional = true }
|
||
| ____________________________--------------^
|
||
| | |
|
||
| | info: This should also be long but not too long
|
||
5 | | this is another line
|
||
6 | | so is this
|
||
7 | | bar = { version = "0.1.0", optional = true }
|
||
| |__________________________________________^ I need this to be really long so I can test overlaps
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn two_multi_and_single() {
|
||
let source = r#"bar = { version = "0.1.0", optional = true }
|
||
this is another line
|
||
so is this
|
||
bar = { version = "0.1.0", optional = true }
|
||
"#;
|
||
let input = Level::Error.title("unused optional dependency").snippet(
|
||
Snippet::source(source)
|
||
.line_start(4)
|
||
.annotation(
|
||
Level::Error
|
||
.span(41..119)
|
||
.label("I need this to be really long so I can test overlaps"),
|
||
)
|
||
.annotation(
|
||
Level::Error
|
||
.span(8..102)
|
||
.label("I need this to be really long so I can test overlaps"),
|
||
)
|
||
.annotation(
|
||
Level::Info
|
||
.span(27..42)
|
||
.label("This should also be long but not too long"),
|
||
),
|
||
);
|
||
let expected = str![[r#"
|
||
error: unused optional dependency
|
||
|
|
||
4 | bar = { version = "0.1.0", optional = true }
|
||
| _________^__________________--------------^
|
||
| | | |
|
||
| |_________| info: This should also be long but not too long
|
||
| ||
|
||
5 | || this is another line
|
||
6 | || so is this
|
||
7 | || bar = { version = "0.1.0", optional = true }
|
||
| ||_________________________^________________^ I need this to be really long so I can test overlaps
|
||
| |__________________________|
|
||
| I need this to be really long so I can test overlaps
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn three_multi_and_single() {
|
||
let source = r#"bar = { version = "0.1.0", optional = true }
|
||
this is another line
|
||
so is this
|
||
bar = { version = "0.1.0", optional = true }
|
||
this is another line
|
||
"#;
|
||
let input = Level::Error.title("unused optional dependency").snippet(
|
||
Snippet::source(source)
|
||
.line_start(4)
|
||
.annotation(
|
||
Level::Error
|
||
.span(41..119)
|
||
.label("I need this to be really long so I can test overlaps"),
|
||
)
|
||
.annotation(
|
||
Level::Error
|
||
.span(8..102)
|
||
.label("I need this to be really long so I can test overlaps"),
|
||
)
|
||
.annotation(
|
||
Level::Error
|
||
.span(48..126)
|
||
.label("I need this to be really long so I can test overlaps"),
|
||
)
|
||
.annotation(
|
||
Level::Info
|
||
.span(27..42)
|
||
.label("This should also be long but not too long"),
|
||
),
|
||
);
|
||
let expected = str![[r#"
|
||
error: unused optional dependency
|
||
|
|
||
4 | bar = { version = "0.1.0", optional = true }
|
||
| __________^__________________--------------^
|
||
| | | |
|
||
| |__________| info: This should also be long but not too long
|
||
| ||
|
||
5 | || this is another line
|
||
| || ____^
|
||
6 | ||| so is this
|
||
7 | ||| bar = { version = "0.1.0", optional = true }
|
||
| |||_________________________^________________^ I need this to be really long so I can test overlaps
|
||
| |_|_________________________|
|
||
| | I need this to be really long so I can test overlaps
|
||
8 | | this is another line
|
||
| |____^ I need this to be really long so I can test overlaps
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn origin_correct_start_line() {
|
||
let source = "aaa\nbbb\nccc\nddd\n";
|
||
let input = Level::Error.title("title").snippet(
|
||
Snippet::source(source)
|
||
.origin("origin.txt")
|
||
.fold(false)
|
||
.annotation(Level::Error.span(8..8 + 3).label("annotation")),
|
||
);
|
||
|
||
let expected = str![[r#"
|
||
error: title
|
||
--> origin.txt:3:1
|
||
|
|
||
1 | aaa
|
||
2 | bbb
|
||
3 | ccc
|
||
| ^^^ annotation
|
||
4 | ddd
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|
||
|
||
#[test]
|
||
fn origin_correct_mid_line() {
|
||
let source = "aaa\nbbb\nccc\nddd\n";
|
||
let input = Level::Error.title("title").snippet(
|
||
Snippet::source(source)
|
||
.origin("origin.txt")
|
||
.fold(false)
|
||
.annotation(Level::Error.span(8 + 1..8 + 3).label("annotation")),
|
||
);
|
||
|
||
let expected = str![[r#"
|
||
error: title
|
||
--> origin.txt:3:2
|
||
|
|
||
1 | aaa
|
||
2 | bbb
|
||
3 | ccc
|
||
| ^^ annotation
|
||
4 | ddd
|
||
|
|
||
"#]];
|
||
let renderer = Renderer::plain();
|
||
assert_data_eq!(renderer.render(input).to_string(), expected);
|
||
}
|