]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_expand/src/tests.rs
Merge commit '266e96785ab71834b917bf474f130a6d8fdecd4b' into sync_cg_clif-2022-10-23
[rust.git] / compiler / rustc_expand / src / tests.rs
1 use rustc_ast as ast;
2 use rustc_ast::tokenstream::TokenStream;
3 use rustc_parse::{new_parser_from_source_str, parser::Parser, source_file_to_stream};
4 use rustc_session::parse::ParseSess;
5 use rustc_span::create_default_session_if_not_set_then;
6 use rustc_span::source_map::{FilePathMapping, SourceMap};
7 use rustc_span::{BytePos, Span};
8
9 use rustc_data_structures::sync::Lrc;
10 use rustc_errors::emitter::EmitterWriter;
11 use rustc_errors::{Handler, MultiSpan, PResult};
12
13 use std::io;
14 use std::io::prelude::*;
15 use std::iter::Peekable;
16 use std::path::{Path, PathBuf};
17 use std::str;
18 use std::sync::{Arc, Mutex};
19
20 /// Map string to parser (via tts).
21 fn string_to_parser(ps: &ParseSess, source_str: String) -> Parser<'_> {
22     new_parser_from_source_str(ps, PathBuf::from("bogofile").into(), source_str)
23 }
24
25 pub(crate) fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> T
26 where
27     F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>,
28 {
29     let mut p = string_to_parser(&ps, s);
30     let x = f(&mut p).unwrap();
31     p.sess.span_diagnostic.abort_if_errors();
32     x
33 }
34
35 /// Maps a string to tts, using a made-up filename.
36 pub(crate) fn string_to_stream(source_str: String) -> TokenStream {
37     let ps = ParseSess::new(FilePathMapping::empty());
38     source_file_to_stream(
39         &ps,
40         ps.source_map().new_source_file(PathBuf::from("bogofile").into(), source_str),
41         None,
42     )
43     .0
44 }
45
46 /// Parses a string, returns a crate.
47 pub(crate) fn string_to_crate(source_str: String) -> ast::Crate {
48     let ps = ParseSess::new(FilePathMapping::empty());
49     with_error_checking_parse(source_str, &ps, |p| p.parse_crate_mod())
50 }
51
52 /// Does the given string match the pattern? whitespace in the first string
53 /// may be deleted or replaced with other whitespace to match the pattern.
54 /// This function is relatively Unicode-ignorant; fortunately, the careful design
55 /// of UTF-8 mitigates this ignorance. It doesn't do NKF-normalization(?).
56 pub(crate) fn matches_codepattern(a: &str, b: &str) -> bool {
57     let mut a_iter = a.chars().peekable();
58     let mut b_iter = b.chars().peekable();
59
60     loop {
61         let (a, b) = match (a_iter.peek(), b_iter.peek()) {
62             (None, None) => return true,
63             (None, _) => return false,
64             (Some(&a), None) => {
65                 if rustc_lexer::is_whitespace(a) {
66                     break; // Trailing whitespace check is out of loop for borrowck.
67                 } else {
68                     return false;
69                 }
70             }
71             (Some(&a), Some(&b)) => (a, b),
72         };
73
74         if rustc_lexer::is_whitespace(a) && rustc_lexer::is_whitespace(b) {
75             // Skip whitespace for `a` and `b`.
76             scan_for_non_ws_or_end(&mut a_iter);
77             scan_for_non_ws_or_end(&mut b_iter);
78         } else if rustc_lexer::is_whitespace(a) {
79             // Skip whitespace for `a`.
80             scan_for_non_ws_or_end(&mut a_iter);
81         } else if a == b {
82             a_iter.next();
83             b_iter.next();
84         } else {
85             return false;
86         }
87     }
88
89     // Check if a has *only* trailing whitespace.
90     a_iter.all(rustc_lexer::is_whitespace)
91 }
92
93 /// Advances the given peekable `Iterator` until it reaches a non-whitespace character.
94 fn scan_for_non_ws_or_end<I: Iterator<Item = char>>(iter: &mut Peekable<I>) {
95     while iter.peek().copied().map(rustc_lexer::is_whitespace) == Some(true) {
96         iter.next();
97     }
98 }
99
100 /// Identifies a position in the text by the n'th occurrence of a string.
101 struct Position {
102     string: &'static str,
103     count: usize,
104 }
105
106 struct SpanLabel {
107     start: Position,
108     end: Position,
109     label: &'static str,
110 }
111
112 pub(crate) struct Shared<T: Write> {
113     pub data: Arc<Mutex<T>>,
114 }
115
116 impl<T: Write> Write for Shared<T> {
117     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
118         self.data.lock().unwrap().write(buf)
119     }
120
121     fn flush(&mut self) -> io::Result<()> {
122         self.data.lock().unwrap().flush()
123     }
124 }
125
126 fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &str) {
127     create_default_session_if_not_set_then(|_| {
128         let output = Arc::new(Mutex::new(Vec::new()));
129
130         let fallback_bundle =
131             rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
132         let source_map = Lrc::new(SourceMap::new(FilePathMapping::empty()));
133         source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned());
134
135         let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end);
136         let mut msp = MultiSpan::from_span(primary_span);
137         for span_label in span_labels {
138             let span = make_span(&file_text, &span_label.start, &span_label.end);
139             msp.push_span_label(span, span_label.label);
140             println!("span: {:?} label: {:?}", span, span_label.label);
141             println!("text: {:?}", source_map.span_to_snippet(span));
142         }
143
144         let emitter = EmitterWriter::new(
145             Box::new(Shared { data: output.clone() }),
146             Some(source_map.clone()),
147             None,
148             fallback_bundle,
149             false,
150             false,
151             false,
152             None,
153             false,
154         );
155         let handler = Handler::with_emitter(true, None, Box::new(emitter));
156         handler.span_err(msp, "foo");
157
158         assert!(
159             expected_output.chars().next() == Some('\n'),
160             "expected output should begin with newline"
161         );
162         let expected_output = &expected_output[1..];
163
164         let bytes = output.lock().unwrap();
165         let actual_output = str::from_utf8(&bytes).unwrap();
166         println!("expected output:\n------\n{}------", expected_output);
167         println!("actual output:\n------\n{}------", actual_output);
168
169         assert!(expected_output == actual_output)
170     })
171 }
172
173 fn make_span(file_text: &str, start: &Position, end: &Position) -> Span {
174     let start = make_pos(file_text, start);
175     let end = make_pos(file_text, end) + end.string.len(); // just after matching thing ends
176     assert!(start <= end);
177     Span::with_root_ctxt(BytePos(start as u32), BytePos(end as u32))
178 }
179
180 fn make_pos(file_text: &str, pos: &Position) -> usize {
181     let mut remainder = file_text;
182     let mut offset = 0;
183     for _ in 0..pos.count {
184         if let Some(n) = remainder.find(&pos.string) {
185             offset += n;
186             remainder = &remainder[n + 1..];
187         } else {
188             panic!("failed to find {} instances of {:?} in {:?}", pos.count, pos.string, file_text);
189         }
190     }
191     offset
192 }
193
194 #[test]
195 fn ends_on_col0() {
196     test_harness(
197         r#"
198 fn foo() {
199 }
200 "#,
201         vec![SpanLabel {
202             start: Position { string: "{", count: 1 },
203             end: Position { string: "}", count: 1 },
204             label: "test",
205         }],
206         r#"
207 error: foo
208  --> test.rs:2:10
209   |
210 2 |   fn foo() {
211   |  __________^
212 3 | | }
213   | |_^ test
214
215 "#,
216     );
217 }
218
219 #[test]
220 fn ends_on_col2() {
221     test_harness(
222         r#"
223 fn foo() {
224
225
226   }
227 "#,
228         vec![SpanLabel {
229             start: Position { string: "{", count: 1 },
230             end: Position { string: "}", count: 1 },
231             label: "test",
232         }],
233         r#"
234 error: foo
235  --> test.rs:2:10
236   |
237 2 |   fn foo() {
238   |  __________^
239 3 | |
240 4 | |
241 5 | |   }
242   | |___^ test
243
244 "#,
245     );
246 }
247 #[test]
248 fn non_nested() {
249     test_harness(
250         r#"
251 fn foo() {
252   X0 Y0
253   X1 Y1
254   X2 Y2
255 }
256 "#,
257         vec![
258             SpanLabel {
259                 start: Position { string: "X0", count: 1 },
260                 end: Position { string: "X2", count: 1 },
261                 label: "`X` is a good letter",
262             },
263             SpanLabel {
264                 start: Position { string: "Y0", count: 1 },
265                 end: Position { string: "Y2", count: 1 },
266                 label: "`Y` is a good letter too",
267             },
268         ],
269         r#"
270 error: foo
271  --> test.rs:3:3
272   |
273 3 |      X0 Y0
274   |  ____^__-
275   | | ___|
276   | ||
277 4 | ||   X1 Y1
278 5 | ||   X2 Y2
279   | ||____^__- `Y` is a good letter too
280   |  |____|
281   |       `X` is a good letter
282
283 "#,
284     );
285 }
286
287 #[test]
288 fn nested() {
289     test_harness(
290         r#"
291 fn foo() {
292   X0 Y0
293   Y1 X1
294 }
295 "#,
296         vec![
297             SpanLabel {
298                 start: Position { string: "X0", count: 1 },
299                 end: Position { string: "X1", count: 1 },
300                 label: "`X` is a good letter",
301             },
302             SpanLabel {
303                 start: Position { string: "Y0", count: 1 },
304                 end: Position { string: "Y1", count: 1 },
305                 label: "`Y` is a good letter too",
306             },
307         ],
308         r#"
309 error: foo
310  --> test.rs:3:3
311   |
312 3 |      X0 Y0
313   |  ____^__-
314   | | ___|
315   | ||
316 4 | ||   Y1 X1
317   | ||____-__^ `X` is a good letter
318   | |_____|
319   |       `Y` is a good letter too
320
321 "#,
322     );
323 }
324
325 #[test]
326 fn different_overlap() {
327     test_harness(
328         r#"
329 fn foo() {
330   X0 Y0 Z0
331   X1 Y1 Z1
332   X2 Y2 Z2
333   X3 Y3 Z3
334 }
335 "#,
336         vec![
337             SpanLabel {
338                 start: Position { string: "Y0", count: 1 },
339                 end: Position { string: "X2", count: 1 },
340                 label: "`X` is a good letter",
341             },
342             SpanLabel {
343                 start: Position { string: "Z1", count: 1 },
344                 end: Position { string: "X3", count: 1 },
345                 label: "`Y` is a good letter too",
346             },
347         ],
348         r#"
349 error: foo
350  --> test.rs:3:6
351   |
352 3 |      X0 Y0 Z0
353   |   ______^
354 4 |  |   X1 Y1 Z1
355   |  |_________-
356 5 | ||   X2 Y2 Z2
357   | ||____^ `X` is a good letter
358 6 | |    X3 Y3 Z3
359   | |_____- `Y` is a good letter too
360
361 "#,
362     );
363 }
364
365 #[test]
366 fn triple_overlap() {
367     test_harness(
368         r#"
369 fn foo() {
370   X0 Y0 Z0
371   X1 Y1 Z1
372   X2 Y2 Z2
373 }
374 "#,
375         vec![
376             SpanLabel {
377                 start: Position { string: "X0", count: 1 },
378                 end: Position { string: "X2", count: 1 },
379                 label: "`X` is a good letter",
380             },
381             SpanLabel {
382                 start: Position { string: "Y0", count: 1 },
383                 end: Position { string: "Y2", count: 1 },
384                 label: "`Y` is a good letter too",
385             },
386             SpanLabel {
387                 start: Position { string: "Z0", count: 1 },
388                 end: Position { string: "Z2", count: 1 },
389                 label: "`Z` label",
390             },
391         ],
392         r#"
393 error: foo
394  --> test.rs:3:3
395   |
396 3 |       X0 Y0 Z0
397   |  _____^__-__-
398   | | ____|__|
399   | || ___|
400   | |||
401 4 | |||   X1 Y1 Z1
402 5 | |||   X2 Y2 Z2
403   | |||____^__-__- `Z` label
404   |  ||____|__|
405   |   |____|  `Y` is a good letter too
406   |        `X` is a good letter
407
408 "#,
409     );
410 }
411
412 #[test]
413 fn triple_exact_overlap() {
414     test_harness(
415         r#"
416 fn foo() {
417   X0 Y0 Z0
418   X1 Y1 Z1
419   X2 Y2 Z2
420 }
421 "#,
422         vec![
423             SpanLabel {
424                 start: Position { string: "X0", count: 1 },
425                 end: Position { string: "X2", count: 1 },
426                 label: "`X` is a good letter",
427             },
428             SpanLabel {
429                 start: Position { string: "X0", count: 1 },
430                 end: Position { string: "X2", count: 1 },
431                 label: "`Y` is a good letter too",
432             },
433             SpanLabel {
434                 start: Position { string: "X0", count: 1 },
435                 end: Position { string: "X2", count: 1 },
436                 label: "`Z` label",
437             },
438         ],
439         r#"
440 error: foo
441  --> test.rs:3:3
442   |
443 3 | /   X0 Y0 Z0
444 4 | |   X1 Y1 Z1
445 5 | |   X2 Y2 Z2
446   | |    ^
447   | |    |
448   | |    `X` is a good letter
449   | |____`Y` is a good letter too
450   |      `Z` label
451
452 "#,
453     );
454 }
455
456 #[test]
457 fn minimum_depth() {
458     test_harness(
459         r#"
460 fn foo() {
461   X0 Y0 Z0
462   X1 Y1 Z1
463   X2 Y2 Z2
464   X3 Y3 Z3
465 }
466 "#,
467         vec![
468             SpanLabel {
469                 start: Position { string: "Y0", count: 1 },
470                 end: Position { string: "X1", count: 1 },
471                 label: "`X` is a good letter",
472             },
473             SpanLabel {
474                 start: Position { string: "Y1", count: 1 },
475                 end: Position { string: "Z2", count: 1 },
476                 label: "`Y` is a good letter too",
477             },
478             SpanLabel {
479                 start: Position { string: "X2", count: 1 },
480                 end: Position { string: "Y3", count: 1 },
481                 label: "`Z`",
482             },
483         ],
484         r#"
485 error: foo
486  --> test.rs:3:6
487   |
488 3 |      X0 Y0 Z0
489   |   ______^
490 4 |  |   X1 Y1 Z1
491   |  |____^_-
492   | ||____|
493   | |     `X` is a good letter
494 5 | |    X2 Y2 Z2
495   | |____-______- `Y` is a good letter too
496   |  ____|
497   | |
498 6 | |    X3 Y3 Z3
499   | |________- `Z`
500
501 "#,
502     );
503 }
504
505 #[test]
506 fn non_overlaping() {
507     test_harness(
508         r#"
509 fn foo() {
510   X0 Y0 Z0
511   X1 Y1 Z1
512   X2 Y2 Z2
513   X3 Y3 Z3
514 }
515 "#,
516         vec![
517             SpanLabel {
518                 start: Position { string: "X0", count: 1 },
519                 end: Position { string: "X1", count: 1 },
520                 label: "`X` is a good letter",
521             },
522             SpanLabel {
523                 start: Position { string: "Y2", count: 1 },
524                 end: Position { string: "Z3", count: 1 },
525                 label: "`Y` is a good letter too",
526             },
527         ],
528         r#"
529 error: foo
530  --> test.rs:3:3
531   |
532 3 | /   X0 Y0 Z0
533 4 | |   X1 Y1 Z1
534   | |____^ `X` is a good letter
535 5 |     X2 Y2 Z2
536   |  ______-
537 6 | |   X3 Y3 Z3
538   | |__________- `Y` is a good letter too
539
540 "#,
541     );
542 }
543
544 #[test]
545 fn overlaping_start_and_end() {
546     test_harness(
547         r#"
548 fn foo() {
549   X0 Y0 Z0
550   X1 Y1 Z1
551   X2 Y2 Z2
552   X3 Y3 Z3
553 }
554 "#,
555         vec![
556             SpanLabel {
557                 start: Position { string: "Y0", count: 1 },
558                 end: Position { string: "X1", count: 1 },
559                 label: "`X` is a good letter",
560             },
561             SpanLabel {
562                 start: Position { string: "Z1", count: 1 },
563                 end: Position { string: "Z3", count: 1 },
564                 label: "`Y` is a good letter too",
565             },
566         ],
567         r#"
568 error: foo
569  --> test.rs:3:6
570   |
571 3 |      X0 Y0 Z0
572   |   ______^
573 4 |  |   X1 Y1 Z1
574   |  |____^____-
575   | ||____|
576   | |     `X` is a good letter
577 5 | |    X2 Y2 Z2
578 6 | |    X3 Y3 Z3
579   | |___________- `Y` is a good letter too
580
581 "#,
582     );
583 }
584
585 #[test]
586 fn multiple_labels_primary_without_message() {
587     test_harness(
588         r#"
589 fn foo() {
590   a { b { c } d }
591 }
592 "#,
593         vec![
594             SpanLabel {
595                 start: Position { string: "b", count: 1 },
596                 end: Position { string: "}", count: 1 },
597                 label: "",
598             },
599             SpanLabel {
600                 start: Position { string: "a", count: 1 },
601                 end: Position { string: "d", count: 1 },
602                 label: "`a` is a good letter",
603             },
604             SpanLabel {
605                 start: Position { string: "c", count: 1 },
606                 end: Position { string: "c", count: 1 },
607                 label: "",
608             },
609         ],
610         r#"
611 error: foo
612  --> test.rs:3:7
613   |
614 3 |   a { b { c } d }
615   |   ----^^^^-^^-- `a` is a good letter
616
617 "#,
618     );
619 }
620
621 #[test]
622 fn multiple_labels_secondary_without_message() {
623     test_harness(
624         r#"
625 fn foo() {
626   a { b { c } d }
627 }
628 "#,
629         vec![
630             SpanLabel {
631                 start: Position { string: "a", count: 1 },
632                 end: Position { string: "d", count: 1 },
633                 label: "`a` is a good letter",
634             },
635             SpanLabel {
636                 start: Position { string: "b", count: 1 },
637                 end: Position { string: "}", count: 1 },
638                 label: "",
639             },
640         ],
641         r#"
642 error: foo
643  --> test.rs:3:3
644   |
645 3 |   a { b { c } d }
646   |   ^^^^-------^^ `a` is a good letter
647
648 "#,
649     );
650 }
651
652 #[test]
653 fn multiple_labels_primary_without_message_2() {
654     test_harness(
655         r#"
656 fn foo() {
657   a { b { c } d }
658 }
659 "#,
660         vec![
661             SpanLabel {
662                 start: Position { string: "b", count: 1 },
663                 end: Position { string: "}", count: 1 },
664                 label: "`b` is a good letter",
665             },
666             SpanLabel {
667                 start: Position { string: "a", count: 1 },
668                 end: Position { string: "d", count: 1 },
669                 label: "",
670             },
671             SpanLabel {
672                 start: Position { string: "c", count: 1 },
673                 end: Position { string: "c", count: 1 },
674                 label: "",
675             },
676         ],
677         r#"
678 error: foo
679  --> test.rs:3:7
680   |
681 3 |   a { b { c } d }
682   |   ----^^^^-^^--
683   |       |
684   |       `b` is a good letter
685
686 "#,
687     );
688 }
689
690 #[test]
691 fn multiple_labels_secondary_without_message_2() {
692     test_harness(
693         r#"
694 fn foo() {
695   a { b { c } d }
696 }
697 "#,
698         vec![
699             SpanLabel {
700                 start: Position { string: "a", count: 1 },
701                 end: Position { string: "d", count: 1 },
702                 label: "",
703             },
704             SpanLabel {
705                 start: Position { string: "b", count: 1 },
706                 end: Position { string: "}", count: 1 },
707                 label: "`b` is a good letter",
708             },
709         ],
710         r#"
711 error: foo
712  --> test.rs:3:3
713   |
714 3 |   a { b { c } d }
715   |   ^^^^-------^^
716   |       |
717   |       `b` is a good letter
718
719 "#,
720     );
721 }
722
723 #[test]
724 fn multiple_labels_secondary_without_message_3() {
725     test_harness(
726         r#"
727 fn foo() {
728   a  bc  d
729 }
730 "#,
731         vec![
732             SpanLabel {
733                 start: Position { string: "a", count: 1 },
734                 end: Position { string: "b", count: 1 },
735                 label: "`a` is a good letter",
736             },
737             SpanLabel {
738                 start: Position { string: "c", count: 1 },
739                 end: Position { string: "d", count: 1 },
740                 label: "",
741             },
742         ],
743         r#"
744 error: foo
745  --> test.rs:3:3
746   |
747 3 |   a  bc  d
748   |   ^^^^----
749   |   |
750   |   `a` is a good letter
751
752 "#,
753     );
754 }
755
756 #[test]
757 fn multiple_labels_without_message() {
758     test_harness(
759         r#"
760 fn foo() {
761   a { b { c } d }
762 }
763 "#,
764         vec![
765             SpanLabel {
766                 start: Position { string: "a", count: 1 },
767                 end: Position { string: "d", count: 1 },
768                 label: "",
769             },
770             SpanLabel {
771                 start: Position { string: "b", count: 1 },
772                 end: Position { string: "}", count: 1 },
773                 label: "",
774             },
775         ],
776         r#"
777 error: foo
778  --> test.rs:3:3
779   |
780 3 |   a { b { c } d }
781   |   ^^^^-------^^
782
783 "#,
784     );
785 }
786
787 #[test]
788 fn multiple_labels_without_message_2() {
789     test_harness(
790         r#"
791 fn foo() {
792   a { b { c } d }
793 }
794 "#,
795         vec![
796             SpanLabel {
797                 start: Position { string: "b", count: 1 },
798                 end: Position { string: "}", count: 1 },
799                 label: "",
800             },
801             SpanLabel {
802                 start: Position { string: "a", count: 1 },
803                 end: Position { string: "d", count: 1 },
804                 label: "",
805             },
806             SpanLabel {
807                 start: Position { string: "c", count: 1 },
808                 end: Position { string: "c", count: 1 },
809                 label: "",
810             },
811         ],
812         r#"
813 error: foo
814  --> test.rs:3:7
815   |
816 3 |   a { b { c } d }
817   |   ----^^^^-^^--
818
819 "#,
820     );
821 }
822
823 #[test]
824 fn multiple_labels_with_message() {
825     test_harness(
826         r#"
827 fn foo() {
828   a { b { c } d }
829 }
830 "#,
831         vec![
832             SpanLabel {
833                 start: Position { string: "a", count: 1 },
834                 end: Position { string: "d", count: 1 },
835                 label: "`a` is a good letter",
836             },
837             SpanLabel {
838                 start: Position { string: "b", count: 1 },
839                 end: Position { string: "}", count: 1 },
840                 label: "`b` is a good letter",
841             },
842         ],
843         r#"
844 error: foo
845  --> test.rs:3:3
846   |
847 3 |   a { b { c } d }
848   |   ^^^^-------^^
849   |   |   |
850   |   |   `b` is a good letter
851   |   `a` is a good letter
852
853 "#,
854     );
855 }
856
857 #[test]
858 fn single_label_with_message() {
859     test_harness(
860         r#"
861 fn foo() {
862   a { b { c } d }
863 }
864 "#,
865         vec![SpanLabel {
866             start: Position { string: "a", count: 1 },
867             end: Position { string: "d", count: 1 },
868             label: "`a` is a good letter",
869         }],
870         r#"
871 error: foo
872  --> test.rs:3:3
873   |
874 3 |   a { b { c } d }
875   |   ^^^^^^^^^^^^^ `a` is a good letter
876
877 "#,
878     );
879 }
880
881 #[test]
882 fn single_label_without_message() {
883     test_harness(
884         r#"
885 fn foo() {
886   a { b { c } d }
887 }
888 "#,
889         vec![SpanLabel {
890             start: Position { string: "a", count: 1 },
891             end: Position { string: "d", count: 1 },
892             label: "",
893         }],
894         r#"
895 error: foo
896  --> test.rs:3:3
897   |
898 3 |   a { b { c } d }
899   |   ^^^^^^^^^^^^^
900
901 "#,
902     );
903 }
904
905 #[test]
906 fn long_snippet() {
907     test_harness(
908         r#"
909 fn foo() {
910   X0 Y0 Z0
911   X1 Y1 Z1
912 1
913 2
914 3
915 4
916 5
917 6
918 7
919 8
920 9
921 10
922   X2 Y2 Z2
923   X3 Y3 Z3
924 }
925 "#,
926         vec![
927             SpanLabel {
928                 start: Position { string: "Y0", count: 1 },
929                 end: Position { string: "X1", count: 1 },
930                 label: "`X` is a good letter",
931             },
932             SpanLabel {
933                 start: Position { string: "Z1", count: 1 },
934                 end: Position { string: "Z3", count: 1 },
935                 label: "`Y` is a good letter too",
936             },
937         ],
938         r#"
939 error: foo
940   --> test.rs:3:6
941    |
942 3  |      X0 Y0 Z0
943    |   ______^
944 4  |  |   X1 Y1 Z1
945    |  |____^____-
946    | ||____|
947    | |     `X` is a good letter
948 5  | |  1
949 6  | |  2
950 7  | |  3
951 ...  |
952 15 | |    X2 Y2 Z2
953 16 | |    X3 Y3 Z3
954    | |___________- `Y` is a good letter too
955
956 "#,
957     );
958 }
959
960 #[test]
961 fn long_snippet_multiple_spans() {
962     test_harness(
963         r#"
964 fn foo() {
965   X0 Y0 Z0
966 1
967 2
968 3
969   X1 Y1 Z1
970 4
971 5
972 6
973   X2 Y2 Z2
974 7
975 8
976 9
977 10
978   X3 Y3 Z3
979 }
980 "#,
981         vec![
982             SpanLabel {
983                 start: Position { string: "Y0", count: 1 },
984                 end: Position { string: "Y3", count: 1 },
985                 label: "`Y` is a good letter",
986             },
987             SpanLabel {
988                 start: Position { string: "Z1", count: 1 },
989                 end: Position { string: "Z2", count: 1 },
990                 label: "`Z` is a good letter too",
991             },
992         ],
993         r#"
994 error: foo
995   --> test.rs:3:6
996    |
997 3  |      X0 Y0 Z0
998    |   ______^
999 4  |  | 1
1000 5  |  | 2
1001 6  |  | 3
1002 7  |  |   X1 Y1 Z1
1003    |  |_________-
1004 8  | || 4
1005 9  | || 5
1006 10 | || 6
1007 11 | ||   X2 Y2 Z2
1008    | ||__________- `Z` is a good letter too
1009 ...   |
1010 15 |  | 10
1011 16 |  |   X3 Y3 Z3
1012    |  |_______^ `Y` is a good letter
1013
1014 "#,
1015     );
1016 }