Don't diagnose imports whose base crate is missing

This commit is contained in:
Jonas Schievink 2020-09-17 14:48:17 +02:00
parent f792bc7ddd
commit 0dca7acf0f
2 changed files with 64 additions and 17 deletions

View File

@ -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,
));
}
}

View File

@ -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(