]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/test_snippet.rs
Specify output filenames for compatibility with Windows
[rust.git] / src / libsyntax / test_snippet.rs
1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use codemap::{CodeMap, FilePathMapping};
12 use errors::Handler;
13 use errors::emitter::EmitterWriter;
14 use std::io;
15 use std::io::prelude::*;
16 use std::rc::Rc;
17 use std::str;
18 use std::sync::{Arc, Mutex};
19 use std::path::Path;
20 use syntax_pos::{BytePos, NO_EXPANSION, Span, MultiSpan};
21
22 /// Identify a position in the text by the Nth occurrence of a string.
23 struct Position {
24     string: &'static str,
25     count: usize,
26 }
27
28 struct SpanLabel {
29     start: Position,
30     end: Position,
31     label: &'static str,
32 }
33
34 struct Shared<T: Write> {
35     data: Arc<Mutex<T>>,
36 }
37
38 impl<T: Write> Write for Shared<T> {
39     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
40         self.data.lock().unwrap().write(buf)
41     }
42
43     fn flush(&mut self) -> io::Result<()> {
44         self.data.lock().unwrap().flush()
45     }
46 }
47
48 fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &str) {
49     let output = Arc::new(Mutex::new(Vec::new()));
50
51     let code_map = Rc::new(CodeMap::new(FilePathMapping::empty()));
52     code_map.new_filemap_and_lines(Path::new("test.rs"), &file_text);
53
54     let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end);
55     let mut msp = MultiSpan::from_span(primary_span);
56     for span_label in span_labels {
57         let span = make_span(&file_text, &span_label.start, &span_label.end);
58         msp.push_span_label(span, span_label.label.to_string());
59         println!("span: {:?} label: {:?}", span, span_label.label);
60         println!("text: {:?}", code_map.span_to_snippet(span));
61     }
62
63     let emitter = EmitterWriter::new(Box::new(Shared { data: output.clone() }),
64                                      Some(code_map.clone()),
65                                      false);
66     let handler = Handler::with_emitter(true, false, Box::new(emitter));
67     handler.span_err(msp, "foo");
68
69     assert!(expected_output.chars().next() == Some('\n'),
70             "expected output should begin with newline");
71     let expected_output = &expected_output[1..];
72
73     let bytes = output.lock().unwrap();
74     let actual_output = str::from_utf8(&bytes).unwrap();
75     println!("expected output:\n------\n{}------", expected_output);
76     println!("actual output:\n------\n{}------", actual_output);
77
78     assert!(expected_output == actual_output)
79 }
80
81 fn make_span(file_text: &str, start: &Position, end: &Position) -> Span {
82     let start = make_pos(file_text, start);
83     let end = make_pos(file_text, end) + end.string.len(); // just after matching thing ends
84     assert!(start <= end);
85     Span::new(BytePos(start as u32), BytePos(end as u32), NO_EXPANSION)
86 }
87
88 fn make_pos(file_text: &str, pos: &Position) -> usize {
89     let mut remainder = file_text;
90     let mut offset = 0;
91     for _ in 0..pos.count {
92         if let Some(n) = remainder.find(&pos.string) {
93             offset += n;
94             remainder = &remainder[n + 1..];
95         } else {
96             panic!("failed to find {} instances of {:?} in {:?}",
97                    pos.count,
98                    pos.string,
99                    file_text);
100         }
101     }
102     offset
103 }
104
105 #[test]
106 fn ends_on_col0() {
107     test_harness(r#"
108 fn foo() {
109 }
110 "#,
111     vec![
112         SpanLabel {
113            start: Position {
114                string: "{",
115                count: 1,
116            },
117            end: Position {
118                string: "}",
119                count: 1,
120            },
121            label: "test",
122        },
123     ],
124     r#"
125 error: foo
126  --> test.rs:2:10
127   |
128 2 |   fn foo() {
129   |  __________^
130 3 | | }
131   | |_^ test
132
133 "#);
134 }
135
136 #[test]
137 fn ends_on_col2() {
138     test_harness(r#"
139 fn foo() {
140
141
142   }
143 "#,
144      vec![
145         SpanLabel {
146             start: Position {
147                 string: "{",
148                 count: 1,
149             },
150             end: Position {
151                 string: "}",
152                 count: 1,
153             },
154             label: "test",
155         },
156      ],
157      r#"
158 error: foo
159  --> test.rs:2:10
160   |
161 2 |   fn foo() {
162   |  __________^
163 3 | |
164 4 | |
165 5 | |   }
166   | |___^ test
167
168 "#);
169 }
170 #[test]
171 fn non_nested() {
172     test_harness(r#"
173 fn foo() {
174   X0 Y0
175   X1 Y1
176   X2 Y2
177 }
178 "#,
179     vec![
180         SpanLabel {
181             start: Position {
182                 string: "X0",
183                 count: 1,
184             },
185             end: Position {
186                 string: "X2",
187                 count: 1,
188             },
189             label: "`X` is a good letter",
190         },
191         SpanLabel {
192             start: Position {
193                 string: "Y0",
194                 count: 1,
195             },
196             end: Position {
197                 string: "Y2",
198                 count: 1,
199             },
200             label: "`Y` is a good letter too",
201         },
202     ],
203     r#"
204 error: foo
205  --> test.rs:3:3
206   |
207 3 |      X0 Y0
208   |  ____^__-
209   | | ___|
210   | ||
211 4 | ||   X1 Y1
212 5 | ||   X2 Y2
213   | ||____^__- `Y` is a good letter too
214   |  |____|
215   |       `X` is a good letter
216
217 "#);
218 }
219
220 #[test]
221 fn nested() {
222     test_harness(r#"
223 fn foo() {
224   X0 Y0
225   Y1 X1
226 }
227 "#,
228     vec![
229         SpanLabel {
230             start: Position {
231                 string: "X0",
232                 count: 1,
233             },
234             end: Position {
235                 string: "X1",
236                 count: 1,
237             },
238             label: "`X` is a good letter",
239         },
240         SpanLabel {
241             start: Position {
242                 string: "Y0",
243                 count: 1,
244             },
245             end: Position {
246                 string: "Y1",
247                 count: 1,
248             },
249             label: "`Y` is a good letter too",
250         },
251     ],
252 r#"
253 error: foo
254  --> test.rs:3:3
255   |
256 3 |      X0 Y0
257   |  ____^__-
258   | | ___|
259   | ||
260 4 | ||   Y1 X1
261   | ||____-__^ `X` is a good letter
262   | |_____|
263   |       `Y` is a good letter too
264
265 "#);
266 }
267
268 #[test]
269 fn different_overlap() {
270     test_harness(r#"
271 fn foo() {
272   X0 Y0 Z0
273   X1 Y1 Z1
274   X2 Y2 Z2
275   X3 Y3 Z3
276 }
277 "#,
278     vec![
279         SpanLabel {
280             start: Position {
281                 string: "Y0",
282                 count: 1,
283             },
284             end: Position {
285                 string: "X2",
286                 count: 1,
287             },
288             label: "`X` is a good letter",
289         },
290         SpanLabel {
291             start: Position {
292                 string: "Z1",
293                 count: 1,
294             },
295             end: Position {
296                 string: "X3",
297                 count: 1,
298             },
299             label: "`Y` is a good letter too",
300         },
301     ],
302     r#"
303 error: foo
304  --> test.rs:3:6
305   |
306 3 |      X0 Y0 Z0
307   |   ______^
308 4 |  |   X1 Y1 Z1
309   |  |_________-
310 5 | ||   X2 Y2 Z2
311   | ||____^ `X` is a good letter
312 6 | |    X3 Y3 Z3
313   | |_____- `Y` is a good letter too
314
315 "#);
316 }
317
318 #[test]
319 fn triple_overlap() {
320     test_harness(r#"
321 fn foo() {
322   X0 Y0 Z0
323   X1 Y1 Z1
324   X2 Y2 Z2
325 }
326 "#,
327     vec![
328         SpanLabel {
329             start: Position {
330                 string: "X0",
331                 count: 1,
332             },
333             end: Position {
334                 string: "X2",
335                 count: 1,
336             },
337             label: "`X` is a good letter",
338         },
339         SpanLabel {
340             start: Position {
341                 string: "Y0",
342                 count: 1,
343             },
344             end: Position {
345                 string: "Y2",
346                 count: 1,
347             },
348             label: "`Y` is a good letter too",
349         },
350         SpanLabel {
351             start: Position {
352                 string: "Z0",
353                 count: 1,
354             },
355             end: Position {
356                 string: "Z2",
357                 count: 1,
358             },
359             label: "`Z` label",
360         },
361     ],
362     r#"
363 error: foo
364  --> test.rs:3:3
365   |
366 3 |       X0 Y0 Z0
367   |  _____^__-__-
368   | | ____|__|
369   | || ___|
370   | |||
371 4 | |||   X1 Y1 Z1
372 5 | |||   X2 Y2 Z2
373   | |||____^__-__- `Z` label
374   |  ||____|__|
375   |   |____|  `Y` is a good letter too
376   |        `X` is a good letter
377
378 "#);
379 }
380
381 #[test]
382 fn minimum_depth() {
383     test_harness(r#"
384 fn foo() {
385   X0 Y0 Z0
386   X1 Y1 Z1
387   X2 Y2 Z2
388   X3 Y3 Z3
389 }
390 "#,
391     vec![
392         SpanLabel {
393             start: Position {
394                 string: "Y0",
395                 count: 1,
396             },
397             end: Position {
398                 string: "X1",
399                 count: 1,
400             },
401             label: "`X` is a good letter",
402         },
403         SpanLabel {
404             start: Position {
405                 string: "Y1",
406                 count: 1,
407             },
408             end: Position {
409                 string: "Z2",
410                 count: 1,
411             },
412             label: "`Y` is a good letter too",
413         },
414         SpanLabel {
415             start: Position {
416                 string: "X2",
417                 count: 1,
418             },
419             end: Position {
420                 string: "Y3",
421                 count: 1,
422             },
423             label: "`Z`",
424         },
425     ],
426     r#"
427 error: foo
428  --> test.rs:3:6
429   |
430 3 |      X0 Y0 Z0
431   |   ______^
432 4 |  |   X1 Y1 Z1
433   |  |____^_-
434   | ||____|
435   | |     `X` is a good letter
436 5 | |    X2 Y2 Z2
437   | |____-______- `Y` is a good letter too
438   |  ____|
439   | |
440 6 | |    X3 Y3 Z3
441   | |________- `Z`
442
443 "#);
444 }
445
446 #[test]
447 fn non_overlaping() {
448     test_harness(r#"
449 fn foo() {
450   X0 Y0 Z0
451   X1 Y1 Z1
452   X2 Y2 Z2
453   X3 Y3 Z3
454 }
455 "#,
456     vec![
457         SpanLabel {
458             start: Position {
459                 string: "X0",
460                 count: 1,
461             },
462             end: Position {
463                 string: "X1",
464                 count: 1,
465             },
466             label: "`X` is a good letter",
467         },
468         SpanLabel {
469             start: Position {
470                 string: "Y2",
471                 count: 1,
472             },
473             end: Position {
474                 string: "Z3",
475                 count: 1,
476             },
477             label: "`Y` is a good letter too",
478         },
479     ],
480     r#"
481 error: foo
482  --> test.rs:3:3
483   |
484 3 | /   X0 Y0 Z0
485 4 | |   X1 Y1 Z1
486   | |____^ `X` is a good letter
487 5 |     X2 Y2 Z2
488   |  ______-
489 6 | |   X3 Y3 Z3
490   | |__________- `Y` is a good letter too
491
492 "#);
493 }
494
495 #[test]
496 fn overlaping_start_and_end() {
497     test_harness(r#"
498 fn foo() {
499   X0 Y0 Z0
500   X1 Y1 Z1
501   X2 Y2 Z2
502   X3 Y3 Z3
503 }
504 "#,
505     vec![
506         SpanLabel {
507             start: Position {
508                 string: "Y0",
509                 count: 1,
510             },
511             end: Position {
512                 string: "X1",
513                 count: 1,
514             },
515             label: "`X` is a good letter",
516         },
517         SpanLabel {
518             start: Position {
519                 string: "Z1",
520                 count: 1,
521             },
522             end: Position {
523                 string: "Z3",
524                 count: 1,
525             },
526             label: "`Y` is a good letter too",
527         },
528     ],
529     r#"
530 error: foo
531  --> test.rs:3:6
532   |
533 3 |      X0 Y0 Z0
534   |   ______^
535 4 |  |   X1 Y1 Z1
536   |  |____^____-
537   | ||____|
538   | |     `X` is a good letter
539 5 | |    X2 Y2 Z2
540 6 | |    X3 Y3 Z3
541   | |___________- `Y` is a good letter too
542
543 "#);
544 }
545
546 #[test]
547 fn multiple_labels_primary_without_message() {
548     test_harness(r#"
549 fn foo() {
550   a { b { c } d }
551 }
552 "#,
553     vec![
554         SpanLabel {
555             start: Position {
556                 string: "b",
557                 count: 1,
558             },
559             end: Position {
560                 string: "}",
561                 count: 1,
562             },
563             label: "",
564         },
565         SpanLabel {
566             start: Position {
567                 string: "a",
568                 count: 1,
569             },
570             end: Position {
571                 string: "d",
572                 count: 1,
573             },
574             label: "`a` is a good letter",
575         },
576         SpanLabel {
577             start: Position {
578                 string: "c",
579                 count: 1,
580             },
581             end: Position {
582                 string: "c",
583                 count: 1,
584             },
585             label: "",
586         },
587     ],
588     r#"
589 error: foo
590  --> test.rs:3:7
591   |
592 3 |   a { b { c } d }
593   |   ----^^^^-^^-- `a` is a good letter
594
595 "#);
596 }
597
598 #[test]
599 fn multiple_labels_secondary_without_message() {
600     test_harness(r#"
601 fn foo() {
602   a { b { c } d }
603 }
604 "#,
605     vec![
606         SpanLabel {
607             start: Position {
608                 string: "a",
609                 count: 1,
610             },
611             end: Position {
612                 string: "d",
613                 count: 1,
614             },
615             label: "`a` is a good letter",
616         },
617         SpanLabel {
618             start: Position {
619                 string: "b",
620                 count: 1,
621             },
622             end: Position {
623                 string: "}",
624                 count: 1,
625             },
626             label: "",
627         },
628     ],
629     r#"
630 error: foo
631  --> test.rs:3:3
632   |
633 3 |   a { b { c } d }
634   |   ^^^^-------^^ `a` is a good letter
635
636 "#);
637 }
638
639 #[test]
640 fn multiple_labels_primary_without_message_2() {
641     test_harness(r#"
642 fn foo() {
643   a { b { c } d }
644 }
645 "#,
646     vec![
647         SpanLabel {
648             start: Position {
649                 string: "b",
650                 count: 1,
651             },
652             end: Position {
653                 string: "}",
654                 count: 1,
655             },
656             label: "`b` is a good letter",
657         },
658         SpanLabel {
659             start: Position {
660                 string: "a",
661                 count: 1,
662             },
663             end: Position {
664                 string: "d",
665                 count: 1,
666             },
667             label: "",
668         },
669         SpanLabel {
670             start: Position {
671                 string: "c",
672                 count: 1,
673             },
674             end: Position {
675                 string: "c",
676                 count: 1,
677             },
678             label: "",
679         },
680     ],
681     r#"
682 error: foo
683  --> test.rs:3:7
684   |
685 3 |   a { b { c } d }
686   |   ----^^^^-^^--
687   |       |
688   |       `b` is a good letter
689
690 "#);
691 }
692
693 #[test]
694 fn multiple_labels_secondary_without_message_2() {
695     test_harness(r#"
696 fn foo() {
697   a { b { c } d }
698 }
699 "#,
700     vec![
701         SpanLabel {
702             start: Position {
703                 string: "a",
704                 count: 1,
705             },
706             end: Position {
707                 string: "d",
708                 count: 1,
709             },
710             label: "",
711         },
712         SpanLabel {
713             start: Position {
714                 string: "b",
715                 count: 1,
716             },
717             end: Position {
718                 string: "}",
719                 count: 1,
720             },
721             label: "`b` is a good letter",
722         },
723     ],
724     r#"
725 error: foo
726  --> test.rs:3:3
727   |
728 3 |   a { b { c } d }
729   |   ^^^^-------^^
730   |       |
731   |       `b` is a good letter
732
733 "#);
734 }
735
736 #[test]
737 fn multiple_labels_secondary_without_message_3() {
738     test_harness(r#"
739 fn foo() {
740   a  bc  d
741 }
742 "#,
743     vec![
744         SpanLabel {
745             start: Position {
746                 string: "a",
747                 count: 1,
748             },
749             end: Position {
750                 string: "b",
751                 count: 1,
752             },
753             label: "`a` is a good letter",
754         },
755         SpanLabel {
756             start: Position {
757                 string: "c",
758                 count: 1,
759             },
760             end: Position {
761                 string: "d",
762                 count: 1,
763             },
764             label: "",
765         },
766     ],
767     r#"
768 error: foo
769  --> test.rs:3:3
770   |
771 3 |   a  bc  d
772   |   ^^^^----
773   |   |
774   |   `a` is a good letter
775
776 "#);
777 }
778
779 #[test]
780 fn multiple_labels_without_message() {
781     test_harness(r#"
782 fn foo() {
783   a { b { c } d }
784 }
785 "#,
786     vec![
787         SpanLabel {
788             start: Position {
789                 string: "a",
790                 count: 1,
791             },
792             end: Position {
793                 string: "d",
794                 count: 1,
795             },
796             label: "",
797         },
798         SpanLabel {
799             start: Position {
800                 string: "b",
801                 count: 1,
802             },
803             end: Position {
804                 string: "}",
805                 count: 1,
806             },
807             label: "",
808         },
809     ],
810     r#"
811 error: foo
812  --> test.rs:3:3
813   |
814 3 |   a { b { c } d }
815   |   ^^^^-------^^
816
817 "#);
818 }
819
820 #[test]
821 fn multiple_labels_without_message_2() {
822     test_harness(r#"
823 fn foo() {
824   a { b { c } d }
825 }
826 "#,
827     vec![
828         SpanLabel {
829             start: Position {
830                 string: "b",
831                 count: 1,
832             },
833             end: Position {
834                 string: "}",
835                 count: 1,
836             },
837             label: "",
838         },
839         SpanLabel {
840             start: Position {
841                 string: "a",
842                 count: 1,
843             },
844             end: Position {
845                 string: "d",
846                 count: 1,
847             },
848             label: "",
849         },
850         SpanLabel {
851             start: Position {
852                 string: "c",
853                 count: 1,
854             },
855             end: Position {
856                 string: "c",
857                 count: 1,
858             },
859             label: "",
860         },
861     ],
862     r#"
863 error: foo
864  --> test.rs:3:7
865   |
866 3 |   a { b { c } d }
867   |   ----^^^^-^^--
868
869 "#);
870 }
871
872 #[test]
873 fn multiple_labels_with_message() {
874     test_harness(r#"
875 fn foo() {
876   a { b { c } d }
877 }
878 "#,
879     vec![
880         SpanLabel {
881             start: Position {
882                 string: "a",
883                 count: 1,
884             },
885             end: Position {
886                 string: "d",
887                 count: 1,
888             },
889             label: "`a` is a good letter",
890         },
891         SpanLabel {
892             start: Position {
893                 string: "b",
894                 count: 1,
895             },
896             end: Position {
897                 string: "}",
898                 count: 1,
899             },
900             label: "`b` is a good letter",
901         },
902     ],
903     r#"
904 error: foo
905  --> test.rs:3:3
906   |
907 3 |   a { b { c } d }
908   |   ^^^^-------^^
909   |   |   |
910   |   |   `b` is a good letter
911   |   `a` is a good letter
912
913 "#);
914 }
915
916 #[test]
917 fn single_label_with_message() {
918     test_harness(r#"
919 fn foo() {
920   a { b { c } d }
921 }
922 "#,
923     vec![
924         SpanLabel {
925             start: Position {
926                 string: "a",
927                 count: 1,
928             },
929             end: Position {
930                 string: "d",
931                 count: 1,
932             },
933             label: "`a` is a good letter",
934         },
935     ],
936     r#"
937 error: foo
938  --> test.rs:3:3
939   |
940 3 |   a { b { c } d }
941   |   ^^^^^^^^^^^^^ `a` is a good letter
942
943 "#);
944 }
945
946 #[test]
947 fn single_label_without_message() {
948     test_harness(r#"
949 fn foo() {
950   a { b { c } d }
951 }
952 "#,
953     vec![
954         SpanLabel {
955             start: Position {
956                 string: "a",
957                 count: 1,
958             },
959             end: Position {
960                 string: "d",
961                 count: 1,
962             },
963             label: "",
964         },
965     ],
966     r#"
967 error: foo
968  --> test.rs:3:3
969   |
970 3 |   a { b { c } d }
971   |   ^^^^^^^^^^^^^
972
973 "#);
974 }
975
976 #[test]
977 fn long_snippet() {
978     test_harness(r#"
979 fn foo() {
980   X0 Y0 Z0
981   X1 Y1 Z1
982 1
983 2
984 3
985 4
986 5
987 6
988 7
989 8
990 9
991 10
992   X2 Y2 Z2
993   X3 Y3 Z3
994 }
995 "#,
996     vec![
997         SpanLabel {
998             start: Position {
999                 string: "Y0",
1000                 count: 1,
1001             },
1002             end: Position {
1003                 string: "X1",
1004                 count: 1,
1005             },
1006             label: "`X` is a good letter",
1007         },
1008         SpanLabel {
1009             start: Position {
1010                 string: "Z1",
1011                 count: 1,
1012             },
1013             end: Position {
1014                 string: "Z3",
1015                 count: 1,
1016             },
1017             label: "`Y` is a good letter too",
1018         },
1019     ],
1020     r#"
1021 error: foo
1022   --> test.rs:3:6
1023    |
1024 3  |      X0 Y0 Z0
1025    |   ______^
1026 4  |  |   X1 Y1 Z1
1027    |  |____^____-
1028    | ||____|
1029    | |     `X` is a good letter
1030 5  | |  1
1031 6  | |  2
1032 7  | |  3
1033 ...  |
1034 15 | |    X2 Y2 Z2
1035 16 | |    X3 Y3 Z3
1036    | |___________- `Y` is a good letter too
1037
1038 "#);
1039 }
1040
1041 #[test]
1042 fn long_snippet_multiple_spans() {
1043     test_harness(r#"
1044 fn foo() {
1045   X0 Y0 Z0
1046 1
1047 2
1048 3
1049   X1 Y1 Z1
1050 4
1051 5
1052 6
1053   X2 Y2 Z2
1054 7
1055 8
1056 9
1057 10
1058   X3 Y3 Z3
1059 }
1060 "#,
1061     vec![
1062         SpanLabel {
1063             start: Position {
1064                 string: "Y0",
1065                 count: 1,
1066             },
1067             end: Position {
1068                 string: "Y3",
1069                 count: 1,
1070             },
1071             label: "`Y` is a good letter",
1072         },
1073         SpanLabel {
1074             start: Position {
1075                 string: "Z1",
1076                 count: 1,
1077             },
1078             end: Position {
1079                 string: "Z2",
1080                 count: 1,
1081             },
1082             label: "`Z` is a good letter too",
1083         },
1084     ],
1085     r#"
1086 error: foo
1087   --> test.rs:3:6
1088    |
1089 3  |      X0 Y0 Z0
1090    |   ______^
1091 4  |  | 1
1092 5  |  | 2
1093 6  |  | 3
1094 7  |  |   X1 Y1 Z1
1095    |  |_________-
1096 8  | || 4
1097 9  | || 5
1098 10 | || 6
1099 11 | ||   X2 Y2 Z2
1100    | ||__________- `Z` is a good letter too
1101 ...   |
1102 15 |  | 10
1103 16 |  |   X3 Y3 Z3
1104    |  |_______^ `Y` is a good letter
1105
1106 "#);
1107 }
1108