Cleanup diagnostics tests

This commit is contained in:
Aleksey Kladov 2020-07-09 14:33:03 +02:00
parent ea68a1d0c9
commit fb0bc941a5
2 changed files with 129 additions and 235 deletions

View file

@ -281,43 +281,11 @@ fn check_struct_shorthand_initialization(
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use insta::assert_debug_snapshot;
use ra_syntax::SourceFile;
use stdx::trim_indent; use stdx::trim_indent;
use test_utils::assert_eq_text; use test_utils::assert_eq_text;
use crate::mock_analysis::{analysis_and_position, single_file}; use crate::mock_analysis::{analysis_and_position, single_file, MockAnalysis};
use expect::{expect, Expect};
use super::*;
type DiagnosticChecker = fn(&mut Vec<Diagnostic>, FileId, &SyntaxNode) -> Option<()>;
fn check_not_applicable(code: &str, func: DiagnosticChecker) {
let parse = SourceFile::parse(code);
let mut diagnostics = Vec::new();
for node in parse.tree().syntax().descendants() {
func(&mut diagnostics, FileId(0), &node);
}
assert!(diagnostics.is_empty());
}
fn check_apply(before: &str, after: &str, func: DiagnosticChecker) {
let parse = SourceFile::parse(before);
let mut diagnostics = Vec::new();
for node in parse.tree().syntax().descendants() {
func(&mut diagnostics, FileId(0), &node);
}
let diagnostic =
diagnostics.pop().unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
let mut fix = diagnostic.fix.unwrap();
let edit = fix.source_change.source_file_edits.pop().unwrap().edit;
let actual = {
let mut actual = before.to_string();
edit.apply(&mut actual);
actual
};
assert_eq_text!(after, &actual);
}
/// Takes a multi-file input fixture with annotated cursor positions, /// Takes a multi-file input fixture with annotated cursor positions,
/// and checks that: /// and checks that:
@ -350,16 +318,21 @@ mod tests {
/// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics /// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
/// apply to the file containing the cursor. /// apply to the file containing the cursor.
fn check_no_diagnostic_for_target_file(ra_fixture: &str) { fn check_no_diagnostics(ra_fixture: &str) {
let (analysis, file_position) = analysis_and_position(ra_fixture); let mock = MockAnalysis::with_files(ra_fixture);
let diagnostics = analysis.diagnostics(file_position.file_id).unwrap(); let files = mock.files().map(|(it, _)| it).collect::<Vec<_>>();
assert_eq!(diagnostics.len(), 0); let analysis = mock.analysis();
let diagnostics = files
.into_iter()
.flat_map(|file_id| analysis.diagnostics(file_id).unwrap())
.collect::<Vec<_>>();
assert_eq!(diagnostics.len(), 0, "unexpected diagnostics:\n{:#?}", diagnostics);
} }
fn check_no_diagnostic(ra_fixture: &str) { fn check_expect(ra_fixture: &str, expect: Expect) {
let (analysis, file_id) = single_file(ra_fixture); let (analysis, file_id) = single_file(ra_fixture);
let diagnostics = analysis.diagnostics(file_id).unwrap(); let diagnostics = analysis.diagnostics(file_id).unwrap();
assert_eq!(diagnostics.len(), 0, "expected no diagnostic, found one"); expect.assert_debug_eq(&diagnostics)
} }
#[test] #[test]
@ -397,7 +370,7 @@ fn div(x: i32, y: i32) -> Result<i32, ()> {
fn test_wrap_return_type_handles_generic_functions() { fn test_wrap_return_type_handles_generic_functions() {
check_fix( check_fix(
r#" r#"
//- /main.rs //- /main.rs
use core::result::Result::{self, Ok, Err}; use core::result::Result::{self, Ok, Err};
fn div<T>(x: T) -> Result<T, i32> { fn div<T>(x: T) -> Result<T, i32> {
@ -461,44 +434,37 @@ fn div(x: i32, y: i32) -> MyResult<i32> {
#[test] #[test]
fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
check_no_diagnostic_for_target_file( check_no_diagnostics(
r" r#"
//- /main.rs //- /main.rs
use core::result::Result::{self, Ok, Err}; use core::result::Result::{self, Ok, Err};
fn foo() -> Result<(), i32> { fn foo() -> Result<(), i32> { 0 }
0<|>
}
//- /core/lib.rs //- /core/lib.rs
pub mod result { pub mod result {
pub enum Result<T, E> { Ok(T), Err(E) } pub enum Result<T, E> { Ok(T), Err(E) }
} }
", "#,
); );
} }
#[test] #[test]
fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
check_no_diagnostic_for_target_file( check_no_diagnostics(
r" r#"
//- /main.rs //- /main.rs
use core::result::Result::{self, Ok, Err}; use core::result::Result::{self, Ok, Err};
enum SomeOtherEnum { enum SomeOtherEnum { Ok(i32), Err(String) }
Ok(i32),
Err(String),
}
fn foo() -> SomeOtherEnum { fn foo() -> SomeOtherEnum { 0 }
0<|>
}
//- /core/lib.rs //- /core/lib.rs
pub mod result { pub mod result {
pub enum Result<T, E> { Ok(T), Err(E) } pub enum Result<T, E> { Ok(T), Err(E) }
} }
", "#,
); );
} }
@ -592,7 +558,7 @@ fn test_fn() {
#[test] #[test]
fn test_fill_struct_fields_no_diagnostic() { fn test_fill_struct_fields_no_diagnostic() {
check_no_diagnostic( check_no_diagnostics(
r" r"
struct TestStruct { one: i32, two: i64 } struct TestStruct { one: i32, two: i64 }
@ -606,7 +572,7 @@ fn test_fn() {
#[test] #[test]
fn test_fill_struct_fields_no_diagnostic_on_spread() { fn test_fill_struct_fields_no_diagnostic_on_spread() {
check_no_diagnostic( check_no_diagnostics(
r" r"
struct TestStruct { one: i32, two: i64 } struct TestStruct { one: i32, two: i64 }
@ -620,202 +586,134 @@ fn test_fn() {
#[test] #[test]
fn test_unresolved_module_diagnostic() { fn test_unresolved_module_diagnostic() {
let (analysis, file_id) = single_file("mod foo;"); check_expect(
let diagnostics = analysis.diagnostics(file_id).unwrap(); r#"mod foo;"#,
assert_debug_snapshot!(diagnostics, @r###" expect![[r#"
[ [
Diagnostic { Diagnostic {
message: "unresolved module", message: "unresolved module",
range: 0..8, range: 0..8,
severity: Error, severity: Error,
fix: Some( fix: Some(
Fix { Fix {
label: "Create module", label: "Create module",
source_change: SourceChange { source_change: SourceChange {
source_file_edits: [], source_file_edits: [],
file_system_edits: [ file_system_edits: [
CreateFile { CreateFile {
anchor: FileId( anchor: FileId(
1, 1,
), ),
dst: "foo.rs", dst: "foo.rs",
},
],
is_snippet: false,
}, },
], },
is_snippet: false, ),
},
}, },
), ]
}, "#]],
] );
"###);
} }
#[test] #[test]
fn range_mapping_out_of_macros() { fn range_mapping_out_of_macros() {
let (analysis, file_id) = single_file( // FIXME: this is very wrong, but somewhat tricky to fix.
r" check_fix(
fn some() {} r#"
fn items() {} fn some() {}
fn here() {} fn items() {}
fn here() {}
macro_rules! id { macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
($($tt:tt)*) => { $($tt)*};
}
fn main() { fn main() {
let _x = id![Foo { a: 42 }]; let _x = id![Foo { a: <|>42 }];
} }
pub struct Foo { pub struct Foo { pub a: i32, pub b: i32 }
pub a: i32, "#,
pub b: i32, r#"
} fn {a:42, b: ()} {}
", fn items() {}
fn here() {}
macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
fn main() {
let _x = id![Foo { a: 42 }];
}
pub struct Foo { pub a: i32, pub b: i32 }
"#,
); );
let diagnostics = analysis.diagnostics(file_id).unwrap();
assert_debug_snapshot!(diagnostics, @r###"
[
Diagnostic {
message: "Missing structure fields:\n- b\n",
range: 127..136,
severity: Error,
fix: Some(
Fix {
label: "Fill struct fields",
source_change: SourceChange {
source_file_edits: [
SourceFileEdit {
file_id: FileId(
1,
),
edit: TextEdit {
indels: [
Indel {
insert: "{a:42, b: ()}",
delete: 3..9,
},
],
},
},
],
file_system_edits: [],
is_snippet: false,
},
},
),
},
]
"###);
} }
#[test] #[test]
fn test_check_unnecessary_braces_in_use_statement() { fn test_check_unnecessary_braces_in_use_statement() {
check_not_applicable( check_no_diagnostics(
" r#"
use a; use a;
use a::{c, d::e}; use a::{c, d::e};
", "#,
check_unnecessary_braces_in_use_statement,
);
check_apply("use {b};", "use b;", check_unnecessary_braces_in_use_statement);
check_apply("use a::{c};", "use a::c;", check_unnecessary_braces_in_use_statement);
check_apply("use a::{self};", "use a;", check_unnecessary_braces_in_use_statement);
check_apply(
"use a::{c, d::{e}};",
"use a::{c, d::e};",
check_unnecessary_braces_in_use_statement,
); );
check_fix(r#"use {<|>b};"#, r#"use b;"#);
check_fix(r#"use {b<|>};"#, r#"use b;"#);
check_fix(r#"use a::{c<|>};"#, r#"use a::c;"#);
check_fix(r#"use a::{self<|>};"#, r#"use a;"#);
check_fix(r#"use a::{c, d::{e<|>}};"#, r#"use a::{c, d::e};"#);
} }
#[test] #[test]
fn test_check_struct_shorthand_initialization() { fn test_check_struct_shorthand_initialization() {
check_not_applicable( check_no_diagnostics(
r#" r#"
struct A { struct A { a: &'static str }
a: &'static str fn main() { A { a: "hello" } }
} "#,
fn main() {
A {
a: "hello"
}
}
"#,
check_struct_shorthand_initialization,
); );
check_not_applicable( check_no_diagnostics(
r#" r#"
struct A(usize); struct A(usize);
fn main() { A { 0: 0 } }
fn main() { "#,
A {
0: 0
}
}
"#,
check_struct_shorthand_initialization,
); );
check_apply( check_fix(
r#" r#"
struct A { struct A { a: &'static str }
a: &'static str
}
fn main() { fn main() {
let a = "haha"; let a = "haha";
A { A { a<|>: a }
a: a
}
} }
"#, "#,
r#" r#"
struct A { struct A { a: &'static str }
a: &'static str
}
fn main() { fn main() {
let a = "haha"; let a = "haha";
A { A { a }
a
}
} }
"#, "#,
check_struct_shorthand_initialization,
); );
check_apply( check_fix(
r#" r#"
struct A { struct A { a: &'static str, b: &'static str }
a: &'static str,
b: &'static str
}
fn main() { fn main() {
let a = "haha"; let a = "haha";
let b = "bb"; let b = "bb";
A { A { a<|>: a, b }
a: a,
b
}
} }
"#, "#,
r#" r#"
struct A { struct A { a: &'static str, b: &'static str }
a: &'static str,
b: &'static str
}
fn main() { fn main() {
let a = "haha"; let a = "haha";
let b = "bb"; let b = "bb";
A { A { a, b }
a,
b
}
} }
"#, "#,
check_struct_shorthand_initialization,
); );
} }

View file

@ -71,20 +71,13 @@ impl MockAnalysis {
} }
pub fn id_of(&self, path: &str) -> FileId { pub fn id_of(&self, path: &str) -> FileId {
let (idx, _) = self let (file_id, _) =
.files self.files().find(|(_, data)| path == data.path).expect("no file in this mock");
.iter() file_id
.enumerate()
.find(|(_, data)| path == data.path)
.expect("no file in this mock");
FileId(idx as u32 + 1)
} }
pub fn annotations(&self) -> Vec<(FileRange, String)> { pub fn annotations(&self) -> Vec<(FileRange, String)> {
self.files self.files()
.iter() .flat_map(|(file_id, fixture)| {
.enumerate()
.flat_map(|(idx, fixture)| {
let file_id = FileId(idx as u32 + 1);
let annotations = extract_annotations(&fixture.text); let annotations = extract_annotations(&fixture.text);
annotations annotations
.into_iter() .into_iter()
@ -92,6 +85,9 @@ impl MockAnalysis {
}) })
.collect() .collect()
} }
pub fn files(&self) -> impl Iterator<Item = (FileId, &Fixture)> + '_ {
self.files.iter().enumerate().map(|(idx, fixture)| (FileId(idx as u32 + 1), fixture))
}
pub fn annotation(&self) -> (FileRange, String) { pub fn annotation(&self) -> (FileRange, String) {
let mut all = self.annotations(); let mut all = self.annotations();
assert_eq!(all.len(), 1); assert_eq!(all.len(), 1);