• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use codespan_reporting::diagnostic::{Diagnostic, Label};
2 use codespan_reporting::files::{SimpleFile, SimpleFiles};
3 use codespan_reporting::term::{termcolor::Color, Config, DisplayStyle, Styles};
4 
5 mod support;
6 
7 use self::support::TestData;
8 
9 lazy_static::lazy_static! {
10     static ref TEST_CONFIG: Config = Config {
11         // Always use blue so tests are consistent across platforms
12         styles: Styles::with_blue(Color::Blue),
13         ..Config::default()
14     };
15 }
16 
17 macro_rules! test_emit {
18     (rich_color) => {
19         #[test]
20         fn rich_color() {
21             let config = Config {
22                 display_style: DisplayStyle::Rich,
23                 ..TEST_CONFIG.clone()
24             };
25 
26             insta::assert_snapshot!(TEST_DATA.emit_color(&config));
27         }
28     };
29     (medium_color) => {
30         #[test]
31         fn medium_color() {
32             let config = Config {
33                 display_style: DisplayStyle::Medium,
34                 ..TEST_CONFIG.clone()
35             };
36 
37             insta::assert_snapshot!(TEST_DATA.emit_color(&config));
38         }
39     };
40     (short_color) => {
41         #[test]
42         fn short_color() {
43             let config = Config {
44                 display_style: DisplayStyle::Short,
45                 ..TEST_CONFIG.clone()
46             };
47 
48             insta::assert_snapshot!(TEST_DATA.emit_color(&config));
49         }
50     };
51     (rich_no_color) => {
52         #[test]
53         fn rich_no_color() {
54             let config = Config {
55                 display_style: DisplayStyle::Rich,
56                 ..TEST_CONFIG.clone()
57             };
58 
59             insta::assert_snapshot!(TEST_DATA.emit_no_color(&config));
60         }
61     };
62     (medium_no_color) => {
63         #[test]
64         fn medium_no_color() {
65             let config = Config {
66                 display_style: DisplayStyle::Medium,
67                 ..TEST_CONFIG.clone()
68             };
69 
70             insta::assert_snapshot!(TEST_DATA.emit_no_color(&config));
71         }
72     };
73     (short_no_color) => {
74         #[test]
75         fn short_no_color() {
76             let config = Config {
77                 display_style: DisplayStyle::Short,
78                 ..TEST_CONFIG.clone()
79             };
80 
81             insta::assert_snapshot!(TEST_DATA.emit_no_color(&config));
82         }
83     };
84 }
85 
86 mod empty {
87     use super::*;
88 
89     lazy_static::lazy_static! {
90         static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = {
91             let files = SimpleFiles::new();
92 
93             let diagnostics = vec![
94                 Diagnostic::bug(),
95                 Diagnostic::error(),
96                 Diagnostic::warning(),
97                 Diagnostic::note(),
98                 Diagnostic::help(),
99                 Diagnostic::bug(),
100             ];
101 
102             TestData { files, diagnostics }
103         };
104     }
105 
106     test_emit!(rich_color);
107     test_emit!(medium_color);
108     test_emit!(short_color);
109     test_emit!(rich_no_color);
110     test_emit!(medium_no_color);
111     test_emit!(short_no_color);
112 }
113 
114 /// Based on:
115 /// - https://github.com/rust-lang/rust/blob/c20d7eecbc0928b57da8fe30b2ef8528e2bdd5be/src/test/ui/codemap_tests/one_line.stderr
116 mod same_line {
117     use super::*;
118 
119     lazy_static::lazy_static! {
120         static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = {
121             let mut files = SimpleFiles::new();
122 
123             let file_id1 = files.add(
124                 "one_line.rs",
125                 unindent::unindent(r#"
126                     fn main() {
127                         let mut v = vec![Some("foo"), Some("bar")];
128                         v.push(v.pop().unwrap());
129                     }
130                 "#),
131             );
132 
133             let diagnostics = vec![
134                 Diagnostic::error()
135                     .with_code("E0499")
136                     .with_message("cannot borrow `v` as mutable more than once at a time")
137                     .with_labels(vec![
138                         Label::primary(file_id1, 71..72)
139                             .with_message("second mutable borrow occurs here"),
140                         Label::secondary(file_id1, 64..65)
141                             .with_message("first borrow later used by call"),
142                         Label::secondary(file_id1, 66..70)
143                             .with_message("first mutable borrow occurs here"),
144                     ]),
145                 Diagnostic::error()
146                     .with_message("aborting due to previous error")
147                     .with_notes(vec![
148                         "For more information about this error, try `rustc --explain E0499`.".to_owned(),
149                     ]),
150             ];
151 
152             TestData { files, diagnostics }
153         };
154     }
155 
156     test_emit!(rich_color);
157     test_emit!(medium_color);
158     test_emit!(short_color);
159     test_emit!(rich_no_color);
160     test_emit!(medium_no_color);
161     test_emit!(short_no_color);
162 }
163 
164 /// Based on:
165 /// - https://github.com/rust-lang/rust/blob/c20d7eecbc0928b57da8fe30b2ef8528e2bdd5be/src/test/ui/nested_impl_trait.stderr
166 /// - https://github.com/rust-lang/rust/blob/c20d7eecbc0928b57da8fe30b2ef8528e2bdd5be/src/test/ui/typeck/typeck_type_placeholder_item.stderr
167 /// - https://github.com/rust-lang/rust/blob/c20d7eecbc0928b57da8fe30b2ef8528e2bdd5be/src/test/ui/no_send_res_ports.stderr
168 mod overlapping {
169     use super::*;
170 
171     lazy_static::lazy_static! {
172         static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = {
173             let mut files = SimpleFiles::new();
174 
175             let file_id1 = files.add(
176                 "nested_impl_trait.rs",
177                 unindent::unindent(r#"
178                     use std::fmt::Debug;
179 
180                     fn fine(x: impl Into<u32>) -> impl Into<u32> { x }
181 
182                     fn bad_in_ret_position(x: impl Into<u32>) -> impl Into<impl Debug> { x }
183                 "#),
184             );
185             let file_id2 = files.add(
186                 "typeck_type_placeholder_item.rs",
187                 unindent::unindent(r#"
188                     fn fn_test1() -> _ { 5 }
189                     fn fn_test2(x: i32) -> (_, _) { (x, x) }
190                 "#),
191             );
192             let file_id3 = files.add(
193                 "libstd/thread/mod.rs",
194                 unindent::unindent(r#"
195                     #[stable(feature = "rust1", since = "1.0.0")]
196                     pub fn spawn<F, T>(self, f: F) -> io::Result<JoinHandle<T>>
197                     where
198                         F: FnOnce() -> T,
199                         F: Send + 'static,
200                         T: Send + 'static,
201                     {
202                         unsafe { self.spawn_unchecked(f) }
203                     }
204                 "#),
205             );
206             let file_id4 = files.add(
207                 "no_send_res_ports.rs",
208                 unindent::unindent(r#"
209                     use std::thread;
210                     use std::rc::Rc;
211 
212                     #[derive(Debug)]
213                     struct Port<T>(Rc<T>);
214 
215                     fn main() {
216                         #[derive(Debug)]
217                         struct Foo {
218                             _x: Port<()>,
219                         }
220 
221                         impl Drop for Foo {
222                             fn drop(&mut self) {}
223                         }
224 
225                         fn foo(x: Port<()>) -> Foo {
226                             Foo {
227                                 _x: x
228                             }
229                         }
230 
231                         let x = foo(Port(Rc::new(())));
232 
233                         thread::spawn(move|| {
234                             let y = x;
235                             println!("{:?}", y);
236                         });
237                     }
238                 "#),
239             );
240 
241             let diagnostics = vec![
242                 Diagnostic::error()
243                     .with_code("E0666")
244                     .with_message("nested `impl Trait` is not allowed")
245                     .with_labels(vec![
246                         Label::primary(file_id1, 129..139)
247                             .with_message("nested `impl Trait` here"),
248                         Label::secondary(file_id1, 119..140)
249                             .with_message("outer `impl Trait`"),
250                     ]),
251                 Diagnostic::error()
252                     .with_code("E0121")
253                     .with_message("the type placeholder `_` is not allowed within types on item signatures")
254                         .with_labels(vec![
255                             Label::primary(file_id2, 17..18)
256                                 .with_message("not allowed in type signatures"),
257                             Label::secondary(file_id2, 17..18)
258                                 .with_message("help: replace with the correct return type: `i32`"),
259                         ]),
260                 Diagnostic::error()
261                     .with_code("E0121")
262                     .with_message("the type placeholder `_` is not allowed within types on item signatures")
263                         .with_labels(vec![
264                             Label::primary(file_id2, 49..50)
265                                 .with_message("not allowed in type signatures"),
266                             Label::primary(file_id2, 52..53)
267                                 .with_message("not allowed in type signatures"),
268                             Label::secondary(file_id2, 48..54)
269                                 .with_message("help: replace with the correct return type: `(i32, i32)`"),
270                         ]),
271                 Diagnostic::error()
272                     .with_code("E0277")
273                     .with_message("`std::rc::Rc<()>` cannot be sent between threads safely")
274                     .with_labels(vec![
275                         Label::primary(file_id4, 339..352)
276                             .with_message("`std::rc::Rc<()>` cannot be sent between threads safely"),
277                         Label::secondary(file_id4, 353..416)
278                             .with_message("within this `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`"),
279                         Label::secondary(file_id3, 141..145)
280                             .with_message("required by this bound in `std::thread::spawn`"),
281                     ])
282                     .with_notes(vec![
283                         "help: within `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>`".to_owned(),
284                         "note: required because it appears within the type `Port<()>`".to_owned(),
285                         "note: required because it appears within the type `main::Foo`".to_owned(),
286                         "note: required because it appears within the type `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`".to_owned(),
287                     ]),
288                 Diagnostic::error()
289                     .with_message("aborting due 5 previous errors")
290                     .with_notes(vec![
291                         "Some errors have detailed explanations: E0121, E0277, E0666.".to_owned(),
292                         "For more information about an error, try `rustc --explain E0121`.".to_owned(),
293                     ]),
294             ];
295 
296             TestData { files, diagnostics }
297         };
298     }
299 
300     test_emit!(rich_color);
301     test_emit!(medium_color);
302     test_emit!(short_color);
303     test_emit!(rich_no_color);
304     test_emit!(medium_no_color);
305     test_emit!(short_no_color);
306 }
307 
308 mod message {
309     use super::*;
310 
311     lazy_static::lazy_static! {
312         static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = {
313             let files = SimpleFiles::new();
314 
315             let diagnostics = vec![
316                 Diagnostic::error().with_message("a message"),
317                 Diagnostic::warning().with_message("a message"),
318                 Diagnostic::note().with_message("a message"),
319                 Diagnostic::help().with_message("a message"),
320             ];
321 
322             TestData { files, diagnostics }
323         };
324     }
325 
326     test_emit!(rich_color);
327     test_emit!(medium_color);
328     test_emit!(short_color);
329     test_emit!(rich_no_color);
330     test_emit!(medium_no_color);
331     test_emit!(short_no_color);
332 }
333 
334 mod message_and_notes {
335     use super::*;
336 
337     lazy_static::lazy_static! {
338         static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = {
339             let files = SimpleFiles::new();
340 
341             let diagnostics = vec![
342                 Diagnostic::error().with_message("a message").with_notes(vec!["a note".to_owned()]),
343                 Diagnostic::warning().with_message("a message").with_notes(vec!["a note".to_owned()]),
344                 Diagnostic::note().with_message("a message").with_notes(vec!["a note".to_owned()]),
345                 Diagnostic::help().with_message("a message").with_notes(vec!["a note".to_owned()]),
346             ];
347 
348             TestData { files, diagnostics }
349         };
350     }
351 
352     test_emit!(rich_color);
353     test_emit!(medium_color);
354     test_emit!(short_color);
355     test_emit!(rich_no_color);
356     test_emit!(medium_no_color);
357     test_emit!(short_no_color);
358 }
359 
360 mod message_errorcode {
361     use super::*;
362 
363     lazy_static::lazy_static! {
364         static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = {
365             let files = SimpleFiles::new();
366 
367             let diagnostics = vec![
368                 Diagnostic::error().with_message("a message").with_code("E0001"),
369                 Diagnostic::warning().with_message("a message").with_code("W001"),
370                 Diagnostic::note().with_message("a message").with_code("N0815"),
371                 Diagnostic::help().with_message("a message").with_code("H4711"),
372                 Diagnostic::error().with_message("where did my errorcode go?").with_code(""),
373                 Diagnostic::warning().with_message("where did my errorcode go?").with_code(""),
374                 Diagnostic::note().with_message("where did my errorcode go?").with_code(""),
375                 Diagnostic::help().with_message("where did my errorcode go?").with_code(""),
376             ];
377 
378             TestData { files, diagnostics }
379         };
380     }
381 
382     test_emit!(rich_no_color);
383     test_emit!(short_no_color);
384 }
385 
386 mod empty_ranges {
387     use super::*;
388 
389     lazy_static::lazy_static! {
390         static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, &'static str>> = {
391             let file = SimpleFile::new("hello", "Hello world!\nBye world!\n   ");
392             let eof = file.source().len();
393 
394             let diagnostics = vec![
395                 Diagnostic::note()
396                     .with_message("middle")
397                     .with_labels(vec![Label::primary((), 6..6).with_message("middle")]),
398                 Diagnostic::note()
399                     .with_message("end of line")
400                     .with_labels(vec![Label::primary((), 12..12).with_message("end of line")]),
401                 Diagnostic::note()
402                     .with_message("end of line")
403                     .with_labels(vec![Label::primary((), 23..23).with_message("end of line")]),
404                 Diagnostic::note()
405                     .with_message("end of file")
406                     .with_labels(vec![Label::primary((), eof..eof).with_message("end of file")]),
407             ];
408 
409             TestData { files: file, diagnostics }
410         };
411     }
412 
413     test_emit!(rich_color);
414     test_emit!(medium_color);
415     test_emit!(short_color);
416     test_emit!(rich_no_color);
417     test_emit!(medium_no_color);
418     test_emit!(short_no_color);
419 }
420 
421 mod same_ranges {
422     use super::*;
423 
424     lazy_static::lazy_static! {
425         static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, &'static str>> = {
426             let file = SimpleFile::new("same_range", "::S { }");
427 
428             let diagnostics = vec![
429                 Diagnostic::error()
430                     .with_message("Unexpected token")
431                     .with_labels(vec![
432                         Label::primary((), 4..4).with_message("Unexpected '{'"),
433                         Label::secondary((), 4..4).with_message("Expected '('"),
434                     ]),
435             ];
436 
437             TestData { files: file, diagnostics }
438         };
439     }
440 
441     test_emit!(rich_color);
442     test_emit!(medium_color);
443     test_emit!(short_color);
444     test_emit!(rich_no_color);
445     test_emit!(medium_no_color);
446     test_emit!(short_no_color);
447 }
448 
449 mod multifile {
450     use super::*;
451 
452     lazy_static::lazy_static! {
453         static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = {
454             let mut files = SimpleFiles::new();
455 
456             let file_id1 = files.add(
457                 "Data/Nat.fun",
458                 unindent::unindent(
459                     "
460                         module Data.Nat where
461 
462                         data Nat : Type where
463                             zero : Nat
464                             succ : Nat → Nat
465 
466                         {-# BUILTIN NATRAL Nat #-}
467 
468                         infixl 6 _+_ _-_
469 
470                         _+_ : Nat → Nat → Nat
471                         zero    + n₂ = n₂
472                         succ n₁ + n₂ = succ (n₁ + n₂)
473 
474                         _-_ : Nat → Nat → Nat
475                         n₁      - zero    = n₁
476                         zero    - succ n₂ = zero
477                         succ n₁ - succ n₂ = n₁ - n₂
478                     ",
479                 ),
480             );
481 
482             let file_id2 = files.add(
483                 "Test.fun",
484                 unindent::unindent(
485                     r#"
486                         module Test where
487 
488                         _ : Nat
489                         _ = 123 + "hello"
490                     "#,
491                 ),
492             );
493 
494             let diagnostics = vec![
495                 // Unknown builtin error
496                 Diagnostic::error()
497                     .with_message("unknown builtin: `NATRAL`")
498                     .with_labels(vec![Label::primary(file_id1, 96..102).with_message("unknown builtin")])
499                     .with_notes(vec![
500                         "there is a builtin with a similar name: `NATURAL`".to_owned(),
501                     ]),
502                 // Unused parameter warning
503                 Diagnostic::warning()
504                     .with_message("unused parameter pattern: `n₂`")
505                     .with_labels(vec![Label::primary(file_id1, 285..289).with_message("unused parameter")])
506                     .with_notes(vec!["consider using a wildcard pattern: `_`".to_owned()]),
507                 // Unexpected type error
508                 Diagnostic::error()
509                     .with_message("unexpected type in application of `_+_`")
510                     .with_code("E0001")
511                     .with_labels(vec![
512                         Label::primary(file_id2, 37..44).with_message("expected `Nat`, found `String`"),
513                         Label::secondary(file_id1, 130..155).with_message("based on the definition of `_+_`"),
514                     ])
515                     .with_notes(vec![unindent::unindent(
516                         "
517                             expected type `Nat`
518                                found type `String`
519                         ",
520                     )]),
521             ];
522 
523             TestData { files, diagnostics }
524         };
525     }
526 
527     test_emit!(rich_color);
528     test_emit!(medium_color);
529     test_emit!(short_color);
530     test_emit!(rich_no_color);
531     test_emit!(medium_no_color);
532     test_emit!(short_no_color);
533 }
534 
535 mod fizz_buzz {
536     use super::*;
537 
538     lazy_static::lazy_static! {
539         static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = {
540             let mut files = SimpleFiles::new();
541 
542             let file_id = files.add(
543                 "FizzBuzz.fun",
544                 unindent::unindent(
545                     r#"
546                         module FizzBuzz where
547 
548                         fizz₁ : Nat → String
549                         fizz₁ num = case (mod num 5) (mod num 3) of
550                             0 0 => "FizzBuzz"
551                             0 _ => "Fizz"
552                             _ 0 => "Buzz"
553                             _ _ => num
554 
555                         fizz₂ : Nat → String
556                         fizz₂ num =
557                             case (mod num 5) (mod num 3) of
558                                 0 0 => "FizzBuzz"
559                                 0 _ => "Fizz"
560                                 _ 0 => "Buzz"
561                                 _ _ => num
562                     "#,
563                 ),
564             );
565 
566             let diagnostics = vec![
567                 // Incompatible match clause error
568                 Diagnostic::error()
569                     .with_message("`case` clauses have incompatible types")
570                     .with_code("E0308")
571                     .with_labels(vec![
572                         Label::primary(file_id, 163..166).with_message("expected `String`, found `Nat`"),
573                         Label::secondary(file_id, 62..166).with_message("`case` clauses have incompatible types"),
574                         Label::secondary(file_id, 41..47).with_message("expected type `String` found here"),
575                     ])
576                     .with_notes(vec![unindent::unindent(
577                         "
578                             expected type `String`
579                                found type `Nat`
580                         ",
581                     )]),
582                 // Incompatible match clause error
583                 Diagnostic::error()
584                     .with_message("`case` clauses have incompatible types")
585                     .with_code("E0308")
586                     .with_labels(vec![
587                         Label::primary(file_id, 328..331).with_message("expected `String`, found `Nat`"),
588                         Label::secondary(file_id, 211..331).with_message("`case` clauses have incompatible types"),
589                         Label::secondary(file_id, 258..268).with_message("this is found to be of type `String`"),
590                         Label::secondary(file_id, 284..290).with_message("this is found to be of type `String`"),
591                         Label::secondary(file_id, 306..312).with_message("this is found to be of type `String`"),
592                         Label::secondary(file_id, 186..192).with_message("expected type `String` found here"),
593                     ])
594                     .with_notes(vec![unindent::unindent(
595                         "
596                             expected type `String`
597                                found type `Nat`
598                         ",
599                     )]),
600             ];
601 
602             TestData { files, diagnostics }
603         };
604     }
605 
606     test_emit!(rich_color);
607     test_emit!(medium_color);
608     test_emit!(short_color);
609     test_emit!(rich_no_color);
610     test_emit!(medium_no_color);
611     test_emit!(short_no_color);
612 }
613 
614 mod multiline_overlapping {
615     use super::*;
616 
617     lazy_static::lazy_static! {
618         static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, String>> = {
619             let file = SimpleFile::new(
620                 "codespan/src/file.rs",
621                 [
622                     "        match line_index.compare(self.last_line_index()) {",
623                     "            Ordering::Less => Ok(self.line_starts()[line_index.to_usize()]),",
624                     "            Ordering::Equal => Ok(self.source_span().end()),",
625                     "            Ordering::Greater => LineIndexOutOfBoundsError {",
626                     "                given: line_index,",
627                     "                max: self.last_line_index(),",
628                     "            },",
629                     "        }",
630                 ].join("\n"),
631             );
632 
633             let diagnostics = vec![
634                 Diagnostic::error()
635                     .with_message("match arms have incompatible types")
636                     .with_code("E0308")
637                     .with_labels(vec![
638                         // this secondary label is before the primary label to test the locus calculation (see issue #259)
639                         Label::secondary((), 89..134).with_message("this is found to be of type `Result<ByteIndex, LineIndexOutOfBoundsError>`"),
640                         Label::primary((), 230..351).with_message("expected enum `Result`, found struct `LineIndexOutOfBoundsError`"),
641                         Label::secondary((), 8..362).with_message("`match` arms have incompatible types"),
642                         Label::secondary((), 167..195).with_message("this is found to be of type `Result<ByteIndex, LineIndexOutOfBoundsError>`"),
643                     ])
644                     .with_notes(vec![unindent::unindent(
645                         "
646                             expected type `Result<ByteIndex, LineIndexOutOfBoundsError>`
647                                found type `LineIndexOutOfBoundsError`
648                         ",
649                     )]),
650             ];
651 
652             TestData { files: file, diagnostics }
653         };
654     }
655 
656     test_emit!(rich_color);
657     test_emit!(medium_color);
658     test_emit!(short_color);
659     test_emit!(rich_no_color);
660     test_emit!(medium_no_color);
661     test_emit!(short_no_color);
662 }
663 
664 mod tabbed {
665     use super::*;
666 
667     lazy_static::lazy_static! {
668         static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = {
669             let mut files = SimpleFiles::new();
670 
671             let file_id = files.add(
672                 "tabbed",
673                 [
674                     "Entity:",
675                     "\tArmament:",
676                     "\t\tWeapon: DogJaw",
677                     "\t\tReloadingCondition:\tattack-cooldown",
678                     "\tFoo: Bar",
679                 ]
680                 .join("\n"),
681             );
682 
683             let diagnostics = vec![
684                 Diagnostic::warning()
685                     .with_message("unknown weapon `DogJaw`")
686                     .with_labels(vec![Label::primary(file_id, 29..35).with_message("the weapon")]),
687                 Diagnostic::warning()
688                     .with_message("unknown condition `attack-cooldown`")
689                     .with_labels(vec![Label::primary(file_id, 58..73).with_message("the condition")]),
690                 Diagnostic::warning()
691                     .with_message("unknown field `Foo`")
692                     .with_labels(vec![Label::primary(file_id, 75..78).with_message("the field")]),
693             ];
694 
695             TestData { files, diagnostics }
696         };
697     }
698 
699     #[test]
tab_width_default_no_color()700     fn tab_width_default_no_color() {
701         let config = TEST_CONFIG.clone();
702 
703         insta::assert_snapshot!(TEST_DATA.emit_no_color(&config));
704     }
705 
706     #[test]
tab_width_3_no_color()707     fn tab_width_3_no_color() {
708         let config = Config {
709             tab_width: 3,
710             ..TEST_CONFIG.clone()
711         };
712 
713         insta::assert_snapshot!(TEST_DATA.emit_no_color(&config));
714     }
715 
716     #[test]
tab_width_6_no_color()717     fn tab_width_6_no_color() {
718         let config = Config {
719             tab_width: 6,
720             ..TEST_CONFIG.clone()
721         };
722 
723         insta::assert_snapshot!(TEST_DATA.emit_no_color(&config));
724     }
725 }
726 
727 mod tab_columns {
728     use super::*;
729 
730     lazy_static::lazy_static! {
731         static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = {
732             let mut files = SimpleFiles::new();
733 
734             let source = [
735                 "\thello",
736                 "∙\thello",
737                 "∙∙\thello",
738                 "∙∙∙\thello",
739                 "∙∙∙∙\thello",
740                 "∙∙∙∙∙\thello",
741                 "∙∙∙∙∙∙\thello",
742             ].join("\n");
743             let hello_ranges = source
744                 .match_indices("hello")
745                 .map(|(start, hello)| start..(start+hello.len()))
746                 .collect::<Vec<_>>();
747 
748             let file_id = files.add("tab_columns", source);
749 
750             let diagnostics = vec![
751                 Diagnostic::warning()
752                     .with_message("tab test")
753                     .with_labels(
754                         hello_ranges
755                             .into_iter()
756                             .map(|range| Label::primary(file_id, range))
757                             .collect(),
758                     ),
759             ];
760 
761             TestData { files, diagnostics }
762         };
763     }
764 
765     #[test]
tab_width_default_no_color()766     fn tab_width_default_no_color() {
767         let config = TEST_CONFIG.clone();
768 
769         insta::assert_snapshot!(TEST_DATA.emit_no_color(&config));
770     }
771 
772     #[test]
tab_width_2_no_color()773     fn tab_width_2_no_color() {
774         let config = Config {
775             tab_width: 2,
776             ..TEST_CONFIG.clone()
777         };
778 
779         insta::assert_snapshot!(TEST_DATA.emit_no_color(&config));
780     }
781 
782     #[test]
tab_width_3_no_color()783     fn tab_width_3_no_color() {
784         let config = Config {
785             tab_width: 3,
786             ..TEST_CONFIG.clone()
787         };
788 
789         insta::assert_snapshot!(TEST_DATA.emit_no_color(&config));
790     }
791 
792     #[test]
tab_width_6_no_color()793     fn tab_width_6_no_color() {
794         let config = Config {
795             tab_width: 6,
796             ..TEST_CONFIG.clone()
797         };
798 
799         insta::assert_snapshot!(TEST_DATA.emit_no_color(&config));
800     }
801 }
802 
803 /// Based on:
804 /// - https://github.com/TheSamsa/rust/blob/75cf41afb468152611212271bae026948cd3ba46/src/test/ui/codemap_tests/unicode.stderr
805 mod unicode {
806     use super::*;
807 
808     lazy_static::lazy_static! {
809         static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, String>> = {
810             let prefix = r#"extern "#;
811             let abi = r#""路濫狼á́́""#;
812             let suffix = r#" fn foo() {}"#;
813 
814             let file = SimpleFile::new(
815                 "unicode.rs",
816                 format!("{}{}{}", prefix, abi, suffix),
817             );
818 
819             let diagnostics = vec![
820                 Diagnostic::error()
821                     .with_code("E0703")
822                     .with_message("invalid ABI: found `路濫狼á́́`")
823                     .with_labels(vec![
824                         Label::primary((), prefix.len()..(prefix.len() + abi.len()))
825                             .with_message("invalid ABI"),
826                     ])
827                     .with_notes(vec![unindent::unindent(
828                         "
829                             valid ABIs:
830                               - aapcs
831                               - amdgpu-kernel
832                               - C
833                               - cdecl
834                               - efiapi
835                               - fastcall
836                               - msp430-interrupt
837                               - platform-intrinsic
838                               - ptx-kernel
839                               - Rust
840                               - rust-call
841                               - rust-intrinsic
842                               - stdcall
843                               - system
844                               - sysv64
845                               - thiscall
846                               - unadjusted
847                               - vectorcall
848                               - win64
849                               - x86-interrupt
850                         ",
851                     )]),
852                 Diagnostic::error()
853                     .with_message("aborting due to previous error")
854                     .with_notes(vec![
855                         "For more information about this error, try `rustc --explain E0703`.".to_owned(),
856                     ]),
857             ];
858 
859             TestData { files: file, diagnostics }
860         };
861     }
862 
863     test_emit!(rich_no_color);
864     test_emit!(medium_no_color);
865     test_emit!(short_no_color);
866 }
867 
868 mod unicode_spans {
869     use super::*;
870 
871     lazy_static::lazy_static! {
872         static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, String>> = {
873             let moon_phases = format!("{}", r#"����������������������������������"#);
874             let invalid_start = 1;
875             let invalid_end = "��".len() - 1;
876             assert_eq!(moon_phases.is_char_boundary(invalid_start), false);
877             assert_eq!(moon_phases.is_char_boundary(invalid_end), false);
878             assert_eq!("��".len(), 4);
879             let file = SimpleFile::new(
880                 "moon_jump.rs",
881                 moon_phases,
882             );
883             let diagnostics = vec![
884                 Diagnostic::error()
885                     .with_code("E01")
886                     .with_message("cow may not jump during new moon.")
887                     .with_labels(vec![
888                         Label::primary((), invalid_start..invalid_end)
889                             .with_message("Invalid jump"),
890                     ]),
891                 Diagnostic::note()
892                     .with_message("invalid unicode range")
893                     .with_labels(vec![
894                         Label::secondary((), invalid_start.."��".len())
895                             .with_message("Cow range does not start at boundary."),
896                     ]),
897                 Diagnostic::note()
898                     .with_message("invalid unicode range")
899                     .with_labels(vec![
900                         Label::secondary((), "����".len().."������".len() - 1)
901                             .with_message("Cow range does not end at boundary."),
902                     ]),
903                 Diagnostic::note()
904                     .with_message("invalid unicode range")
905                     .with_labels(vec![
906                         Label::secondary((), invalid_start.."������".len() - 1)
907                             .with_message("Cow does not start or end at boundary."),
908                     ]),
909             ];
910             TestData{files: file, diagnostics }
911         };
912     }
913 
914     test_emit!(rich_no_color);
915     test_emit!(medium_no_color);
916     test_emit!(short_no_color);
917 }
918 
919 mod position_indicator {
920     use super::*;
921 
922     lazy_static::lazy_static! {
923         static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, String>> = {
924             let file = SimpleFile::new(
925                 "tests/main.js",
926                 [
927                     "\"use strict\";",
928                     "let zero=0;",
929                     "function foo() {",
930                     "  \"use strict\";",
931                     "  one=1;",
932                     "}",
933                 ].join("\n"),
934             );
935             let diagnostics = vec![
936                 Diagnostic::warning()
937                     .with_code("ParserWarning")
938                     .with_message("The strict mode declaration in the body of function `foo` is redundant, as the outer scope is already in strict mode")
939                     .with_labels(vec![
940                         Label::primary((), 45..57)
941                             .with_message("This strict mode declaration is redundant"),
942                         Label::secondary((), 0..12)
943                             .with_message("Strict mode is first declared here"),
944                     ]),
945             ];
946             TestData{files: file, diagnostics }
947         };
948     }
949 
950     test_emit!(rich_no_color);
951     test_emit!(medium_no_color);
952     test_emit!(short_no_color);
953 }
954 
955 mod multiline_omit {
956     use super::*;
957 
958     lazy_static::lazy_static! {
959         static ref TEST_CONFIG: Config = Config {
960             styles: Styles::with_blue(Color::Blue),
961             start_context_lines: 2,
962             end_context_lines: 1,
963             ..Config::default()
964         };
965 
966         static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = {
967             let mut files = SimpleFiles::new();
968 
969             let file_id1 = files.add(
970                 "empty_if_comments.lua",
971                 [
972                     "elseif 3 then", // primary label starts here
973                     "",              // context line
974                     "",
975                     "",
976                     "",
977                     "",
978                     "",
979                     "",
980                     "",     // context line
981                     "else", // primary label ends here
982                 ]
983                 .join("\n"),
984             );
985 
986             let file_id2 = files.add(
987                 "src/lib.rs",
988                 [
989                     "fn main() {",
990                     "    1",   // primary label starts here
991                     "    + 1", // context line
992                     "    + 1", // skip
993                     "    + 1", // skip
994                     "    + 1", // skip
995                     "    +1",  // secondary label here
996                     "    + 1", // this single line will not be skipped; the previously filtered out label must be retrieved
997                     "    + 1", // context line
998                     "    + 1", // primary label ends here
999                     "}",
1000                 ]
1001                 .join("\n"),
1002             );
1003 
1004             let diagnostics = vec![
1005                 Diagnostic::error()
1006                     .with_message("empty elseif block")
1007                     .with_code("empty_if")
1008                     .with_labels(vec![
1009                         Label::primary(file_id1, 0..23),
1010                         Label::secondary(file_id1, 15..21).with_message("content should be in here"),
1011                     ]),
1012                 Diagnostic::error()
1013                     .with_message("mismatched types")
1014                     .with_code("E0308")
1015                     .with_labels(vec![
1016                         Label::primary(file_id2, 17..80).with_message("expected (), found integer"),
1017                         Label::secondary(file_id2, 55..55).with_message("missing whitespace"),
1018                     ])
1019                     .with_notes(vec![
1020                         "note:\texpected type `()`\n\tfound type `{integer}`".to_owned()
1021                     ]),
1022             ];
1023 
1024             TestData { files, diagnostics }
1025         };
1026     }
1027 
1028     test_emit!(rich_no_color);
1029 }
1030