summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_expand/src/tests.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_expand/src/tests.rs')
-rw-r--r--compiler/rustc_expand/src/tests.rs1016
1 files changed, 1016 insertions, 0 deletions
diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs
new file mode 100644
index 000000000..e44f06081
--- /dev/null
+++ b/compiler/rustc_expand/src/tests.rs
@@ -0,0 +1,1016 @@
+use rustc_ast as ast;
+use rustc_ast::tokenstream::TokenStream;
+use rustc_parse::{new_parser_from_source_str, parser::Parser, source_file_to_stream};
+use rustc_session::parse::ParseSess;
+use rustc_span::create_default_session_if_not_set_then;
+use rustc_span::source_map::{FilePathMapping, SourceMap};
+use rustc_span::{BytePos, Span};
+
+use rustc_data_structures::sync::Lrc;
+use rustc_errors::emitter::EmitterWriter;
+use rustc_errors::{Handler, MultiSpan, PResult};
+
+use std::io;
+use std::io::prelude::*;
+use std::iter::Peekable;
+use std::path::{Path, PathBuf};
+use std::str;
+use std::sync::{Arc, Mutex};
+
+/// Map string to parser (via tts).
+fn string_to_parser(ps: &ParseSess, source_str: String) -> Parser<'_> {
+ new_parser_from_source_str(ps, PathBuf::from("bogofile").into(), source_str)
+}
+
+pub(crate) fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> T
+where
+ F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>,
+{
+ let mut p = string_to_parser(&ps, s);
+ let x = f(&mut p).unwrap();
+ p.sess.span_diagnostic.abort_if_errors();
+ x
+}
+
+/// Maps a string to tts, using a made-up filename.
+pub(crate) fn string_to_stream(source_str: String) -> TokenStream {
+ let ps = ParseSess::new(FilePathMapping::empty());
+ source_file_to_stream(
+ &ps,
+ ps.source_map().new_source_file(PathBuf::from("bogofile").into(), source_str),
+ None,
+ )
+ .0
+}
+
+/// Parses a string, returns a crate.
+pub(crate) fn string_to_crate(source_str: String) -> ast::Crate {
+ let ps = ParseSess::new(FilePathMapping::empty());
+ with_error_checking_parse(source_str, &ps, |p| p.parse_crate_mod())
+}
+
+/// Does the given string match the pattern? whitespace in the first string
+/// may be deleted or replaced with other whitespace to match the pattern.
+/// This function is relatively Unicode-ignorant; fortunately, the careful design
+/// of UTF-8 mitigates this ignorance. It doesn't do NKF-normalization(?).
+pub(crate) fn matches_codepattern(a: &str, b: &str) -> bool {
+ let mut a_iter = a.chars().peekable();
+ let mut b_iter = b.chars().peekable();
+
+ loop {
+ let (a, b) = match (a_iter.peek(), b_iter.peek()) {
+ (None, None) => return true,
+ (None, _) => return false,
+ (Some(&a), None) => {
+ if rustc_lexer::is_whitespace(a) {
+ break; // Trailing whitespace check is out of loop for borrowck.
+ } else {
+ return false;
+ }
+ }
+ (Some(&a), Some(&b)) => (a, b),
+ };
+
+ if rustc_lexer::is_whitespace(a) && rustc_lexer::is_whitespace(b) {
+ // Skip whitespace for `a` and `b`.
+ scan_for_non_ws_or_end(&mut a_iter);
+ scan_for_non_ws_or_end(&mut b_iter);
+ } else if rustc_lexer::is_whitespace(a) {
+ // Skip whitespace for `a`.
+ scan_for_non_ws_or_end(&mut a_iter);
+ } else if a == b {
+ a_iter.next();
+ b_iter.next();
+ } else {
+ return false;
+ }
+ }
+
+ // Check if a has *only* trailing whitespace.
+ a_iter.all(rustc_lexer::is_whitespace)
+}
+
+/// Advances the given peekable `Iterator` until it reaches a non-whitespace character.
+fn scan_for_non_ws_or_end<I: Iterator<Item = char>>(iter: &mut Peekable<I>) {
+ while iter.peek().copied().map(rustc_lexer::is_whitespace) == Some(true) {
+ iter.next();
+ }
+}
+
+/// Identifies a position in the text by the n'th occurrence of a string.
+struct Position {
+ string: &'static str,
+ count: usize,
+}
+
+struct SpanLabel {
+ start: Position,
+ end: Position,
+ label: &'static str,
+}
+
+pub(crate) struct Shared<T: Write> {
+ pub data: Arc<Mutex<T>>,
+}
+
+impl<T: Write> Write for Shared<T> {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ self.data.lock().unwrap().write(buf)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.data.lock().unwrap().flush()
+ }
+}
+
+fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &str) {
+ create_default_session_if_not_set_then(|_| {
+ let output = Arc::new(Mutex::new(Vec::new()));
+
+ let fallback_bundle =
+ rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+ let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
+ source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned());
+
+ let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end);
+ let mut msp = MultiSpan::from_span(primary_span);
+ for span_label in span_labels {
+ let span = make_span(&file_text, &span_label.start, &span_label.end);
+ msp.push_span_label(span, span_label.label);
+ println!("span: {:?} label: {:?}", span, span_label.label);
+ println!("text: {:?}", source_map.span_to_snippet(span));
+ }
+
+ let emitter = EmitterWriter::new(
+ Box::new(Shared { data: output.clone() }),
+ Some(source_map.clone()),
+ None,
+ fallback_bundle,
+ false,
+ false,
+ false,
+ None,
+ false,
+ );
+ let handler = Handler::with_emitter(true, None, Box::new(emitter));
+ handler.span_err(msp, "foo");
+
+ assert!(
+ expected_output.chars().next() == Some('\n'),
+ "expected output should begin with newline"
+ );
+ let expected_output = &expected_output[1..];
+
+ let bytes = output.lock().unwrap();
+ let actual_output = str::from_utf8(&bytes).unwrap();
+ println!("expected output:\n------\n{}------", expected_output);
+ println!("actual output:\n------\n{}------", actual_output);
+
+ assert!(expected_output == actual_output)
+ })
+}
+
+fn make_span(file_text: &str, start: &Position, end: &Position) -> Span {
+ let start = make_pos(file_text, start);
+ let end = make_pos(file_text, end) + end.string.len(); // just after matching thing ends
+ assert!(start <= end);
+ Span::with_root_ctxt(BytePos(start as u32), BytePos(end as u32))
+}
+
+fn make_pos(file_text: &str, pos: &Position) -> usize {
+ let mut remainder = file_text;
+ let mut offset = 0;
+ for _ in 0..pos.count {
+ if let Some(n) = remainder.find(&pos.string) {
+ offset += n;
+ remainder = &remainder[n + 1..];
+ } else {
+ panic!("failed to find {} instances of {:?} in {:?}", pos.count, pos.string, file_text);
+ }
+ }
+ offset
+}
+
+#[test]
+fn ends_on_col0() {
+ test_harness(
+ r#"
+fn foo() {
+}
+"#,
+ vec![SpanLabel {
+ start: Position { string: "{", count: 1 },
+ end: Position { string: "}", count: 1 },
+ label: "test",
+ }],
+ r#"
+error: foo
+ --> test.rs:2:10
+ |
+2 | fn foo() {
+ | __________^
+3 | | }
+ | |_^ test
+
+"#,
+ );
+}
+
+#[test]
+fn ends_on_col2() {
+ test_harness(
+ r#"
+fn foo() {
+
+
+ }
+"#,
+ vec![SpanLabel {
+ start: Position { string: "{", count: 1 },
+ end: Position { string: "}", count: 1 },
+ label: "test",
+ }],
+ r#"
+error: foo
+ --> test.rs:2:10
+ |
+2 | fn foo() {
+ | __________^
+3 | |
+4 | |
+5 | | }
+ | |___^ test
+
+"#,
+ );
+}
+#[test]
+fn non_nested() {
+ test_harness(
+ r#"
+fn foo() {
+ X0 Y0
+ X1 Y1
+ X2 Y2
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "X0", count: 1 },
+ end: Position { string: "X2", count: 1 },
+ label: "`X` is a good letter",
+ },
+ SpanLabel {
+ start: Position { string: "Y0", count: 1 },
+ end: Position { string: "Y2", count: 1 },
+ label: "`Y` is a good letter too",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:3
+ |
+3 | X0 Y0
+ | ____^__-
+ | | ___|
+ | ||
+4 | || X1 Y1
+5 | || X2 Y2
+ | ||____^__- `Y` is a good letter too
+ | |____|
+ | `X` is a good letter
+
+"#,
+ );
+}
+
+#[test]
+fn nested() {
+ test_harness(
+ r#"
+fn foo() {
+ X0 Y0
+ Y1 X1
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "X0", count: 1 },
+ end: Position { string: "X1", count: 1 },
+ label: "`X` is a good letter",
+ },
+ SpanLabel {
+ start: Position { string: "Y0", count: 1 },
+ end: Position { string: "Y1", count: 1 },
+ label: "`Y` is a good letter too",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:3
+ |
+3 | X0 Y0
+ | ____^__-
+ | | ___|
+ | ||
+4 | || Y1 X1
+ | ||____-__^ `X` is a good letter
+ | |_____|
+ | `Y` is a good letter too
+
+"#,
+ );
+}
+
+#[test]
+fn different_overlap() {
+ test_harness(
+ r#"
+fn foo() {
+ X0 Y0 Z0
+ X1 Y1 Z1
+ X2 Y2 Z2
+ X3 Y3 Z3
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "Y0", count: 1 },
+ end: Position { string: "X2", count: 1 },
+ label: "`X` is a good letter",
+ },
+ SpanLabel {
+ start: Position { string: "Z1", count: 1 },
+ end: Position { string: "X3", count: 1 },
+ label: "`Y` is a good letter too",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:6
+ |
+3 | X0 Y0 Z0
+ | ______^
+4 | | X1 Y1 Z1
+ | |_________-
+5 | || X2 Y2 Z2
+ | ||____^ `X` is a good letter
+6 | | X3 Y3 Z3
+ | |_____- `Y` is a good letter too
+
+"#,
+ );
+}
+
+#[test]
+fn triple_overlap() {
+ test_harness(
+ r#"
+fn foo() {
+ X0 Y0 Z0
+ X1 Y1 Z1
+ X2 Y2 Z2
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "X0", count: 1 },
+ end: Position { string: "X2", count: 1 },
+ label: "`X` is a good letter",
+ },
+ SpanLabel {
+ start: Position { string: "Y0", count: 1 },
+ end: Position { string: "Y2", count: 1 },
+ label: "`Y` is a good letter too",
+ },
+ SpanLabel {
+ start: Position { string: "Z0", count: 1 },
+ end: Position { string: "Z2", count: 1 },
+ label: "`Z` label",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:3
+ |
+3 | X0 Y0 Z0
+ | _____^__-__-
+ | | ____|__|
+ | || ___|
+ | |||
+4 | ||| X1 Y1 Z1
+5 | ||| X2 Y2 Z2
+ | |||____^__-__- `Z` label
+ | ||____|__|
+ | |____| `Y` is a good letter too
+ | `X` is a good letter
+
+"#,
+ );
+}
+
+#[test]
+fn triple_exact_overlap() {
+ test_harness(
+ r#"
+fn foo() {
+ X0 Y0 Z0
+ X1 Y1 Z1
+ X2 Y2 Z2
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "X0", count: 1 },
+ end: Position { string: "X2", count: 1 },
+ label: "`X` is a good letter",
+ },
+ SpanLabel {
+ start: Position { string: "X0", count: 1 },
+ end: Position { string: "X2", count: 1 },
+ label: "`Y` is a good letter too",
+ },
+ SpanLabel {
+ start: Position { string: "X0", count: 1 },
+ end: Position { string: "X2", count: 1 },
+ label: "`Z` label",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:3
+ |
+3 | / X0 Y0 Z0
+4 | | X1 Y1 Z1
+5 | | X2 Y2 Z2
+ | | ^
+ | | |
+ | | `X` is a good letter
+ | |____`Y` is a good letter too
+ | `Z` label
+
+"#,
+ );
+}
+
+#[test]
+fn minimum_depth() {
+ test_harness(
+ r#"
+fn foo() {
+ X0 Y0 Z0
+ X1 Y1 Z1
+ X2 Y2 Z2
+ X3 Y3 Z3
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "Y0", count: 1 },
+ end: Position { string: "X1", count: 1 },
+ label: "`X` is a good letter",
+ },
+ SpanLabel {
+ start: Position { string: "Y1", count: 1 },
+ end: Position { string: "Z2", count: 1 },
+ label: "`Y` is a good letter too",
+ },
+ SpanLabel {
+ start: Position { string: "X2", count: 1 },
+ end: Position { string: "Y3", count: 1 },
+ label: "`Z`",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:6
+ |
+3 | X0 Y0 Z0
+ | ______^
+4 | | X1 Y1 Z1
+ | |____^_-
+ | ||____|
+ | | `X` is a good letter
+5 | | X2 Y2 Z2
+ | |____-______- `Y` is a good letter too
+ | ____|
+ | |
+6 | | X3 Y3 Z3
+ | |________- `Z`
+
+"#,
+ );
+}
+
+#[test]
+fn non_overlaping() {
+ test_harness(
+ r#"
+fn foo() {
+ X0 Y0 Z0
+ X1 Y1 Z1
+ X2 Y2 Z2
+ X3 Y3 Z3
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "X0", count: 1 },
+ end: Position { string: "X1", count: 1 },
+ label: "`X` is a good letter",
+ },
+ SpanLabel {
+ start: Position { string: "Y2", count: 1 },
+ end: Position { string: "Z3", count: 1 },
+ label: "`Y` is a good letter too",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:3
+ |
+3 | / X0 Y0 Z0
+4 | | X1 Y1 Z1
+ | |____^ `X` is a good letter
+5 | X2 Y2 Z2
+ | ______-
+6 | | X3 Y3 Z3
+ | |__________- `Y` is a good letter too
+
+"#,
+ );
+}
+
+#[test]
+fn overlaping_start_and_end() {
+ test_harness(
+ r#"
+fn foo() {
+ X0 Y0 Z0
+ X1 Y1 Z1
+ X2 Y2 Z2
+ X3 Y3 Z3
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "Y0", count: 1 },
+ end: Position { string: "X1", count: 1 },
+ label: "`X` is a good letter",
+ },
+ SpanLabel {
+ start: Position { string: "Z1", count: 1 },
+ end: Position { string: "Z3", count: 1 },
+ label: "`Y` is a good letter too",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:6
+ |
+3 | X0 Y0 Z0
+ | ______^
+4 | | X1 Y1 Z1
+ | |____^____-
+ | ||____|
+ | | `X` is a good letter
+5 | | X2 Y2 Z2
+6 | | X3 Y3 Z3
+ | |___________- `Y` is a good letter too
+
+"#,
+ );
+}
+
+#[test]
+fn multiple_labels_primary_without_message() {
+ test_harness(
+ r#"
+fn foo() {
+ a { b { c } d }
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "b", count: 1 },
+ end: Position { string: "}", count: 1 },
+ label: "",
+ },
+ SpanLabel {
+ start: Position { string: "a", count: 1 },
+ end: Position { string: "d", count: 1 },
+ label: "`a` is a good letter",
+ },
+ SpanLabel {
+ start: Position { string: "c", count: 1 },
+ end: Position { string: "c", count: 1 },
+ label: "",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:7
+ |
+3 | a { b { c } d }
+ | ----^^^^-^^-- `a` is a good letter
+
+"#,
+ );
+}
+
+#[test]
+fn multiple_labels_secondary_without_message() {
+ test_harness(
+ r#"
+fn foo() {
+ a { b { c } d }
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "a", count: 1 },
+ end: Position { string: "d", count: 1 },
+ label: "`a` is a good letter",
+ },
+ SpanLabel {
+ start: Position { string: "b", count: 1 },
+ end: Position { string: "}", count: 1 },
+ label: "",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:3
+ |
+3 | a { b { c } d }
+ | ^^^^-------^^ `a` is a good letter
+
+"#,
+ );
+}
+
+#[test]
+fn multiple_labels_primary_without_message_2() {
+ test_harness(
+ r#"
+fn foo() {
+ a { b { c } d }
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "b", count: 1 },
+ end: Position { string: "}", count: 1 },
+ label: "`b` is a good letter",
+ },
+ SpanLabel {
+ start: Position { string: "a", count: 1 },
+ end: Position { string: "d", count: 1 },
+ label: "",
+ },
+ SpanLabel {
+ start: Position { string: "c", count: 1 },
+ end: Position { string: "c", count: 1 },
+ label: "",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:7
+ |
+3 | a { b { c } d }
+ | ----^^^^-^^--
+ | |
+ | `b` is a good letter
+
+"#,
+ );
+}
+
+#[test]
+fn multiple_labels_secondary_without_message_2() {
+ test_harness(
+ r#"
+fn foo() {
+ a { b { c } d }
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "a", count: 1 },
+ end: Position { string: "d", count: 1 },
+ label: "",
+ },
+ SpanLabel {
+ start: Position { string: "b", count: 1 },
+ end: Position { string: "}", count: 1 },
+ label: "`b` is a good letter",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:3
+ |
+3 | a { b { c } d }
+ | ^^^^-------^^
+ | |
+ | `b` is a good letter
+
+"#,
+ );
+}
+
+#[test]
+fn multiple_labels_secondary_without_message_3() {
+ test_harness(
+ r#"
+fn foo() {
+ a bc d
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "a", count: 1 },
+ end: Position { string: "b", count: 1 },
+ label: "`a` is a good letter",
+ },
+ SpanLabel {
+ start: Position { string: "c", count: 1 },
+ end: Position { string: "d", count: 1 },
+ label: "",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:3
+ |
+3 | a bc d
+ | ^^^^----
+ | |
+ | `a` is a good letter
+
+"#,
+ );
+}
+
+#[test]
+fn multiple_labels_without_message() {
+ test_harness(
+ r#"
+fn foo() {
+ a { b { c } d }
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "a", count: 1 },
+ end: Position { string: "d", count: 1 },
+ label: "",
+ },
+ SpanLabel {
+ start: Position { string: "b", count: 1 },
+ end: Position { string: "}", count: 1 },
+ label: "",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:3
+ |
+3 | a { b { c } d }
+ | ^^^^-------^^
+
+"#,
+ );
+}
+
+#[test]
+fn multiple_labels_without_message_2() {
+ test_harness(
+ r#"
+fn foo() {
+ a { b { c } d }
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "b", count: 1 },
+ end: Position { string: "}", count: 1 },
+ label: "",
+ },
+ SpanLabel {
+ start: Position { string: "a", count: 1 },
+ end: Position { string: "d", count: 1 },
+ label: "",
+ },
+ SpanLabel {
+ start: Position { string: "c", count: 1 },
+ end: Position { string: "c", count: 1 },
+ label: "",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:7
+ |
+3 | a { b { c } d }
+ | ----^^^^-^^--
+
+"#,
+ );
+}
+
+#[test]
+fn multiple_labels_with_message() {
+ test_harness(
+ r#"
+fn foo() {
+ a { b { c } d }
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "a", count: 1 },
+ end: Position { string: "d", count: 1 },
+ label: "`a` is a good letter",
+ },
+ SpanLabel {
+ start: Position { string: "b", count: 1 },
+ end: Position { string: "}", count: 1 },
+ label: "`b` is a good letter",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:3
+ |
+3 | a { b { c } d }
+ | ^^^^-------^^
+ | | |
+ | | `b` is a good letter
+ | `a` is a good letter
+
+"#,
+ );
+}
+
+#[test]
+fn single_label_with_message() {
+ test_harness(
+ r#"
+fn foo() {
+ a { b { c } d }
+}
+"#,
+ vec![SpanLabel {
+ start: Position { string: "a", count: 1 },
+ end: Position { string: "d", count: 1 },
+ label: "`a` is a good letter",
+ }],
+ r#"
+error: foo
+ --> test.rs:3:3
+ |
+3 | a { b { c } d }
+ | ^^^^^^^^^^^^^ `a` is a good letter
+
+"#,
+ );
+}
+
+#[test]
+fn single_label_without_message() {
+ test_harness(
+ r#"
+fn foo() {
+ a { b { c } d }
+}
+"#,
+ vec![SpanLabel {
+ start: Position { string: "a", count: 1 },
+ end: Position { string: "d", count: 1 },
+ label: "",
+ }],
+ r#"
+error: foo
+ --> test.rs:3:3
+ |
+3 | a { b { c } d }
+ | ^^^^^^^^^^^^^
+
+"#,
+ );
+}
+
+#[test]
+fn long_snippet() {
+ test_harness(
+ r#"
+fn foo() {
+ X0 Y0 Z0
+ X1 Y1 Z1
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+ X2 Y2 Z2
+ X3 Y3 Z3
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "Y0", count: 1 },
+ end: Position { string: "X1", count: 1 },
+ label: "`X` is a good letter",
+ },
+ SpanLabel {
+ start: Position { string: "Z1", count: 1 },
+ end: Position { string: "Z3", count: 1 },
+ label: "`Y` is a good letter too",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:6
+ |
+3 | X0 Y0 Z0
+ | ______^
+4 | | X1 Y1 Z1
+ | |____^____-
+ | ||____|
+ | | `X` is a good letter
+5 | | 1
+6 | | 2
+7 | | 3
+... |
+15 | | X2 Y2 Z2
+16 | | X3 Y3 Z3
+ | |___________- `Y` is a good letter too
+
+"#,
+ );
+}
+
+#[test]
+fn long_snippet_multiple_spans() {
+ test_harness(
+ r#"
+fn foo() {
+ X0 Y0 Z0
+1
+2
+3
+ X1 Y1 Z1
+4
+5
+6
+ X2 Y2 Z2
+7
+8
+9
+10
+ X3 Y3 Z3
+}
+"#,
+ vec![
+ SpanLabel {
+ start: Position { string: "Y0", count: 1 },
+ end: Position { string: "Y3", count: 1 },
+ label: "`Y` is a good letter",
+ },
+ SpanLabel {
+ start: Position { string: "Z1", count: 1 },
+ end: Position { string: "Z2", count: 1 },
+ label: "`Z` is a good letter too",
+ },
+ ],
+ r#"
+error: foo
+ --> test.rs:3:6
+ |
+3 | X0 Y0 Z0
+ | ______^
+4 | | 1
+5 | | 2
+6 | | 3
+7 | | X1 Y1 Z1
+ | |_________-
+8 | || 4
+9 | || 5
+10 | || 6
+11 | || X2 Y2 Z2
+ | ||__________- `Z` is a good letter too
+... |
+15 | | 10
+16 | | X3 Y3 Z3
+ | |_______^ `Y` is a good letter
+
+"#,
+ );
+}