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