From 0dca7acf0fb65545f0c46f0c604bb15400aa6d91 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Thu, 17 Sep 2020 14:48:17 +0200 Subject: Don't diagnose imports whose base crate is missing --- crates/hir_def/src/nameres/collector.rs | 57 +++++++++++++++++-------- crates/hir_def/src/nameres/tests/diagnostics.rs | 24 +++++++++++ 2 files changed, 64 insertions(+), 17 deletions(-) (limited to 'crates/hir_def') diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index deb3885f9..818008169 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -15,6 +15,7 @@ use hir_expand::{ HirFileId, MacroCallId, MacroDefId, MacroDefKind, }; use rustc_hash::FxHashMap; +use rustc_hash::FxHashSet; use syntax::ast; use test_utils::mark; @@ -788,25 +789,47 @@ impl DefCollector<'_> { } fn finish(mut self) -> CrateDefMap { + // Emit diagnostics for all remaining unresolved imports. + + // We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't + // resolve. We first emit diagnostics for unresolved extern crates and collect the missing + // crate names. Then we emit diagnostics for unresolved imports, but only if the import + // doesn't start with an unresolved crate's name. Due to renaming and reexports, this is a + // heuristic, but it works in practice. + let mut diagnosed_extern_crates = FxHashSet::default(); for directive in &self.unresolved_imports { - match directive.import.source { - ImportSource::Import(import) => { - let item_tree = self.db.item_tree(import.file_id); - let import_data = &item_tree[import.value]; - self.def_map.diagnostics.push(DefDiagnostic::unresolved_import( - directive.module_id, - InFile::new(import.file_id, import_data.ast_id), - import_data.index, - )); - } - ImportSource::ExternCrate(krate) => { - let item_tree = self.db.item_tree(krate.file_id); - let extern_crate = &item_tree[krate.value]; - self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate( - directive.module_id, - InFile::new(krate.file_id, extern_crate.ast_id), - )); + if let ImportSource::ExternCrate(krate) = directive.import.source { + let item_tree = self.db.item_tree(krate.file_id); + let extern_crate = &item_tree[krate.value]; + + diagnosed_extern_crates.insert(extern_crate.path.segments[0].clone()); + + self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate( + directive.module_id, + InFile::new(krate.file_id, extern_crate.ast_id), + )); + } + } + + for directive in &self.unresolved_imports { + if let ImportSource::Import(import) = &directive.import.source { + let item_tree = self.db.item_tree(import.file_id); + let import_data = &item_tree[import.value]; + + match (import_data.path.segments.first(), &import_data.path.kind) { + (Some(krate), PathKind::Plain) | (Some(krate), PathKind::Abs) => { + if diagnosed_extern_crates.contains(krate) { + continue; + } + } + _ => {} } + + self.def_map.diagnostics.push(DefDiagnostic::unresolved_import( + directive.module_id, + InFile::new(import.file_id, import_data.ast_id), + import_data.index, + )); } } diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs index cd0eb1a4b..576b813d2 100644 --- a/crates/hir_def/src/nameres/tests/diagnostics.rs +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs @@ -92,6 +92,30 @@ fn unresolved_extern_crate() { ); } +#[test] +fn dedup_unresolved_import_from_unresolved_crate() { + check_diagnostics( + r" + //- /main.rs crate:main + mod a { + extern crate doesnotexist; + //^^^^^^^^^^^^^^^^^^^^^^^^^^ unresolved extern crate + + // Should not error, since we already errored for the missing crate. + use doesnotexist::{self, bla, *}; + + use crate::doesnotexist; + //^^^^^^^^^^^^^^^^^^^ unresolved import + } + + mod m { + use super::doesnotexist; + //^^^^^^^^^^^^^^^^^^^ unresolved import + } + ", + ); +} + #[test] fn unresolved_module() { check_diagnostics( -- cgit v1.2.3