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