Remove ImportSource::ExternCrate as the fixed point loop can't affect it

This commit is contained in:
Lukas Wirth 2024-10-05 13:42:46 +02:00
parent 5982d9c420
commit f7ca085690
4 changed files with 102 additions and 138 deletions

View file

@ -288,6 +288,11 @@ pub struct CrateData {
/// The cfg options that could be used by the crate /// The cfg options that could be used by the crate
pub potential_cfg_options: Option<Arc<CfgOptions>>, pub potential_cfg_options: Option<Arc<CfgOptions>>,
pub env: Env, pub env: Env,
/// The dependencies of this crate.
///
/// Note that this may contain more dependencies than the crate actually uses.
/// A common example is the test crate which is included but only actually is active when
/// declared in source via `extern crate test`.
pub dependencies: Vec<Dependency>, pub dependencies: Vec<Dependency>,
pub origin: CrateOrigin, pub origin: CrateOrigin,
pub is_proc_macro: bool, pub is_proc_macro: bool,

View file

@ -30,8 +30,8 @@ use crate::{
db::DefDatabase, db::DefDatabase,
item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports}, item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports},
item_tree::{ item_tree::{
self, AttrOwner, ExternCrate, FieldsShape, FileItemTreeId, ImportKind, ItemTree, self, AttrOwner, FieldsShape, FileItemTreeId, ImportKind, ItemTree, ItemTreeId,
ItemTreeId, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId,
}, },
macro_call_as_call_id, macro_call_as_call_id_with_eager, macro_call_as_call_id, macro_call_as_call_id_with_eager,
nameres::{ nameres::{
@ -93,6 +93,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
proc_macros, proc_macros,
from_glob_import: Default::default(), from_glob_import: Default::default(),
skip_attrs: Default::default(), skip_attrs: Default::default(),
unresolved_extern_crates: Default::default(),
is_proc_macro: krate.is_proc_macro, is_proc_macro: krate.is_proc_macro,
}; };
if tree_id.is_block() { if tree_id.is_block() {
@ -128,7 +129,6 @@ impl PartialResolvedImport {
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
enum ImportSource { enum ImportSource {
Use { use_tree: Idx<ast::UseTree>, id: UseId, is_prelude: bool, kind: ImportKind }, Use { use_tree: Idx<ast::UseTree>, id: UseId, is_prelude: bool, kind: ImportKind },
ExternCrate { id: ExternCrateId },
} }
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
@ -158,21 +158,6 @@ impl Import {
}); });
}); });
} }
fn from_extern_crate(
tree: &ItemTree,
item_tree_id: ItemTreeId<item_tree::ExternCrate>,
id: ExternCrateId,
) -> Self {
let it = &tree[item_tree_id.value];
let visibility = &tree[it.visibility];
Self {
path: ModPath::from_segments(PathKind::Plain, iter::once(it.name.clone())),
alias: it.alias.clone(),
visibility: visibility.clone(),
source: ImportSource::ExternCrate { id },
}
}
} }
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
@ -218,11 +203,16 @@ enum MacroDirectiveKind {
struct DefCollector<'a> { struct DefCollector<'a> {
db: &'a dyn DefDatabase, db: &'a dyn DefDatabase,
def_map: DefMap, def_map: DefMap,
// The dependencies of the current crate, including optional deps like `test`.
deps: FxHashMap<Name, Dependency>, deps: FxHashMap<Name, Dependency>,
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, UseId)>>, glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, UseId)>>,
unresolved_imports: Vec<ImportDirective>, unresolved_imports: Vec<ImportDirective>,
indeterminate_imports: Vec<(ImportDirective, PerNs)>, indeterminate_imports: Vec<(ImportDirective, PerNs)>,
unresolved_macros: Vec<MacroDirective>, unresolved_macros: Vec<MacroDirective>,
// We'd like to avoid emitting a diagnostics avalanche when some `extern crate` doesn't
// resolve. When we emit diagnostics for unresolved imports, we only do so if the import
// doesn't start with an unresolved crate's name.
unresolved_extern_crates: FxHashSet<Name>,
mod_dirs: FxHashMap<LocalModuleId, ModDir>, mod_dirs: FxHashMap<LocalModuleId, ModDir>,
cfg_options: &'a CfgOptions, cfg_options: &'a CfgOptions,
/// List of procedural macros defined by this crate. This is read from the dynamic library /// List of procedural macros defined by this crate. This is read from the dynamic library
@ -310,6 +300,7 @@ impl DefCollector<'_> {
} }
for (name, dep) in &self.deps { for (name, dep) in &self.deps {
// Add all
if dep.is_prelude() { if dep.is_prelude() {
// This is a bit confusing but the gist is that `no_core` and `no_std` remove the // This is a bit confusing but the gist is that `no_core` and `no_std` remove the
// sysroot dependence on `core` and `std` respectively. Our `CrateGraph` is eagerly // sysroot dependence on `core` and `std` respectively. Our `CrateGraph` is eagerly
@ -329,6 +320,7 @@ impl DefCollector<'_> {
if skip { if skip {
continue; continue;
} }
crate_data crate_data
.extern_prelude .extern_prelude
.insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None)); .insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None));
@ -789,23 +781,6 @@ impl DefCollector<'_> {
.entered(); .entered();
tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition); tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition);
match import.source { match import.source {
ImportSource::ExternCrate { .. } => {
let name = import
.path
.as_ident()
.expect("extern crate should have been desugared to one-element path");
let res = self.resolve_extern_crate(name);
match res {
Some(res) => PartialResolvedImport::Resolved(PerNs::types(
res.into(),
Visibility::Public,
None,
)),
None => PartialResolvedImport::Unresolved,
}
}
ImportSource::Use { .. } => { ImportSource::Use { .. } => {
let res = self.def_map.resolve_path_fp_with_macro( let res = self.def_map.resolve_path_fp_with_macro(
self.db, self.db,
@ -837,15 +812,6 @@ impl DefCollector<'_> {
} }
} }
fn resolve_extern_crate(&self, name: &Name) -> Option<CrateRootModuleId> {
if *name == sym::self_.clone() {
cov_mark::hit!(extern_crate_self_as);
Some(self.def_map.crate_root())
} else {
self.deps.get(name).map(|dep| CrateRootModuleId { krate: dep.crate_id })
}
}
fn record_resolved_import(&mut self, directive: &ImportDirective) { fn record_resolved_import(&mut self, directive: &ImportDirective) {
let _p = tracing::info_span!("record_resolved_import").entered(); let _p = tracing::info_span!("record_resolved_import").entered();
@ -858,8 +824,7 @@ impl DefCollector<'_> {
.unwrap_or(Visibility::Public); .unwrap_or(Visibility::Public);
match import.source { match import.source {
ImportSource::ExternCrate { .. } ImportSource::Use { kind: ImportKind::Plain | ImportKind::TypeOnly, .. } => {
| ImportSource::Use { kind: ImportKind::Plain | ImportKind::TypeOnly, .. } => {
let name = match &import.alias { let name = match &import.alias {
Some(ImportAlias::Alias(name)) => Some(name), Some(ImportAlias::Alias(name)) => Some(name),
Some(ImportAlias::Underscore) => None, Some(ImportAlias::Underscore) => None,
@ -873,22 +838,6 @@ impl DefCollector<'_> {
}; };
let imp = match import.source { let imp = match import.source {
// extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
ImportSource::ExternCrate { id, .. } => {
if self.def_map.block.is_none() && module_id == DefMap::ROOT {
if let (Some(ModuleDefId::ModuleId(def)), Some(name)) =
(def.take_types(), name)
{
if let Ok(def) = def.try_into() {
Arc::get_mut(&mut self.def_map.data)
.unwrap()
.extern_prelude
.insert(name.clone(), (def, Some(id)));
}
}
}
ImportType::ExternCrate(id)
}
ImportSource::Use { kind, id, use_tree, .. } => { ImportSource::Use { kind, id, use_tree, .. } => {
if kind == ImportKind::TypeOnly { if kind == ImportKind::TypeOnly {
def.values = None; def.values = None;
@ -1560,45 +1509,21 @@ impl DefCollector<'_> {
} }
// Emit diagnostics for all remaining unresolved imports. // 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 { for directive in &self.unresolved_imports {
if let ImportSource::ExternCrate { id } = directive.import.source { let ImportSource::Use { use_tree, id, is_prelude: _, kind: _ } =
let item_tree_id = id.lookup(self.db).id; directive.import.source;
let item_tree = item_tree_id.item_tree(self.db); if matches!(
let extern_crate = &item_tree[item_tree_id.value]; (directive.import.path.segments().first(), &directive.import.path.kind),
(Some(krate), PathKind::Plain | PathKind::Abs) if self.unresolved_extern_crates.contains(krate)
diagnosed_extern_crates.insert(extern_crate.name.clone()); ) {
continue;
self.def_map.diagnostics.push(DefDiagnostic::unresolved_extern_crate(
directive.module_id,
InFile::new(item_tree_id.file_id(), extern_crate.ast_id),
));
}
}
for directive in &self.unresolved_imports {
if let ImportSource::Use { use_tree, id, is_prelude: _, kind: _ } =
directive.import.source
{
if matches!(
(directive.import.path.segments().first(), &directive.import.path.kind),
(Some(krate), PathKind::Plain | PathKind::Abs) if diagnosed_extern_crates.contains(krate)
) {
continue;
}
let item_tree_id = id.lookup(self.db).id;
self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
directive.module_id,
item_tree_id,
use_tree,
));
} }
let item_tree_id = id.lookup(self.db).id;
self.def_map.diagnostics.push(DefDiagnostic::unresolved_import(
directive.module_id,
item_tree_id,
use_tree,
));
} }
self.def_map self.def_map
@ -1623,7 +1548,8 @@ impl ModCollector<'_, '_> {
fn collect(&mut self, items: &[ModItem], container: ItemContainerId) { fn collect(&mut self, items: &[ModItem], container: ItemContainerId) {
let krate = self.def_collector.def_map.krate; let krate = self.def_collector.def_map.krate;
let is_crate_root = self.module_id == DefMap::ROOT; let is_crate_root =
self.module_id == DefMap::ROOT && self.def_collector.def_map.block.is_none();
// Note: don't assert that inserted value is fresh: it's simply not true // Note: don't assert that inserted value is fresh: it's simply not true
// for macros. // for macros.
@ -1632,10 +1558,7 @@ impl ModCollector<'_, '_> {
// Prelude module is always considered to be `#[macro_use]`. // Prelude module is always considered to be `#[macro_use]`.
if let Some((prelude_module, _use)) = self.def_collector.def_map.prelude { if let Some((prelude_module, _use)) = self.def_collector.def_map.prelude {
// Don't insert macros from the prelude into blocks, as they can be shadowed by other macros. // Don't insert macros from the prelude into blocks, as they can be shadowed by other macros.
if prelude_module.krate != krate if prelude_module.krate != krate && is_crate_root {
&& is_crate_root
&& self.def_collector.def_map.block.is_none()
{
cov_mark::hit!(prelude_is_macro_use); cov_mark::hit!(prelude_is_macro_use);
self.def_collector.import_macros_from_extern_crate( self.def_collector.import_macros_from_extern_crate(
prelude_module.krate, prelude_module.krate,
@ -1709,26 +1632,73 @@ impl ModCollector<'_, '_> {
id: ItemTreeId::new(self.tree_id, item_tree_id), id: ItemTreeId::new(self.tree_id, item_tree_id),
} }
.intern(db); .intern(db);
if is_crate_root { def_map.modules[self.module_id].scope.define_extern_crate_decl(id);
self.process_macro_use_extern_crate(
item_tree_id, let item_tree::ExternCrate { name, visibility, alias, ast_id } =
id, &self.item_tree[item_tree_id];
attrs.by_key(&sym::macro_use).attrs(),
let is_self = *name == sym::self_;
let resolved = if is_self {
cov_mark::hit!(extern_crate_self_as);
Some(def_map.crate_root())
} else {
self.def_collector
.deps
.get(name)
.map(|dep| CrateRootModuleId { krate: dep.crate_id })
};
let name = match alias {
Some(ImportAlias::Alias(name)) => Some(name),
Some(ImportAlias::Underscore) => None,
None => Some(name),
};
if let Some(resolved) = resolved {
let vis = resolve_vis(def_map, &self.item_tree[*visibility]);
if is_crate_root {
// extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
if let Some(name) = name {
Arc::get_mut(&mut def_map.data)
.unwrap()
.extern_prelude
.insert(name.clone(), (resolved, Some(id)));
}
// they also allow `#[macro_use]`
if !is_self {
self.process_macro_use_extern_crate(
id,
attrs.by_key(&sym::macro_use).attrs(),
resolved.krate,
);
}
}
self.def_collector.update(
module_id,
&[(
name.cloned(),
PerNs::types(
resolved.into(),
vis,
Some(ImportOrExternCrate::ExternCrate(id)),
),
)],
vis,
Some(ImportType::ExternCrate(id)),
);
} else {
if let Some(name) = name {
self.def_collector.unresolved_extern_crates.insert(name.clone());
}
self.def_collector.def_map.diagnostics.push(
DefDiagnostic::unresolved_extern_crate(
module_id,
InFile::new(self.file_id(), *ast_id),
),
); );
} }
self.def_collector.def_map.modules[self.module_id]
.scope
.define_extern_crate_decl(id);
self.def_collector.unresolved_imports.push(ImportDirective {
module_id: self.module_id,
import: Import::from_extern_crate(
self.item_tree,
ItemTreeId::new(self.tree_id, item_tree_id),
id,
),
status: PartialResolvedImport::Unresolved,
})
} }
ModItem::ExternBlock(block) => self.collect( ModItem::ExternBlock(block) => self.collect(
&self.item_tree[block].children, &self.item_tree[block].children,
@ -1939,27 +1909,15 @@ impl ModCollector<'_, '_> {
fn process_macro_use_extern_crate<'a>( fn process_macro_use_extern_crate<'a>(
&mut self, &mut self,
extern_crate: FileItemTreeId<ExternCrate>,
extern_crate_id: ExternCrateId, extern_crate_id: ExternCrateId,
macro_use_attrs: impl Iterator<Item = &'a Attr>, macro_use_attrs: impl Iterator<Item = &'a Attr>,
target_crate: CrateId,
) { ) {
let db = self.def_collector.db;
let target_crate =
match self.def_collector.resolve_extern_crate(&self.item_tree[extern_crate].name) {
Some(m) if m.krate == self.def_collector.def_map.krate => {
cov_mark::hit!(ignore_macro_use_extern_crate_self);
return;
}
Some(m) => m.krate,
None => return,
};
cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use); cov_mark::hit!(macro_rules_from_other_crates_are_visible_with_macro_use);
let mut single_imports = Vec::new(); let mut single_imports = Vec::new();
for attr in macro_use_attrs { for attr in macro_use_attrs {
let Some(paths) = attr.parse_path_comma_token_tree(db.upcast()) else { let Some(paths) = attr.parse_path_comma_token_tree(self.def_collector.db.upcast())
else {
// `#[macro_use]` (without any paths) found, forget collected names and just import // `#[macro_use]` (without any paths) found, forget collected names and just import
// all visible macros. // all visible macros.
self.def_collector.import_macros_from_extern_crate( self.def_collector.import_macros_from_extern_crate(
@ -2523,6 +2481,7 @@ mod tests {
from_glob_import: Default::default(), from_glob_import: Default::default(),
skip_attrs: Default::default(), skip_attrs: Default::default(),
is_proc_macro: false, is_proc_macro: false,
unresolved_extern_crates: Default::default(),
}; };
collector.seed_with_top_level(); collector.seed_with_top_level();
collector.collect(); collector.collect();

View file

@ -416,7 +416,6 @@ pub struct Arc;
#[test] #[test]
fn macro_use_extern_crate_self() { fn macro_use_extern_crate_self() {
cov_mark::check!(ignore_macro_use_extern_crate_self);
check( check(
r#" r#"
//- /main.rs crate:main //- /main.rs crate:main

View file

@ -792,6 +792,7 @@ pub(crate) fn orig_range_with_focus_r(
.definition_range(db) .definition_range(db)
}; };
// FIXME: Also make use of the syntax context to determine which site we are at?
let value_range = InFile::new(hir_file, value).original_node_file_range_opt(db); let value_range = InFile::new(hir_file, value).original_node_file_range_opt(db);
let ((call_site_range, call_site_focus), def_site) = let ((call_site_range, call_site_focus), def_site) =
match InFile::new(hir_file, name).original_node_file_range_opt(db) { match InFile::new(hir_file, name).original_node_file_range_opt(db) {