]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_expand/src/tests.rs
Auto merge of #105531 - matthiaskrgr:rollup-7y7zbgl, r=matthiaskrgr
[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             false,
155         );
156         let handler = Handler::with_emitter(true, None, Box::new(emitter));
157         #[allow(rustc::untranslatable_diagnostic)]
158         handler.span_err(msp, "foo");
159
160         assert!(
161             expected_output.chars().next() == Some('\n'),
162             "expected output should begin with newline"
163         );
164         let expected_output = &expected_output[1..];
165
166         let bytes = output.lock().unwrap();
167         let actual_output = str::from_utf8(&bytes).unwrap();
168         println!("expected output:\n------\n{}------", expected_output);
169         println!("actual output:\n------\n{}------", actual_output);
170
171         assert!(expected_output == actual_output)
172     })
173 }
174
175 fn make_span(file_text: &str, start: &Position, end: &Position) -> Span {
176     let start = make_pos(file_text, start);
177     let end = make_pos(file_text, end) + end.string.len(); // just after matching thing ends
178     assert!(start <= end);
179     Span::with_root_ctxt(BytePos(start as u32), BytePos(end as u32))
180 }
181
182 fn make_pos(file_text: &str, pos: &Position) -> usize {
183     let mut remainder = file_text;
184     let mut offset = 0;
185     for _ in 0..pos.count {
186         if let Some(n) = remainder.find(&pos.string) {
187             offset += n;
188             remainder = &remainder[n + 1..];
189         } else {
190             panic!("failed to find {} instances of {:?} in {:?}", pos.count, pos.string, file_text);
191         }
192     }
193     offset
194 }
195
196 #[test]
197 fn ends_on_col0() {
198     test_harness(
199         r#"
200 fn foo() {
201 }
202 "#,
203         vec![SpanLabel {
204             start: Position { string: "{", count: 1 },
205             end: Position { string: "}", count: 1 },
206             label: "test",
207         }],
208         r#"
209 error: foo
210  --> test.rs:2:10
211   |
212 2 |   fn foo() {
213   |  __________^
214 3 | | }
215   | |_^ test
216
217 "#,
218     );
219 }
220
221 #[test]
222 fn ends_on_col2() {
223     test_harness(
224         r#"
225 fn foo() {
226
227
228   }
229 "#,
230         vec![SpanLabel {
231             start: Position { string: "{", count: 1 },
232             end: Position { string: "}", count: 1 },
233             label: "test",
234         }],
235         r#"
236 error: foo
237  --> test.rs:2:10
238   |
239 2 |   fn foo() {
240   |  __________^
241 3 | |
242 4 | |
243 5 | |   }
244   | |___^ test
245
246 "#,
247     );
248 }
249 #[test]
250 fn non_nested() {
251     test_harness(
252         r#"
253 fn foo() {
254   X0 Y0
255   X1 Y1
256   X2 Y2
257 }
258 "#,
259         vec![
260             SpanLabel {
261                 start: Position { string: "X0", count: 1 },
262                 end: Position { string: "X2", count: 1 },
263                 label: "`X` is a good letter",
264             },
265             SpanLabel {
266                 start: Position { string: "Y0", count: 1 },
267                 end: Position { string: "Y2", count: 1 },
268                 label: "`Y` is a good letter too",
269             },
270         ],
271         r#"
272 error: foo
273  --> test.rs:3:3
274   |
275 3 |      X0 Y0
276   |   ___^__-
277   |  |___|
278   | ||
279 4 | ||   X1 Y1
280 5 | ||   X2 Y2
281   | ||____^__- `Y` is a good letter too
282   | |_____|
283   |       `X` is a good letter
284
285 "#,
286     );
287 }
288
289 #[test]
290 fn nested() {
291     test_harness(
292         r#"
293 fn foo() {
294   X0 Y0
295   Y1 X1
296 }
297 "#,
298         vec![
299             SpanLabel {
300                 start: Position { string: "X0", count: 1 },
301                 end: Position { string: "X1", count: 1 },
302                 label: "`X` is a good letter",
303             },
304             SpanLabel {
305                 start: Position { string: "Y0", count: 1 },
306                 end: Position { string: "Y1", count: 1 },
307                 label: "`Y` is a good letter too",
308             },
309         ],
310         r#"
311 error: foo
312  --> test.rs:3:3
313   |
314 3 |      X0 Y0
315   |   ___^__-
316   |  |___|
317   | ||
318 4 | ||   Y1 X1
319   | ||____-__^ `X` is a good letter
320   |  |____|
321   |       `Y` is a good letter too
322
323 "#,
324     );
325 }
326
327 #[test]
328 fn different_overlap() {
329     test_harness(
330         r#"
331 fn foo() {
332   X0 Y0 Z0
333   X1 Y1 Z1
334   X2 Y2 Z2
335   X3 Y3 Z3
336 }
337 "#,
338         vec![
339             SpanLabel {
340                 start: Position { string: "Y0", count: 1 },
341                 end: Position { string: "X2", count: 1 },
342                 label: "`X` is a good letter",
343             },
344             SpanLabel {
345                 start: Position { string: "Z1", count: 1 },
346                 end: Position { string: "X3", count: 1 },
347                 label: "`Y` is a good letter too",
348             },
349         ],
350         r#"
351 error: foo
352  --> test.rs:3:6
353   |
354 3 |      X0 Y0 Z0
355   |  _______^
356 4 | |    X1 Y1 Z1
357   | | _________-
358 5 | ||   X2 Y2 Z2
359   | ||____^ `X` is a good letter
360 6 |  |   X3 Y3 Z3
361   |  |____- `Y` is a good letter too
362
363 "#,
364     );
365 }
366
367 #[test]
368 fn triple_overlap() {
369     test_harness(
370         r#"
371 fn foo() {
372   X0 Y0 Z0
373   X1 Y1 Z1
374   X2 Y2 Z2
375 }
376 "#,
377         vec![
378             SpanLabel {
379                 start: Position { string: "X0", count: 1 },
380                 end: Position { string: "X2", count: 1 },
381                 label: "`X` is a good letter",
382             },
383             SpanLabel {
384                 start: Position { string: "Y0", count: 1 },
385                 end: Position { string: "Y2", count: 1 },
386                 label: "`Y` is a good letter too",
387             },
388             SpanLabel {
389                 start: Position { string: "Z0", count: 1 },
390                 end: Position { string: "Z2", count: 1 },
391                 label: "`Z` label",
392             },
393         ],
394         r#"
395 error: foo
396  --> test.rs:3:3
397   |
398 3 |       X0 Y0 Z0
399   |    ___^__-__-
400   |   |___|__|
401   |  ||___|
402   | |||
403 4 | |||   X1 Y1 Z1
404 5 | |||   X2 Y2 Z2
405   | |||____^__-__- `Z` label
406   | ||_____|__|
407   | |______|  `Y` is a good letter too
408   |        `X` is a good letter
409
410 "#,
411     );
412 }
413
414 #[test]
415 fn triple_exact_overlap() {
416     test_harness(
417         r#"
418 fn foo() {
419   X0 Y0 Z0
420   X1 Y1 Z1
421   X2 Y2 Z2
422 }
423 "#,
424         vec![
425             SpanLabel {
426                 start: Position { string: "X0", count: 1 },
427                 end: Position { string: "X2", count: 1 },
428                 label: "`X` is a good letter",
429             },
430             SpanLabel {
431                 start: Position { string: "X0", count: 1 },
432                 end: Position { string: "X2", count: 1 },
433                 label: "`Y` is a good letter too",
434             },
435             SpanLabel {
436                 start: Position { string: "X0", count: 1 },
437                 end: Position { string: "X2", count: 1 },
438                 label: "`Z` label",
439             },
440         ],
441         r#"
442 error: foo
443  --> test.rs:3:3
444   |
445 3 | /   X0 Y0 Z0
446 4 | |   X1 Y1 Z1
447 5 | |   X2 Y2 Z2
448   | |    ^
449   | |    |
450   | |    `X` is a good letter
451   | |____`Y` is a good letter too
452   |      `Z` label
453
454 "#,
455     );
456 }
457
458 #[test]
459 fn minimum_depth() {
460     test_harness(
461         r#"
462 fn foo() {
463   X0 Y0 Z0
464   X1 Y1 Z1
465   X2 Y2 Z2
466   X3 Y3 Z3
467 }
468 "#,
469         vec![
470             SpanLabel {
471                 start: Position { string: "Y0", count: 1 },
472                 end: Position { string: "X1", count: 1 },
473                 label: "`X` is a good letter",
474             },
475             SpanLabel {
476                 start: Position { string: "Y1", count: 1 },
477                 end: Position { string: "Z2", count: 1 },
478                 label: "`Y` is a good letter too",
479             },
480             SpanLabel {
481                 start: Position { string: "X2", count: 1 },
482                 end: Position { string: "Y3", count: 1 },
483                 label: "`Z`",
484             },
485         ],
486         r#"
487 error: foo
488  --> test.rs:3:6
489   |
490 3 |      X0 Y0 Z0
491   |  _______^
492 4 | |    X1 Y1 Z1
493   | | ____^_-
494   | ||____|
495   |  |    `X` is a good letter
496 5 |  |   X2 Y2 Z2
497   |  |___-______- `Y` is a good letter too
498   |   ___|
499   |  |
500 6 |  |   X3 Y3 Z3
501   |  |_______- `Z`
502
503 "#,
504     );
505 }
506
507 #[test]
508 fn non_overlaping() {
509     test_harness(
510         r#"
511 fn foo() {
512   X0 Y0 Z0
513   X1 Y1 Z1
514   X2 Y2 Z2
515   X3 Y3 Z3
516 }
517 "#,
518         vec![
519             SpanLabel {
520                 start: Position { string: "X0", count: 1 },
521                 end: Position { string: "X1", count: 1 },
522                 label: "`X` is a good letter",
523             },
524             SpanLabel {
525                 start: Position { string: "Y2", count: 1 },
526                 end: Position { string: "Z3", count: 1 },
527                 label: "`Y` is a good letter too",
528             },
529         ],
530         r#"
531 error: foo
532  --> test.rs:3:3
533   |
534 3 | /   X0 Y0 Z0
535 4 | |   X1 Y1 Z1
536   | |____^ `X` is a good letter
537 5 |     X2 Y2 Z2
538   |  ______-
539 6 | |   X3 Y3 Z3
540   | |__________- `Y` is a good letter too
541
542 "#,
543     );
544 }
545
546 #[test]
547 fn overlaping_start_and_end() {
548     test_harness(
549         r#"
550 fn foo() {
551   X0 Y0 Z0
552   X1 Y1 Z1
553   X2 Y2 Z2
554   X3 Y3 Z3
555 }
556 "#,
557         vec![
558             SpanLabel {
559                 start: Position { string: "Y0", count: 1 },
560                 end: Position { string: "X1", count: 1 },
561                 label: "`X` is a good letter",
562             },
563             SpanLabel {
564                 start: Position { string: "Z1", count: 1 },
565                 end: Position { string: "Z3", count: 1 },
566                 label: "`Y` is a good letter too",
567             },
568         ],
569         r#"
570 error: foo
571  --> test.rs:3:6
572   |
573 3 |      X0 Y0 Z0
574   |  _______^
575 4 | |    X1 Y1 Z1
576   | | ____^____-
577   | ||____|
578   |  |    `X` is a good letter
579 5 |  |   X2 Y2 Z2
580 6 |  |   X3 Y3 Z3
581   |  |__________- `Y` is a good letter too
582
583 "#,
584     );
585 }
586
587 #[test]
588 fn multiple_labels_primary_without_message() {
589     test_harness(
590         r#"
591 fn foo() {
592   a { b { c } d }
593 }
594 "#,
595         vec![
596             SpanLabel {
597                 start: Position { string: "b", count: 1 },
598                 end: Position { string: "}", count: 1 },
599                 label: "",
600             },
601             SpanLabel {
602                 start: Position { string: "a", count: 1 },
603                 end: Position { string: "d", count: 1 },
604                 label: "`a` is a good letter",
605             },
606             SpanLabel {
607                 start: Position { string: "c", count: 1 },
608                 end: Position { string: "c", count: 1 },
609                 label: "",
610             },
611         ],
612         r#"
613 error: foo
614  --> test.rs:3:7
615   |
616 3 |   a { b { c } d }
617   |   ----^^^^-^^-- `a` is a good letter
618
619 "#,
620     );
621 }
622
623 #[test]
624 fn multiple_labels_secondary_without_message() {
625     test_harness(
626         r#"
627 fn foo() {
628   a { b { c } d }
629 }
630 "#,
631         vec![
632             SpanLabel {
633                 start: Position { string: "a", count: 1 },
634                 end: Position { string: "d", count: 1 },
635                 label: "`a` is a good letter",
636             },
637             SpanLabel {
638                 start: Position { string: "b", count: 1 },
639                 end: Position { string: "}", count: 1 },
640                 label: "",
641             },
642         ],
643         r#"
644 error: foo
645  --> test.rs:3:3
646   |
647 3 |   a { b { c } d }
648   |   ^^^^-------^^ `a` is a good letter
649
650 "#,
651     );
652 }
653
654 #[test]
655 fn multiple_labels_primary_without_message_2() {
656     test_harness(
657         r#"
658 fn foo() {
659   a { b { c } d }
660 }
661 "#,
662         vec![
663             SpanLabel {
664                 start: Position { string: "b", count: 1 },
665                 end: Position { string: "}", count: 1 },
666                 label: "`b` is a good letter",
667             },
668             SpanLabel {
669                 start: Position { string: "a", count: 1 },
670                 end: Position { string: "d", count: 1 },
671                 label: "",
672             },
673             SpanLabel {
674                 start: Position { string: "c", count: 1 },
675                 end: Position { string: "c", count: 1 },
676                 label: "",
677             },
678         ],
679         r#"
680 error: foo
681  --> test.rs:3:7
682   |
683 3 |   a { b { c } d }
684   |   ----^^^^-^^--
685   |       |
686   |       `b` is a good letter
687
688 "#,
689     );
690 }
691
692 #[test]
693 fn multiple_labels_secondary_without_message_2() {
694     test_harness(
695         r#"
696 fn foo() {
697   a { b { c } d }
698 }
699 "#,
700         vec![
701             SpanLabel {
702                 start: Position { string: "a", count: 1 },
703                 end: Position { string: "d", count: 1 },
704                 label: "",
705             },
706             SpanLabel {
707                 start: Position { string: "b", count: 1 },
708                 end: Position { string: "}", count: 1 },
709                 label: "`b` is a good letter",
710             },
711         ],
712         r#"
713 error: foo
714  --> test.rs:3:3
715   |
716 3 |   a { b { c } d }
717   |   ^^^^-------^^
718   |       |
719   |       `b` is a good letter
720
721 "#,
722     );
723 }
724
725 #[test]
726 fn multiple_labels_secondary_without_message_3() {
727     test_harness(
728         r#"
729 fn foo() {
730   a  bc  d
731 }
732 "#,
733         vec![
734             SpanLabel {
735                 start: Position { string: "a", count: 1 },
736                 end: Position { string: "b", count: 1 },
737                 label: "`a` is a good letter",
738             },
739             SpanLabel {
740                 start: Position { string: "c", count: 1 },
741                 end: Position { string: "d", count: 1 },
742                 label: "",
743             },
744         ],
745         r#"
746 error: foo
747  --> test.rs:3:3
748   |
749 3 |   a  bc  d
750   |   ^^^^----
751   |   |
752   |   `a` is a good letter
753
754 "#,
755     );
756 }
757
758 #[test]
759 fn multiple_labels_without_message() {
760     test_harness(
761         r#"
762 fn foo() {
763   a { b { c } d }
764 }
765 "#,
766         vec![
767             SpanLabel {
768                 start: Position { string: "a", count: 1 },
769                 end: Position { string: "d", count: 1 },
770                 label: "",
771             },
772             SpanLabel {
773                 start: Position { string: "b", count: 1 },
774                 end: Position { string: "}", count: 1 },
775                 label: "",
776             },
777         ],
778         r#"
779 error: foo
780  --> test.rs:3:3
781   |
782 3 |   a { b { c } d }
783   |   ^^^^-------^^
784
785 "#,
786     );
787 }
788
789 #[test]
790 fn multiple_labels_without_message_2() {
791     test_harness(
792         r#"
793 fn foo() {
794   a { b { c } d }
795 }
796 "#,
797         vec![
798             SpanLabel {
799                 start: Position { string: "b", count: 1 },
800                 end: Position { string: "}", count: 1 },
801                 label: "",
802             },
803             SpanLabel {
804                 start: Position { string: "a", count: 1 },
805                 end: Position { string: "d", count: 1 },
806                 label: "",
807             },
808             SpanLabel {
809                 start: Position { string: "c", count: 1 },
810                 end: Position { string: "c", count: 1 },
811                 label: "",
812             },
813         ],
814         r#"
815 error: foo
816  --> test.rs:3:7
817   |
818 3 |   a { b { c } d }
819   |   ----^^^^-^^--
820
821 "#,
822     );
823 }
824
825 #[test]
826 fn multiple_labels_with_message() {
827     test_harness(
828         r#"
829 fn foo() {
830   a { b { c } d }
831 }
832 "#,
833         vec![
834             SpanLabel {
835                 start: Position { string: "a", count: 1 },
836                 end: Position { string: "d", count: 1 },
837                 label: "`a` is a good letter",
838             },
839             SpanLabel {
840                 start: Position { string: "b", count: 1 },
841                 end: Position { string: "}", count: 1 },
842                 label: "`b` is a good letter",
843             },
844         ],
845         r#"
846 error: foo
847  --> test.rs:3:3
848   |
849 3 |   a { b { c } d }
850   |   ^^^^-------^^
851   |   |   |
852   |   |   `b` is a good letter
853   |   `a` is a good letter
854
855 "#,
856     );
857 }
858
859 #[test]
860 fn single_label_with_message() {
861     test_harness(
862         r#"
863 fn foo() {
864   a { b { c } d }
865 }
866 "#,
867         vec![SpanLabel {
868             start: Position { string: "a", count: 1 },
869             end: Position { string: "d", count: 1 },
870             label: "`a` is a good letter",
871         }],
872         r#"
873 error: foo
874  --> test.rs:3:3
875   |
876 3 |   a { b { c } d }
877   |   ^^^^^^^^^^^^^ `a` is a good letter
878
879 "#,
880     );
881 }
882
883 #[test]
884 fn single_label_without_message() {
885     test_harness(
886         r#"
887 fn foo() {
888   a { b { c } d }
889 }
890 "#,
891         vec![SpanLabel {
892             start: Position { string: "a", count: 1 },
893             end: Position { string: "d", count: 1 },
894             label: "",
895         }],
896         r#"
897 error: foo
898  --> test.rs:3:3
899   |
900 3 |   a { b { c } d }
901   |   ^^^^^^^^^^^^^
902
903 "#,
904     );
905 }
906
907 #[test]
908 fn long_snippet() {
909     test_harness(
910         r#"
911 fn foo() {
912   X0 Y0 Z0
913   X1 Y1 Z1
914 1
915 2
916 3
917 4
918 5
919 6
920 7
921 8
922 9
923 10
924   X2 Y2 Z2
925   X3 Y3 Z3
926 }
927 "#,
928         vec![
929             SpanLabel {
930                 start: Position { string: "Y0", count: 1 },
931                 end: Position { string: "X1", count: 1 },
932                 label: "`X` is a good letter",
933             },
934             SpanLabel {
935                 start: Position { string: "Z1", count: 1 },
936                 end: Position { string: "Z3", count: 1 },
937                 label: "`Y` is a good letter too",
938             },
939         ],
940         r#"
941 error: foo
942   --> test.rs:3:6
943    |
944 3  |      X0 Y0 Z0
945    |  _______^
946 4  | |    X1 Y1 Z1
947    | | ____^____-
948    | ||____|
949    |  |    `X` is a good letter
950 5  |  | 1
951 6  |  | 2
952 7  |  | 3
953 ...   |
954 15 |  |   X2 Y2 Z2
955 16 |  |   X3 Y3 Z3
956    |  |__________- `Y` is a good letter too
957
958 "#,
959     );
960 }
961
962 #[test]
963 fn long_snippet_multiple_spans() {
964     test_harness(
965         r#"
966 fn foo() {
967   X0 Y0 Z0
968 1
969 2
970 3
971   X1 Y1 Z1
972 4
973 5
974 6
975   X2 Y2 Z2
976 7
977 8
978 9
979 10
980   X3 Y3 Z3
981 }
982 "#,
983         vec![
984             SpanLabel {
985                 start: Position { string: "Y0", count: 1 },
986                 end: Position { string: "Y3", count: 1 },
987                 label: "`Y` is a good letter",
988             },
989             SpanLabel {
990                 start: Position { string: "Z1", count: 1 },
991                 end: Position { string: "Z2", count: 1 },
992                 label: "`Z` is a good letter too",
993             },
994         ],
995         r#"
996 error: foo
997   --> test.rs:3:6
998    |
999 3  |      X0 Y0 Z0
1000    |  _______^
1001 4  | |  1
1002 5  | |  2
1003 6  | |  3
1004 7  | |    X1 Y1 Z1
1005    | | _________-
1006 8  | || 4
1007 9  | || 5
1008 10 | || 6
1009 11 | ||   X2 Y2 Z2
1010    | ||__________- `Z` is a good letter too
1011 ...  |
1012 15 | |  10
1013 16 | |    X3 Y3 Z3
1014    | |________^ `Y` is a good letter
1015
1016 "#,
1017     );
1018 }