Add annotation-based nameres diagnostic tests
This commit is contained in:
parent
603613a302
commit
f792bc7ddd
@ -2,6 +2,7 @@
|
|||||||
mod incremental;
|
mod incremental;
|
||||||
mod macros;
|
mod macros;
|
||||||
mod mod_resolution;
|
mod mod_resolution;
|
||||||
|
mod diagnostics;
|
||||||
mod primitives;
|
mod primitives;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
107
crates/hir_def/src/nameres/tests/diagnostics.rs
Normal file
107
crates/hir_def/src/nameres/tests/diagnostics.rs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
use base_db::fixture::WithFixture;
|
||||||
|
use base_db::FileId;
|
||||||
|
use base_db::SourceDatabaseExt;
|
||||||
|
use hir_expand::db::AstDatabase;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
use syntax::TextRange;
|
||||||
|
use syntax::TextSize;
|
||||||
|
|
||||||
|
use crate::test_db::TestDB;
|
||||||
|
|
||||||
|
fn check_diagnostics(ra_fixture: &str) {
|
||||||
|
let db: TestDB = TestDB::with_files(ra_fixture);
|
||||||
|
let annotations = db.extract_annotations();
|
||||||
|
assert!(!annotations.is_empty());
|
||||||
|
|
||||||
|
let mut actual: FxHashMap<FileId, Vec<(TextRange, String)>> = FxHashMap::default();
|
||||||
|
db.diagnostics(|d| {
|
||||||
|
let src = d.display_source();
|
||||||
|
let root = db.parse_or_expand(src.file_id).unwrap();
|
||||||
|
// FIXME: macros...
|
||||||
|
let file_id = src.file_id.original_file(&db);
|
||||||
|
let range = src.value.to_node(&root).text_range();
|
||||||
|
let message = d.message().to_owned();
|
||||||
|
actual.entry(file_id).or_default().push((range, message));
|
||||||
|
});
|
||||||
|
|
||||||
|
for (file_id, diags) in actual.iter_mut() {
|
||||||
|
diags.sort_by_key(|it| it.0.start());
|
||||||
|
let text = db.file_text(*file_id);
|
||||||
|
// For multiline spans, place them on line start
|
||||||
|
for (range, content) in diags {
|
||||||
|
if text[*range].contains('\n') {
|
||||||
|
*range = TextRange::new(range.start(), range.start() + TextSize::from(1));
|
||||||
|
*content = format!("... {}", content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(annotations, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unresolved_import() {
|
||||||
|
check_diagnostics(
|
||||||
|
r"
|
||||||
|
use does_exist;
|
||||||
|
use does_not_exist;
|
||||||
|
//^^^^^^^^^^^^^^ unresolved import
|
||||||
|
|
||||||
|
mod does_exist {}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unresolved_import_in_use_tree() {
|
||||||
|
// Only the relevant part of a nested `use` item should be highlighted.
|
||||||
|
check_diagnostics(
|
||||||
|
r"
|
||||||
|
use does_exist::{Exists, DoesntExist};
|
||||||
|
//^^^^^^^^^^^ unresolved import
|
||||||
|
|
||||||
|
use {does_not_exist::*, does_exist};
|
||||||
|
//^^^^^^^^^^^^^^^^^ unresolved import
|
||||||
|
|
||||||
|
use does_not_exist::{
|
||||||
|
a,
|
||||||
|
//^ unresolved import
|
||||||
|
b,
|
||||||
|
//^ unresolved import
|
||||||
|
c,
|
||||||
|
//^ unresolved import
|
||||||
|
};
|
||||||
|
|
||||||
|
mod does_exist {
|
||||||
|
pub struct Exists;
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unresolved_extern_crate() {
|
||||||
|
check_diagnostics(
|
||||||
|
r"
|
||||||
|
//- /main.rs crate:main deps:core
|
||||||
|
extern crate core;
|
||||||
|
extern crate doesnotexist;
|
||||||
|
//^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate
|
||||||
|
//- /lib.rs crate:core
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unresolved_module() {
|
||||||
|
check_diagnostics(
|
||||||
|
r"
|
||||||
|
//- /lib.rs
|
||||||
|
mod foo;
|
||||||
|
mod bar;
|
||||||
|
//^^^^^^^^ unresolved module
|
||||||
|
mod baz {}
|
||||||
|
//- /foo.rs
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
@ -671,44 +671,6 @@ mod bar {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn unresolved_module_diagnostics() {
|
|
||||||
let db = TestDB::with_files(
|
|
||||||
r"
|
|
||||||
//- /lib.rs
|
|
||||||
mod foo;
|
|
||||||
mod bar;
|
|
||||||
mod baz {}
|
|
||||||
//- /foo.rs
|
|
||||||
",
|
|
||||||
);
|
|
||||||
let krate = db.test_crate();
|
|
||||||
|
|
||||||
let crate_def_map = db.crate_def_map(krate);
|
|
||||||
|
|
||||||
expect![[r#"
|
|
||||||
[
|
|
||||||
DefDiagnostic {
|
|
||||||
in_module: Idx::<ModuleData>(0),
|
|
||||||
kind: UnresolvedModule {
|
|
||||||
declaration: InFile {
|
|
||||||
file_id: HirFileId(
|
|
||||||
FileId(
|
|
||||||
FileId(
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
value: FileAstId::<syntax::ast::generated::nodes::Module>(1),
|
|
||||||
},
|
|
||||||
candidate: "bar.rs",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
"#]]
|
|
||||||
.assert_debug_eq(&crate_def_map.diagnostics);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn module_resolution_decl_inside_module_in_non_crate_root_2() {
|
fn module_resolution_decl_inside_module_in_non_crate_root_2() {
|
||||||
check(
|
check(
|
||||||
|
@ -5,9 +5,15 @@
|
|||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use base_db::SourceDatabase;
|
||||||
use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast};
|
use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast};
|
||||||
use hir_expand::db::AstDatabase;
|
use hir_expand::db::AstDatabase;
|
||||||
|
use hir_expand::diagnostics::Diagnostic;
|
||||||
|
use hir_expand::diagnostics::DiagnosticSinkBuilder;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
use syntax::TextRange;
|
||||||
|
use test_utils::extract_annotations;
|
||||||
|
|
||||||
use crate::db::DefDatabase;
|
use crate::db::DefDatabase;
|
||||||
|
|
||||||
@ -98,4 +104,40 @@ pub fn log_executed(&self, f: impl FnOnce()) -> Vec<String> {
|
|||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn extract_annotations(&self) -> FxHashMap<FileId, Vec<(TextRange, String)>> {
|
||||||
|
let mut files = Vec::new();
|
||||||
|
let crate_graph = self.crate_graph();
|
||||||
|
for krate in crate_graph.iter() {
|
||||||
|
let crate_def_map = self.crate_def_map(krate);
|
||||||
|
for (module_id, _) in crate_def_map.modules.iter() {
|
||||||
|
let file_id = crate_def_map[module_id].origin.file_id();
|
||||||
|
files.extend(file_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(!files.is_empty());
|
||||||
|
files
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|file_id| {
|
||||||
|
let text = self.file_text(file_id);
|
||||||
|
let annotations = extract_annotations(&text);
|
||||||
|
if annotations.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some((file_id, annotations))
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn diagnostics<F: FnMut(&dyn Diagnostic)>(&self, mut cb: F) {
|
||||||
|
let crate_graph = self.crate_graph();
|
||||||
|
for krate in crate_graph.iter() {
|
||||||
|
let crate_def_map = self.crate_def_map(krate);
|
||||||
|
|
||||||
|
let mut sink = DiagnosticSinkBuilder::new().build(&mut cb);
|
||||||
|
for (module_id, _) in crate_def_map.modules.iter() {
|
||||||
|
crate_def_map.add_diagnostics(self, module_id, &mut sink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user