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)]
mod tests {
use insta::assert_debug_snapshot;
use ra_syntax::SourceFile;
use stdx::trim_indent;
use test_utils::assert_eq_text;
use crate::mock_analysis::{analysis_and_position, single_file};
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);
}
use crate::mock_analysis::{analysis_and_position, single_file, MockAnalysis};
use expect::{expect, Expect};
/// Takes a multi-file input fixture with annotated cursor positions,
/// and checks that:
@ -350,16 +318,21 @@ mod tests {
/// Takes a multi-file input fixture with annotated cursor position and checks that no diagnostics
/// apply to the file containing the cursor.
fn check_no_diagnostic_for_target_file(ra_fixture: &str) {
let (analysis, file_position) = analysis_and_position(ra_fixture);
let diagnostics = analysis.diagnostics(file_position.file_id).unwrap();
assert_eq!(diagnostics.len(), 0);
fn check_no_diagnostics(ra_fixture: &str) {
let mock = MockAnalysis::with_files(ra_fixture);
let files = mock.files().map(|(it, _)| it).collect::<Vec<_>>();
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 diagnostics = analysis.diagnostics(file_id).unwrap();
assert_eq!(diagnostics.len(), 0, "expected no diagnostic, found one");
expect.assert_debug_eq(&diagnostics)
}
#[test]
@ -397,7 +370,7 @@ fn div(x: i32, y: i32) -> Result<i32, ()> {
fn test_wrap_return_type_handles_generic_functions() {
check_fix(
r#"
//- /main.rs
//- /main.rs
use core::result::Result::{self, Ok, Err};
fn div<T>(x: T) -> Result<T, i32> {
@ -461,44 +434,37 @@ fn div(x: i32, y: i32) -> MyResult<i32> {
#[test]
fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
check_no_diagnostic_for_target_file(
r"
//- /main.rs
use core::result::Result::{self, Ok, Err};
check_no_diagnostics(
r#"
//- /main.rs
use core::result::Result::{self, Ok, Err};
fn foo() -> Result<(), i32> {
0<|>
}
fn foo() -> Result<(), i32> { 0 }
//- /core/lib.rs
pub mod result {
pub enum Result<T, E> { Ok(T), Err(E) }
}
",
//- /core/lib.rs
pub mod result {
pub enum Result<T, E> { Ok(T), Err(E) }
}
"#,
);
}
#[test]
fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
check_no_diagnostic_for_target_file(
r"
//- /main.rs
use core::result::Result::{self, Ok, Err};
check_no_diagnostics(
r#"
//- /main.rs
use core::result::Result::{self, Ok, Err};
enum SomeOtherEnum {
Ok(i32),
Err(String),
}
enum SomeOtherEnum { Ok(i32), Err(String) }
fn foo() -> SomeOtherEnum {
0<|>
}
fn foo() -> SomeOtherEnum { 0 }
//- /core/lib.rs
pub mod result {
pub enum Result<T, E> { Ok(T), Err(E) }
}
",
//- /core/lib.rs
pub mod result {
pub enum Result<T, E> { Ok(T), Err(E) }
}
"#,
);
}
@ -592,7 +558,7 @@ fn test_fn() {
#[test]
fn test_fill_struct_fields_no_diagnostic() {
check_no_diagnostic(
check_no_diagnostics(
r"
struct TestStruct { one: i32, two: i64 }
@ -606,7 +572,7 @@ fn test_fn() {
#[test]
fn test_fill_struct_fields_no_diagnostic_on_spread() {
check_no_diagnostic(
check_no_diagnostics(
r"
struct TestStruct { one: i32, two: i64 }
@ -620,202 +586,134 @@ fn test_fn() {
#[test]
fn test_unresolved_module_diagnostic() {
let (analysis, file_id) = single_file("mod foo;");
let diagnostics = analysis.diagnostics(file_id).unwrap();
assert_debug_snapshot!(diagnostics, @r###"
[
Diagnostic {
message: "unresolved module",
range: 0..8,
severity: Error,
fix: Some(
Fix {
label: "Create module",
source_change: SourceChange {
source_file_edits: [],
file_system_edits: [
CreateFile {
anchor: FileId(
1,
),
dst: "foo.rs",
check_expect(
r#"mod foo;"#,
expect![[r#"
[
Diagnostic {
message: "unresolved module",
range: 0..8,
severity: Error,
fix: Some(
Fix {
label: "Create module",
source_change: SourceChange {
source_file_edits: [],
file_system_edits: [
CreateFile {
anchor: FileId(
1,
),
dst: "foo.rs",
},
],
is_snippet: false,
},
],
is_snippet: false,
},
},
),
},
),
},
]
"###);
]
"#]],
);
}
#[test]
fn range_mapping_out_of_macros() {
let (analysis, file_id) = single_file(
r"
fn some() {}
fn items() {}
fn here() {}
// FIXME: this is very wrong, but somewhat tricky to fix.
check_fix(
r#"
fn some() {}
fn items() {}
fn here() {}
macro_rules! id {
($($tt:tt)*) => { $($tt)*};
}
macro_rules! id { ($($tt:tt)*) => { $($tt)*}; }
fn main() {
let _x = id![Foo { a: 42 }];
}
fn main() {
let _x = id![Foo { a: <|>42 }];
}
pub struct Foo {
pub a: i32,
pub b: i32,
}
",
pub struct Foo { 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]
fn test_check_unnecessary_braces_in_use_statement() {
check_not_applicable(
"
use a;
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_no_diagnostics(
r#"
use a;
use a::{c, d::e};
"#,
);
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]
fn test_check_struct_shorthand_initialization() {
check_not_applicable(
check_no_diagnostics(
r#"
struct A {
a: &'static str
}
fn main() {
A {
a: "hello"
}
}
"#,
check_struct_shorthand_initialization,
struct A { a: &'static str }
fn main() { A { a: "hello" } }
"#,
);
check_not_applicable(
check_no_diagnostics(
r#"
struct A(usize);
fn main() {
A {
0: 0
}
}
"#,
check_struct_shorthand_initialization,
struct A(usize);
fn main() { A { 0: 0 } }
"#,
);
check_apply(
check_fix(
r#"
struct A {
a: &'static str
}
struct A { a: &'static str }
fn main() {
let a = "haha";
A {
a: a
}
A { a<|>: a }
}
"#,
"#,
r#"
struct A {
a: &'static str
}
struct A { a: &'static str }
fn main() {
let a = "haha";
A {
a
}
A { a }
}
"#,
check_struct_shorthand_initialization,
"#,
);
check_apply(
check_fix(
r#"
struct A {
a: &'static str,
b: &'static str
}
struct A { a: &'static str, b: &'static str }
fn main() {
let a = "haha";
let b = "bb";
A {
a: a,
b
}
A { a<|>: a, b }
}
"#,
"#,
r#"
struct A {
a: &'static str,
b: &'static str
}
struct A { a: &'static str, b: &'static str }
fn main() {
let a = "haha";
let b = "bb";
A {
a,
b
}
A { a, b }
}
"#,
check_struct_shorthand_initialization,
"#,
);
}

View file

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