mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 21:54:42 +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)]
|
#[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,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue