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