| use codespan_reporting::diagnostic::{Diagnostic, Label}; |
| use codespan_reporting::files::{SimpleFile, SimpleFiles}; |
| use codespan_reporting::term::{termcolor::Color, Chars, Config, DisplayStyle, Styles}; |
| |
| mod support; |
| |
| use self::support::TestData; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_CONFIG: Config = Config { |
| // Always use blue so tests are consistent across platforms |
| styles: Styles::with_blue(Color::Blue), |
| ..Config::default() |
| }; |
| } |
| |
| macro_rules! test_emit { |
| (rich_color) => { |
| #[test] |
| fn rich_color() { |
| let config = Config { |
| display_style: DisplayStyle::Rich, |
| ..TEST_CONFIG.clone() |
| }; |
| |
| insta::assert_snapshot!(TEST_DATA.emit_color(&config)); |
| } |
| }; |
| (medium_color) => { |
| #[test] |
| fn medium_color() { |
| let config = Config { |
| display_style: DisplayStyle::Medium, |
| ..TEST_CONFIG.clone() |
| }; |
| |
| insta::assert_snapshot!(TEST_DATA.emit_color(&config)); |
| } |
| }; |
| (short_color) => { |
| #[test] |
| fn short_color() { |
| let config = Config { |
| display_style: DisplayStyle::Short, |
| ..TEST_CONFIG.clone() |
| }; |
| |
| insta::assert_snapshot!(TEST_DATA.emit_color(&config)); |
| } |
| }; |
| (rich_no_color) => { |
| #[test] |
| fn rich_no_color() { |
| let config = Config { |
| display_style: DisplayStyle::Rich, |
| ..TEST_CONFIG.clone() |
| }; |
| |
| insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); |
| } |
| }; |
| (medium_no_color) => { |
| #[test] |
| fn medium_no_color() { |
| let config = Config { |
| display_style: DisplayStyle::Medium, |
| ..TEST_CONFIG.clone() |
| }; |
| |
| insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); |
| } |
| }; |
| (short_no_color) => { |
| #[test] |
| fn short_no_color() { |
| let config = Config { |
| display_style: DisplayStyle::Short, |
| ..TEST_CONFIG.clone() |
| }; |
| |
| insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); |
| } |
| }; |
| (rich_ascii_no_color) => { |
| #[test] |
| fn rich_ascii_no_color() { |
| let config = Config { |
| display_style: DisplayStyle::Rich, |
| chars: Chars::ascii(), |
| ..TEST_CONFIG.clone() |
| }; |
| |
| insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); |
| } |
| }; |
| } |
| |
| mod empty { |
| use super::*; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = { |
| let files = SimpleFiles::new(); |
| |
| let diagnostics = vec![ |
| Diagnostic::bug(), |
| Diagnostic::error(), |
| Diagnostic::warning(), |
| Diagnostic::note(), |
| Diagnostic::help(), |
| Diagnostic::bug(), |
| ]; |
| |
| TestData { files, diagnostics } |
| }; |
| } |
| |
| test_emit!(rich_color); |
| test_emit!(medium_color); |
| test_emit!(short_color); |
| test_emit!(rich_no_color); |
| test_emit!(medium_no_color); |
| test_emit!(short_no_color); |
| test_emit!(rich_ascii_no_color); |
| } |
| |
| /// Based on: |
| /// - https://github.com/rust-lang/rust/blob/c20d7eecbc0928b57da8fe30b2ef8528e2bdd5be/src/test/ui/codemap_tests/one_line.stderr |
| mod same_line { |
| use super::*; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = { |
| let mut files = SimpleFiles::new(); |
| |
| let file_id1 = files.add( |
| "one_line.rs", |
| unindent::unindent(r#" |
| fn main() { |
| let mut v = vec![Some("foo"), Some("bar")]; |
| v.push(v.pop().unwrap()); |
| } |
| "#), |
| ); |
| |
| let diagnostics = vec![ |
| Diagnostic::error() |
| .with_code("E0499") |
| .with_message("cannot borrow `v` as mutable more than once at a time") |
| .with_labels(vec![ |
| Label::primary(file_id1, 71..72) |
| .with_message("second mutable borrow occurs here"), |
| Label::secondary(file_id1, 64..65) |
| .with_message("first borrow later used by call"), |
| Label::secondary(file_id1, 66..70) |
| .with_message("first mutable borrow occurs here"), |
| ]), |
| Diagnostic::error() |
| .with_message("aborting due to previous error") |
| .with_notes(vec![ |
| "For more information about this error, try `rustc --explain E0499`.".to_owned(), |
| ]), |
| ]; |
| |
| TestData { files, diagnostics } |
| }; |
| } |
| |
| test_emit!(rich_color); |
| test_emit!(medium_color); |
| test_emit!(short_color); |
| test_emit!(rich_no_color); |
| test_emit!(medium_no_color); |
| test_emit!(short_no_color); |
| test_emit!(rich_ascii_no_color); |
| } |
| |
| /// Based on: |
| /// - https://github.com/rust-lang/rust/blob/c20d7eecbc0928b57da8fe30b2ef8528e2bdd5be/src/test/ui/nested_impl_trait.stderr |
| /// - https://github.com/rust-lang/rust/blob/c20d7eecbc0928b57da8fe30b2ef8528e2bdd5be/src/test/ui/typeck/typeck_type_placeholder_item.stderr |
| /// - https://github.com/rust-lang/rust/blob/c20d7eecbc0928b57da8fe30b2ef8528e2bdd5be/src/test/ui/no_send_res_ports.stderr |
| mod overlapping { |
| use super::*; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = { |
| let mut files = SimpleFiles::new(); |
| |
| let file_id1 = files.add( |
| "nested_impl_trait.rs", |
| unindent::unindent(r#" |
| use std::fmt::Debug; |
| |
| fn fine(x: impl Into<u32>) -> impl Into<u32> { x } |
| |
| fn bad_in_ret_position(x: impl Into<u32>) -> impl Into<impl Debug> { x } |
| "#), |
| ); |
| let file_id2 = files.add( |
| "typeck_type_placeholder_item.rs", |
| unindent::unindent(r#" |
| fn fn_test1() -> _ { 5 } |
| fn fn_test2(x: i32) -> (_, _) { (x, x) } |
| "#), |
| ); |
| let file_id3 = files.add( |
| "libstd/thread/mod.rs", |
| unindent::unindent(r#" |
| #[stable(feature = "rust1", since = "1.0.0")] |
| pub fn spawn<F, T>(self, f: F) -> io::Result<JoinHandle<T>> |
| where |
| F: FnOnce() -> T, |
| F: Send + 'static, |
| T: Send + 'static, |
| { |
| unsafe { self.spawn_unchecked(f) } |
| } |
| "#), |
| ); |
| let file_id4 = files.add( |
| "no_send_res_ports.rs", |
| unindent::unindent(r#" |
| use std::thread; |
| use std::rc::Rc; |
| |
| #[derive(Debug)] |
| struct Port<T>(Rc<T>); |
| |
| fn main() { |
| #[derive(Debug)] |
| struct Foo { |
| _x: Port<()>, |
| } |
| |
| impl Drop for Foo { |
| fn drop(&mut self) {} |
| } |
| |
| fn foo(x: Port<()>) -> Foo { |
| Foo { |
| _x: x |
| } |
| } |
| |
| let x = foo(Port(Rc::new(()))); |
| |
| thread::spawn(move|| { |
| let y = x; |
| println!("{:?}", y); |
| }); |
| } |
| "#), |
| ); |
| |
| let diagnostics = vec![ |
| Diagnostic::error() |
| .with_code("E0666") |
| .with_message("nested `impl Trait` is not allowed") |
| .with_labels(vec![ |
| Label::primary(file_id1, 129..139) |
| .with_message("nested `impl Trait` here"), |
| Label::secondary(file_id1, 119..140) |
| .with_message("outer `impl Trait`"), |
| ]), |
| Diagnostic::error() |
| .with_code("E0121") |
| .with_message("the type placeholder `_` is not allowed within types on item signatures") |
| .with_labels(vec![ |
| Label::primary(file_id2, 17..18) |
| .with_message("not allowed in type signatures"), |
| Label::secondary(file_id2, 17..18) |
| .with_message("help: replace with the correct return type: `i32`"), |
| ]), |
| Diagnostic::error() |
| .with_code("E0121") |
| .with_message("the type placeholder `_` is not allowed within types on item signatures") |
| .with_labels(vec![ |
| Label::primary(file_id2, 49..50) |
| .with_message("not allowed in type signatures"), |
| Label::primary(file_id2, 52..53) |
| .with_message("not allowed in type signatures"), |
| Label::secondary(file_id2, 48..54) |
| .with_message("help: replace with the correct return type: `(i32, i32)`"), |
| ]), |
| Diagnostic::error() |
| .with_code("E0277") |
| .with_message("`std::rc::Rc<()>` cannot be sent between threads safely") |
| .with_labels(vec![ |
| Label::primary(file_id4, 339..352) |
| .with_message("`std::rc::Rc<()>` cannot be sent between threads safely"), |
| Label::secondary(file_id4, 353..416) |
| .with_message("within this `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`"), |
| Label::secondary(file_id3, 141..145) |
| .with_message("required by this bound in `std::thread::spawn`"), |
| ]) |
| .with_notes(vec![ |
| "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(), |
| "note: required because it appears within the type `Port<()>`".to_owned(), |
| "note: required because it appears within the type `main::Foo`".to_owned(), |
| "note: required because it appears within the type `[closure@no_send_res_ports.rs:29:19: 33:6 x:main::Foo]`".to_owned(), |
| ]), |
| Diagnostic::error() |
| .with_message("aborting due 5 previous errors") |
| .with_notes(vec![ |
| "Some errors have detailed explanations: E0121, E0277, E0666.".to_owned(), |
| "For more information about an error, try `rustc --explain E0121`.".to_owned(), |
| ]), |
| ]; |
| |
| TestData { files, diagnostics } |
| }; |
| } |
| |
| test_emit!(rich_color); |
| test_emit!(medium_color); |
| test_emit!(short_color); |
| test_emit!(rich_no_color); |
| test_emit!(medium_no_color); |
| test_emit!(short_no_color); |
| test_emit!(rich_ascii_no_color); |
| } |
| |
| mod message { |
| use super::*; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = { |
| let files = SimpleFiles::new(); |
| |
| let diagnostics = vec![ |
| Diagnostic::error().with_message("a message"), |
| Diagnostic::warning().with_message("a message"), |
| Diagnostic::note().with_message("a message"), |
| Diagnostic::help().with_message("a message"), |
| ]; |
| |
| TestData { files, diagnostics } |
| }; |
| } |
| |
| test_emit!(rich_color); |
| test_emit!(medium_color); |
| test_emit!(short_color); |
| test_emit!(rich_no_color); |
| test_emit!(medium_no_color); |
| test_emit!(short_no_color); |
| test_emit!(rich_ascii_no_color); |
| } |
| |
| mod message_and_notes { |
| use super::*; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = { |
| let files = SimpleFiles::new(); |
| |
| let diagnostics = vec![ |
| Diagnostic::error().with_message("a message").with_notes(vec!["a note".to_owned()]), |
| Diagnostic::warning().with_message("a message").with_notes(vec!["a note".to_owned()]), |
| Diagnostic::note().with_message("a message").with_notes(vec!["a note".to_owned()]), |
| Diagnostic::help().with_message("a message").with_notes(vec!["a note".to_owned()]), |
| ]; |
| |
| TestData { files, diagnostics } |
| }; |
| } |
| |
| test_emit!(rich_color); |
| test_emit!(medium_color); |
| test_emit!(short_color); |
| test_emit!(rich_no_color); |
| test_emit!(medium_no_color); |
| test_emit!(short_no_color); |
| test_emit!(rich_ascii_no_color); |
| } |
| |
| mod message_errorcode { |
| use super::*; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, &'static str>> = { |
| let files = SimpleFiles::new(); |
| |
| let diagnostics = vec![ |
| Diagnostic::error().with_message("a message").with_code("E0001"), |
| Diagnostic::warning().with_message("a message").with_code("W001"), |
| Diagnostic::note().with_message("a message").with_code("N0815"), |
| Diagnostic::help().with_message("a message").with_code("H4711"), |
| Diagnostic::error().with_message("where did my errorcode go?").with_code(""), |
| Diagnostic::warning().with_message("where did my errorcode go?").with_code(""), |
| Diagnostic::note().with_message("where did my errorcode go?").with_code(""), |
| Diagnostic::help().with_message("where did my errorcode go?").with_code(""), |
| ]; |
| |
| TestData { files, diagnostics } |
| }; |
| } |
| |
| test_emit!(rich_no_color); |
| test_emit!(short_no_color); |
| test_emit!(rich_ascii_no_color); |
| } |
| |
| mod empty_ranges { |
| use super::*; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, &'static str>> = { |
| let file = SimpleFile::new("hello", "Hello world!\nBye world!\n "); |
| let eof = file.source().len(); |
| |
| let diagnostics = vec![ |
| Diagnostic::note() |
| .with_message("middle") |
| .with_labels(vec![Label::primary((), 6..6).with_message("middle")]), |
| Diagnostic::note() |
| .with_message("end of line") |
| .with_labels(vec![Label::primary((), 12..12).with_message("end of line")]), |
| Diagnostic::note() |
| .with_message("end of line") |
| .with_labels(vec![Label::primary((), 23..23).with_message("end of line")]), |
| Diagnostic::note() |
| .with_message("end of file") |
| .with_labels(vec![Label::primary((), eof..eof).with_message("end of file")]), |
| ]; |
| |
| TestData { files: file, diagnostics } |
| }; |
| } |
| |
| test_emit!(rich_color); |
| test_emit!(medium_color); |
| test_emit!(short_color); |
| test_emit!(rich_no_color); |
| test_emit!(medium_no_color); |
| test_emit!(short_no_color); |
| test_emit!(rich_ascii_no_color); |
| } |
| |
| mod same_ranges { |
| use super::*; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, &'static str>> = { |
| let file = SimpleFile::new("same_range", "::S { }"); |
| |
| let diagnostics = vec![ |
| Diagnostic::error() |
| .with_message("Unexpected token") |
| .with_labels(vec![ |
| Label::primary((), 4..4).with_message("Unexpected '{'"), |
| Label::secondary((), 4..4).with_message("Expected '('"), |
| ]), |
| ]; |
| |
| TestData { files: file, diagnostics } |
| }; |
| } |
| |
| test_emit!(rich_color); |
| test_emit!(medium_color); |
| test_emit!(short_color); |
| test_emit!(rich_no_color); |
| test_emit!(medium_no_color); |
| test_emit!(short_no_color); |
| test_emit!(rich_ascii_no_color); |
| } |
| |
| mod multifile { |
| use super::*; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = { |
| let mut files = SimpleFiles::new(); |
| |
| let file_id1 = files.add( |
| "Data/Nat.fun", |
| unindent::unindent( |
| " |
| module Data.Nat where |
| |
| data Nat : Type where |
| zero : Nat |
| succ : Nat → Nat |
| |
| {-# BUILTIN NATRAL Nat #-} |
| |
| infixl 6 _+_ _-_ |
| |
| _+_ : Nat → Nat → Nat |
| zero + n₂ = n₂ |
| succ n₁ + n₂ = succ (n₁ + n₂) |
| |
| _-_ : Nat → Nat → Nat |
| n₁ - zero = n₁ |
| zero - succ n₂ = zero |
| succ n₁ - succ n₂ = n₁ - n₂ |
| ", |
| ), |
| ); |
| |
| let file_id2 = files.add( |
| "Test.fun", |
| unindent::unindent( |
| r#" |
| module Test where |
| |
| _ : Nat |
| _ = 123 + "hello" |
| "#, |
| ), |
| ); |
| |
| let diagnostics = vec![ |
| // Unknown builtin error |
| Diagnostic::error() |
| .with_message("unknown builtin: `NATRAL`") |
| .with_labels(vec![Label::primary(file_id1, 96..102).with_message("unknown builtin")]) |
| .with_notes(vec![ |
| "there is a builtin with a similar name: `NATURAL`".to_owned(), |
| ]), |
| // Unused parameter warning |
| Diagnostic::warning() |
| .with_message("unused parameter pattern: `n₂`") |
| .with_labels(vec![Label::primary(file_id1, 285..289).with_message("unused parameter")]) |
| .with_notes(vec!["consider using a wildcard pattern: `_`".to_owned()]), |
| // Unexpected type error |
| Diagnostic::error() |
| .with_message("unexpected type in application of `_+_`") |
| .with_code("E0001") |
| .with_labels(vec![ |
| Label::primary(file_id2, 37..44).with_message("expected `Nat`, found `String`"), |
| Label::secondary(file_id1, 130..155).with_message("based on the definition of `_+_`"), |
| ]) |
| .with_notes(vec![unindent::unindent( |
| " |
| expected type `Nat` |
| found type `String` |
| ", |
| )]), |
| ]; |
| |
| TestData { files, diagnostics } |
| }; |
| } |
| |
| test_emit!(rich_color); |
| test_emit!(medium_color); |
| test_emit!(short_color); |
| test_emit!(rich_no_color); |
| test_emit!(medium_no_color); |
| test_emit!(short_no_color); |
| test_emit!(rich_ascii_no_color); |
| } |
| |
| mod fizz_buzz { |
| use super::*; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = { |
| let mut files = SimpleFiles::new(); |
| |
| let file_id = files.add( |
| "FizzBuzz.fun", |
| unindent::unindent( |
| r#" |
| module FizzBuzz where |
| |
| fizz₁ : Nat → String |
| fizz₁ num = case (mod num 5) (mod num 3) of |
| 0 0 => "FizzBuzz" |
| 0 _ => "Fizz" |
| _ 0 => "Buzz" |
| _ _ => num |
| |
| fizz₂ : Nat → String |
| fizz₂ num = |
| case (mod num 5) (mod num 3) of |
| 0 0 => "FizzBuzz" |
| 0 _ => "Fizz" |
| _ 0 => "Buzz" |
| _ _ => num |
| "#, |
| ), |
| ); |
| |
| let diagnostics = vec![ |
| // Incompatible match clause error |
| Diagnostic::error() |
| .with_message("`case` clauses have incompatible types") |
| .with_code("E0308") |
| .with_labels(vec![ |
| Label::primary(file_id, 163..166).with_message("expected `String`, found `Nat`"), |
| Label::secondary(file_id, 62..166).with_message("`case` clauses have incompatible types"), |
| Label::secondary(file_id, 41..47).with_message("expected type `String` found here"), |
| ]) |
| .with_notes(vec![unindent::unindent( |
| " |
| expected type `String` |
| found type `Nat` |
| ", |
| )]), |
| // Incompatible match clause error |
| Diagnostic::error() |
| .with_message("`case` clauses have incompatible types") |
| .with_code("E0308") |
| .with_labels(vec![ |
| Label::primary(file_id, 328..331).with_message("expected `String`, found `Nat`"), |
| Label::secondary(file_id, 211..331).with_message("`case` clauses have incompatible types"), |
| Label::secondary(file_id, 258..268).with_message("this is found to be of type `String`"), |
| Label::secondary(file_id, 284..290).with_message("this is found to be of type `String`"), |
| Label::secondary(file_id, 306..312).with_message("this is found to be of type `String`"), |
| Label::secondary(file_id, 186..192).with_message("expected type `String` found here"), |
| ]) |
| .with_notes(vec![unindent::unindent( |
| " |
| expected type `String` |
| found type `Nat` |
| ", |
| )]), |
| ]; |
| |
| TestData { files, diagnostics } |
| }; |
| } |
| |
| test_emit!(rich_color); |
| test_emit!(medium_color); |
| test_emit!(short_color); |
| test_emit!(rich_no_color); |
| test_emit!(medium_no_color); |
| test_emit!(short_no_color); |
| test_emit!(rich_ascii_no_color); |
| } |
| |
| mod multiline_overlapping { |
| use super::*; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, String>> = { |
| let file = SimpleFile::new( |
| "codespan/src/file.rs", |
| [ |
| " match line_index.compare(self.last_line_index()) {", |
| " Ordering::Less => Ok(self.line_starts()[line_index.to_usize()]),", |
| " Ordering::Equal => Ok(self.source_span().end()),", |
| " Ordering::Greater => LineIndexOutOfBoundsError {", |
| " given: line_index,", |
| " max: self.last_line_index(),", |
| " },", |
| " }", |
| ].join("\n"), |
| ); |
| |
| let diagnostics = vec![ |
| Diagnostic::error() |
| .with_message("match arms have incompatible types") |
| .with_code("E0308") |
| .with_labels(vec![ |
| // this secondary label is before the primary label to test the locus calculation (see issue #259) |
| Label::secondary((), 89..134).with_message("this is found to be of type `Result<ByteIndex, LineIndexOutOfBoundsError>`"), |
| Label::primary((), 230..351).with_message("expected enum `Result`, found struct `LineIndexOutOfBoundsError`"), |
| Label::secondary((), 8..362).with_message("`match` arms have incompatible types"), |
| Label::secondary((), 167..195).with_message("this is found to be of type `Result<ByteIndex, LineIndexOutOfBoundsError>`"), |
| ]) |
| .with_notes(vec![unindent::unindent( |
| " |
| expected type `Result<ByteIndex, LineIndexOutOfBoundsError>` |
| found type `LineIndexOutOfBoundsError` |
| ", |
| )]), |
| ]; |
| |
| TestData { files: file, diagnostics } |
| }; |
| } |
| |
| test_emit!(rich_color); |
| test_emit!(medium_color); |
| test_emit!(short_color); |
| test_emit!(rich_no_color); |
| test_emit!(medium_no_color); |
| test_emit!(short_no_color); |
| test_emit!(rich_ascii_no_color); |
| } |
| |
| mod tabbed { |
| use super::*; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = { |
| let mut files = SimpleFiles::new(); |
| |
| let file_id = files.add( |
| "tabbed", |
| [ |
| "Entity:", |
| "\tArmament:", |
| "\t\tWeapon: DogJaw", |
| "\t\tReloadingCondition:\tattack-cooldown", |
| "\tFoo: Bar", |
| ] |
| .join("\n"), |
| ); |
| |
| let diagnostics = vec![ |
| Diagnostic::warning() |
| .with_message("unknown weapon `DogJaw`") |
| .with_labels(vec![Label::primary(file_id, 29..35).with_message("the weapon")]), |
| Diagnostic::warning() |
| .with_message("unknown condition `attack-cooldown`") |
| .with_labels(vec![Label::primary(file_id, 58..73).with_message("the condition")]), |
| Diagnostic::warning() |
| .with_message("unknown field `Foo`") |
| .with_labels(vec![Label::primary(file_id, 75..78).with_message("the field")]), |
| ]; |
| |
| TestData { files, diagnostics } |
| }; |
| } |
| |
| #[test] |
| fn tab_width_default_no_color() { |
| let config = TEST_CONFIG.clone(); |
| |
| insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); |
| } |
| |
| #[test] |
| fn tab_width_3_no_color() { |
| let config = Config { |
| tab_width: 3, |
| ..TEST_CONFIG.clone() |
| }; |
| |
| insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); |
| } |
| |
| #[test] |
| fn tab_width_6_no_color() { |
| let config = Config { |
| tab_width: 6, |
| ..TEST_CONFIG.clone() |
| }; |
| |
| insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); |
| } |
| } |
| |
| mod tab_columns { |
| use super::*; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = { |
| let mut files = SimpleFiles::new(); |
| |
| let source = [ |
| "\thello", |
| "∙\thello", |
| "∙∙\thello", |
| "∙∙∙\thello", |
| "∙∙∙∙\thello", |
| "∙∙∙∙∙\thello", |
| "∙∙∙∙∙∙\thello", |
| ].join("\n"); |
| let hello_ranges = source |
| .match_indices("hello") |
| .map(|(start, hello)| start..(start+hello.len())) |
| .collect::<Vec<_>>(); |
| |
| let file_id = files.add("tab_columns", source); |
| |
| let diagnostics = vec![ |
| Diagnostic::warning() |
| .with_message("tab test") |
| .with_labels( |
| hello_ranges |
| .into_iter() |
| .map(|range| Label::primary(file_id, range)) |
| .collect(), |
| ), |
| ]; |
| |
| TestData { files, diagnostics } |
| }; |
| } |
| |
| #[test] |
| fn tab_width_default_no_color() { |
| let config = TEST_CONFIG.clone(); |
| |
| insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); |
| } |
| |
| #[test] |
| fn tab_width_2_no_color() { |
| let config = Config { |
| tab_width: 2, |
| ..TEST_CONFIG.clone() |
| }; |
| |
| insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); |
| } |
| |
| #[test] |
| fn tab_width_3_no_color() { |
| let config = Config { |
| tab_width: 3, |
| ..TEST_CONFIG.clone() |
| }; |
| |
| insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); |
| } |
| |
| #[test] |
| fn tab_width_6_no_color() { |
| let config = Config { |
| tab_width: 6, |
| ..TEST_CONFIG.clone() |
| }; |
| |
| insta::assert_snapshot!(TEST_DATA.emit_no_color(&config)); |
| } |
| } |
| |
| /// Based on: |
| /// - https://github.com/TheSamsa/rust/blob/75cf41afb468152611212271bae026948cd3ba46/src/test/ui/codemap_tests/unicode.stderr |
| mod unicode { |
| use super::*; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, String>> = { |
| let prefix = r#"extern "#; |
| let abi = r#""路濫狼á́́""#; |
| let suffix = r#" fn foo() {}"#; |
| |
| let file = SimpleFile::new( |
| "unicode.rs", |
| format!("{}{}{}", prefix, abi, suffix), |
| ); |
| |
| let diagnostics = vec![ |
| Diagnostic::error() |
| .with_code("E0703") |
| .with_message("invalid ABI: found `路濫狼á́́`") |
| .with_labels(vec![ |
| Label::primary((), prefix.len()..(prefix.len() + abi.len())) |
| .with_message("invalid ABI"), |
| ]) |
| .with_notes(vec![unindent::unindent( |
| " |
| valid ABIs: |
| - aapcs |
| - amdgpu-kernel |
| - C |
| - cdecl |
| - efiapi |
| - fastcall |
| - msp430-interrupt |
| - platform-intrinsic |
| - ptx-kernel |
| - Rust |
| - rust-call |
| - rust-intrinsic |
| - stdcall |
| - system |
| - sysv64 |
| - thiscall |
| - unadjusted |
| - vectorcall |
| - win64 |
| - x86-interrupt |
| ", |
| )]), |
| Diagnostic::error() |
| .with_message("aborting due to previous error") |
| .with_notes(vec![ |
| "For more information about this error, try `rustc --explain E0703`.".to_owned(), |
| ]), |
| ]; |
| |
| TestData { files: file, diagnostics } |
| }; |
| } |
| |
| test_emit!(rich_no_color); |
| test_emit!(medium_no_color); |
| test_emit!(short_no_color); |
| } |
| |
| mod unicode_spans { |
| use super::*; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, String>> = { |
| let moon_phases = format!("{}", r#"🐄🌑🐄🌒🐄🌓🐄🌔🐄🌕🐄🌖🐄🌗🐄🌘🐄"#); |
| let invalid_start = 1; |
| let invalid_end = "🐄".len() - 1; |
| assert_eq!(moon_phases.is_char_boundary(invalid_start), false); |
| assert_eq!(moon_phases.is_char_boundary(invalid_end), false); |
| assert_eq!("🐄".len(), 4); |
| let file = SimpleFile::new( |
| "moon_jump.rs", |
| moon_phases, |
| ); |
| let diagnostics = vec![ |
| Diagnostic::error() |
| .with_code("E01") |
| .with_message("cow may not jump during new moon.") |
| .with_labels(vec![ |
| Label::primary((), invalid_start..invalid_end) |
| .with_message("Invalid jump"), |
| ]), |
| Diagnostic::note() |
| .with_message("invalid unicode range") |
| .with_labels(vec![ |
| Label::secondary((), invalid_start.."🐄".len()) |
| .with_message("Cow range does not start at boundary."), |
| ]), |
| Diagnostic::note() |
| .with_message("invalid unicode range") |
| .with_labels(vec![ |
| Label::secondary((), "🐄🌑".len().."🐄🌑🐄".len() - 1) |
| .with_message("Cow range does not end at boundary."), |
| ]), |
| Diagnostic::note() |
| .with_message("invalid unicode range") |
| .with_labels(vec![ |
| Label::secondary((), invalid_start.."🐄🌑🐄".len() - 1) |
| .with_message("Cow does not start or end at boundary."), |
| ]), |
| ]; |
| TestData{files: file, diagnostics } |
| }; |
| } |
| |
| test_emit!(rich_no_color); |
| test_emit!(medium_no_color); |
| test_emit!(short_no_color); |
| } |
| |
| mod position_indicator { |
| use super::*; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, String>> = { |
| let file = SimpleFile::new( |
| "tests/main.js", |
| [ |
| "\"use strict\";", |
| "let zero=0;", |
| "function foo() {", |
| " \"use strict\";", |
| " one=1;", |
| "}", |
| ].join("\n"), |
| ); |
| let diagnostics = vec![ |
| Diagnostic::warning() |
| .with_code("ParserWarning") |
| .with_message("The strict mode declaration in the body of function `foo` is redundant, as the outer scope is already in strict mode") |
| .with_labels(vec![ |
| Label::primary((), 45..57) |
| .with_message("This strict mode declaration is redundant"), |
| Label::secondary((), 0..12) |
| .with_message("Strict mode is first declared here"), |
| ]), |
| ]; |
| TestData{files: file, diagnostics } |
| }; |
| } |
| |
| test_emit!(rich_no_color); |
| test_emit!(medium_no_color); |
| test_emit!(short_no_color); |
| test_emit!(rich_ascii_no_color); |
| } |
| |
| mod multiline_omit { |
| use super::*; |
| |
| lazy_static::lazy_static! { |
| static ref TEST_CONFIG: Config = Config { |
| styles: Styles::with_blue(Color::Blue), |
| start_context_lines: 2, |
| end_context_lines: 1, |
| ..Config::default() |
| }; |
| |
| static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = { |
| let mut files = SimpleFiles::new(); |
| |
| let file_id1 = files.add( |
| "empty_if_comments.lua", |
| [ |
| "elseif 3 then", // primary label starts here |
| "", // context line |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", |
| "", // context line |
| "else", // primary label ends here |
| ] |
| .join("\n"), |
| ); |
| |
| let file_id2 = files.add( |
| "src/lib.rs", |
| [ |
| "fn main() {", |
| " 1", // primary label starts here |
| " + 1", // context line |
| " + 1", // skip |
| " + 1", // skip |
| " + 1", // skip |
| " +1", // secondary label here |
| " + 1", // this single line will not be skipped; the previously filtered out label must be retrieved |
| " + 1", // context line |
| " + 1", // primary label ends here |
| "}", |
| ] |
| .join("\n"), |
| ); |
| |
| let diagnostics = vec![ |
| Diagnostic::error() |
| .with_message("empty elseif block") |
| .with_code("empty_if") |
| .with_labels(vec![ |
| Label::primary(file_id1, 0..23), |
| Label::secondary(file_id1, 15..21).with_message("content should be in here"), |
| ]), |
| Diagnostic::error() |
| .with_message("mismatched types") |
| .with_code("E0308") |
| .with_labels(vec![ |
| Label::primary(file_id2, 17..80).with_message("expected (), found integer"), |
| Label::secondary(file_id2, 55..55).with_message("missing whitespace"), |
| ]) |
| .with_notes(vec![ |
| "note:\texpected type `()`\n\tfound type `{integer}`".to_owned() |
| ]), |
| ]; |
| |
| TestData { files, diagnostics } |
| }; |
| } |
| |
| test_emit!(rich_no_color); |
| } |