From f792bc7ddd2616c0bb1fcdffda204151fc40b3d6 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Wed, 16 Sep 2020 17:26:16 +0200 Subject: Add annotation-based nameres diagnostic tests --- crates/hir_def/src/nameres/tests.rs | 1 + crates/hir_def/src/nameres/tests/diagnostics.rs | 107 +++++++++++++++++++++ crates/hir_def/src/nameres/tests/mod_resolution.rs | 38 -------- crates/hir_def/src/test_db.rs | 42 ++++++++ 4 files changed, 150 insertions(+), 38 deletions(-) create mode 100644 crates/hir_def/src/nameres/tests/diagnostics.rs diff --git a/crates/hir_def/src/nameres/tests.rs b/crates/hir_def/src/nameres/tests.rs index 5ca30dac9..11d84f808 100644 --- a/crates/hir_def/src/nameres/tests.rs +++ b/crates/hir_def/src/nameres/tests.rs @@ -2,6 +2,7 @@ mod globs; mod incremental; mod macros; mod mod_resolution; +mod diagnostics; mod primitives; use std::sync::Arc; diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs new file mode 100644 index 000000000..cd0eb1a4b --- /dev/null +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs @@ -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> = 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 + ", + ); +} diff --git a/crates/hir_def/src/nameres/tests/mod_resolution.rs b/crates/hir_def/src/nameres/tests/mod_resolution.rs index 3b9f79544..f93337a6e 100644 --- a/crates/hir_def/src/nameres/tests/mod_resolution.rs +++ b/crates/hir_def/src/nameres/tests/mod_resolution.rs @@ -671,44 +671,6 @@ pub struct Baz; ); } -#[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::(0), - kind: UnresolvedModule { - declaration: InFile { - file_id: HirFileId( - FileId( - FileId( - 0, - ), - ), - ), - value: FileAstId::(1), - }, - candidate: "bar.rs", - }, - }, - ] - "#]] - .assert_debug_eq(&crate_def_map.diagnostics); -} - #[test] fn module_resolution_decl_inside_module_in_non_crate_root_2() { check( diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs index 42a762936..fb1d3c974 100644 --- a/crates/hir_def/src/test_db.rs +++ b/crates/hir_def/src/test_db.rs @@ -5,9 +5,15 @@ use std::{ sync::{Arc, Mutex}, }; +use base_db::SourceDatabase; use base_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; use hir_expand::db::AstDatabase; +use hir_expand::diagnostics::Diagnostic; +use hir_expand::diagnostics::DiagnosticSinkBuilder; +use rustc_hash::FxHashMap; use rustc_hash::FxHashSet; +use syntax::TextRange; +use test_utils::extract_annotations; use crate::db::DefDatabase; @@ -98,4 +104,40 @@ impl TestDB { }) .collect() } + + pub fn extract_annotations(&self) -> FxHashMap> { + 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(&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); + } + } + } } -- cgit v1.2.3