mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 21:13:37 +00:00
Remove ImportSource::ExternCrate as the fixed point loop can't affect it
This commit is contained in:
parent
5982d9c420
commit
f7ca085690
4 changed files with 102 additions and 138 deletions
|
@ -288,6 +288,11 @@ pub struct CrateData {
|
|||
/// The cfg options that could be used by the crate
|
||||
pub potential_cfg_options: Option<Arc<CfgOptions>>,
|
||||
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 origin: CrateOrigin,
|
||||
pub is_proc_macro: bool,
|
||||
|
|
|
@ -30,8 +30,8 @@ use crate::{
|
|||
db::DefDatabase,
|
||||
item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports},
|
||||
item_tree::{
|
||||
self, AttrOwner, ExternCrate, FieldsShape, FileItemTreeId, ImportKind, ItemTree,
|
||||
ItemTreeId, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId,
|
||||
self, AttrOwner, FieldsShape, FileItemTreeId, ImportKind, ItemTree, ItemTreeId,
|
||||
ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId,
|
||||
},
|
||||
macro_call_as_call_id, macro_call_as_call_id_with_eager,
|
||||
nameres::{
|
||||
|
@ -93,6 +93,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, def_map: DefMap, tree_id: TreeI
|
|||
proc_macros,
|
||||
from_glob_import: Default::default(),
|
||||
skip_attrs: Default::default(),
|
||||
unresolved_extern_crates: Default::default(),
|
||||
is_proc_macro: krate.is_proc_macro,
|
||||
};
|
||||
if tree_id.is_block() {
|
||||
|
@ -128,7 +129,6 @@ impl PartialResolvedImport {
|
|||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
enum ImportSource {
|
||||
Use { use_tree: Idx<ast::UseTree>, id: UseId, is_prelude: bool, kind: ImportKind },
|
||||
ExternCrate { id: ExternCrateId },
|
||||
}
|
||||
|
||||
#[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)]
|
||||
|
@ -218,11 +203,16 @@ enum MacroDirectiveKind {
|
|||
struct DefCollector<'a> {
|
||||
db: &'a dyn DefDatabase,
|
||||
def_map: DefMap,
|
||||
// The dependencies of the current crate, including optional deps like `test`.
|
||||
deps: FxHashMap<Name, Dependency>,
|
||||
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, UseId)>>,
|
||||
unresolved_imports: Vec<ImportDirective>,
|
||||
indeterminate_imports: Vec<(ImportDirective, PerNs)>,
|
||||
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>,
|
||||
cfg_options: &'a CfgOptions,
|
||||
/// 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 {
|
||||
// Add all
|
||||
if dep.is_prelude() {
|
||||
// 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
|
||||
|
@ -329,6 +320,7 @@ impl DefCollector<'_> {
|
|||
if skip {
|
||||
continue;
|
||||
}
|
||||
|
||||
crate_data
|
||||
.extern_prelude
|
||||
.insert(name.clone(), (CrateRootModuleId { krate: dep.crate_id }, None));
|
||||
|
@ -789,23 +781,6 @@ impl DefCollector<'_> {
|
|||
.entered();
|
||||
tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition);
|
||||
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 { .. } => {
|
||||
let res = self.def_map.resolve_path_fp_with_macro(
|
||||
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) {
|
||||
let _p = tracing::info_span!("record_resolved_import").entered();
|
||||
|
||||
|
@ -858,8 +824,7 @@ impl DefCollector<'_> {
|
|||
.unwrap_or(Visibility::Public);
|
||||
|
||||
match import.source {
|
||||
ImportSource::ExternCrate { .. }
|
||||
| ImportSource::Use { kind: ImportKind::Plain | ImportKind::TypeOnly, .. } => {
|
||||
ImportSource::Use { kind: ImportKind::Plain | ImportKind::TypeOnly, .. } => {
|
||||
let name = match &import.alias {
|
||||
Some(ImportAlias::Alias(name)) => Some(name),
|
||||
Some(ImportAlias::Underscore) => None,
|
||||
|
@ -873,22 +838,6 @@ impl DefCollector<'_> {
|
|||
};
|
||||
|
||||
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, .. } => {
|
||||
if kind == ImportKind::TypeOnly {
|
||||
def.values = None;
|
||||
|
@ -1560,35 +1509,12 @@ impl DefCollector<'_> {
|
|||
}
|
||||
|
||||
// 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 {
|
||||
if let ImportSource::ExternCrate { id } = directive.import.source {
|
||||
let item_tree_id = id.lookup(self.db).id;
|
||||
let item_tree = item_tree_id.item_tree(self.db);
|
||||
let extern_crate = &item_tree[item_tree_id.value];
|
||||
|
||||
diagnosed_extern_crates.insert(extern_crate.name.clone());
|
||||
|
||||
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
|
||||
{
|
||||
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)
|
||||
(Some(krate), PathKind::Plain | PathKind::Abs) if self.unresolved_extern_crates.contains(krate)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
@ -1599,7 +1525,6 @@ impl DefCollector<'_> {
|
|||
use_tree,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
self.def_map
|
||||
}
|
||||
|
@ -1623,7 +1548,8 @@ impl ModCollector<'_, '_> {
|
|||
|
||||
fn collect(&mut self, items: &[ModItem], container: ItemContainerId) {
|
||||
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
|
||||
// for macros.
|
||||
|
@ -1632,10 +1558,7 @@ impl ModCollector<'_, '_> {
|
|||
// Prelude module is always considered to be `#[macro_use]`.
|
||||
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.
|
||||
if prelude_module.krate != krate
|
||||
&& is_crate_root
|
||||
&& self.def_collector.def_map.block.is_none()
|
||||
{
|
||||
if prelude_module.krate != krate && is_crate_root {
|
||||
cov_mark::hit!(prelude_is_macro_use);
|
||||
self.def_collector.import_macros_from_extern_crate(
|
||||
prelude_module.krate,
|
||||
|
@ -1709,26 +1632,73 @@ impl ModCollector<'_, '_> {
|
|||
id: ItemTreeId::new(self.tree_id, item_tree_id),
|
||||
}
|
||||
.intern(db);
|
||||
def_map.modules[self.module_id].scope.define_extern_crate_decl(id);
|
||||
|
||||
let item_tree::ExternCrate { name, visibility, alias, ast_id } =
|
||||
&self.item_tree[item_tree_id];
|
||||
|
||||
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(
|
||||
item_tree_id,
|
||||
id,
|
||||
attrs.by_key(&sym::macro_use).attrs(),
|
||||
resolved.krate,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
self.def_collector.update(
|
||||
module_id,
|
||||
&[(
|
||||
name.cloned(),
|
||||
PerNs::types(
|
||||
resolved.into(),
|
||||
vis,
|
||||
Some(ImportOrExternCrate::ExternCrate(id)),
|
||||
),
|
||||
status: PartialResolvedImport::Unresolved,
|
||||
})
|
||||
)],
|
||||
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),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
ModItem::ExternBlock(block) => self.collect(
|
||||
&self.item_tree[block].children,
|
||||
|
@ -1939,27 +1909,15 @@ impl ModCollector<'_, '_> {
|
|||
|
||||
fn process_macro_use_extern_crate<'a>(
|
||||
&mut self,
|
||||
extern_crate: FileItemTreeId<ExternCrate>,
|
||||
extern_crate_id: ExternCrateId,
|
||||
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);
|
||||
|
||||
let mut single_imports = Vec::new();
|
||||
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
|
||||
// all visible macros.
|
||||
self.def_collector.import_macros_from_extern_crate(
|
||||
|
@ -2523,6 +2481,7 @@ mod tests {
|
|||
from_glob_import: Default::default(),
|
||||
skip_attrs: Default::default(),
|
||||
is_proc_macro: false,
|
||||
unresolved_extern_crates: Default::default(),
|
||||
};
|
||||
collector.seed_with_top_level();
|
||||
collector.collect();
|
||||
|
|
|
@ -416,7 +416,6 @@ pub struct Arc;
|
|||
|
||||
#[test]
|
||||
fn macro_use_extern_crate_self() {
|
||||
cov_mark::check!(ignore_macro_use_extern_crate_self);
|
||||
check(
|
||||
r#"
|
||||
//- /main.rs crate:main
|
||||
|
|
|
@ -792,6 +792,7 @@ pub(crate) fn orig_range_with_focus_r(
|
|||
.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 ((call_site_range, call_site_focus), def_site) =
|
||||
match InFile::new(hir_file, name).original_node_file_range_opt(db) {
|
||||
|
|
Loading…
Reference in a new issue