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