mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-14 00:47:18 +00:00
Cleanup diagnostics tests
This commit is contained in:
parent
ea68a1d0c9
commit
fb0bc941a5
2 changed files with 129 additions and 235 deletions
|
@ -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,
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue