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