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