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