mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-17 10:18:31 +00:00
Use ImportMap
in find_path
, remove old queries
This commit is contained in:
parent
d08c63cb9e
commit
3c496f7fa7
3 changed files with 137 additions and 94 deletions
|
@ -1,7 +1,7 @@
|
||||||
//! Defines database & queries for name resolution.
|
//! Defines database & queries for name resolution.
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use hir_expand::{db::AstDatabase, name::Name, HirFileId};
|
use hir_expand::{db::AstDatabase, HirFileId};
|
||||||
use ra_db::{salsa, CrateId, SourceDatabase, Upcast};
|
use ra_db::{salsa, CrateId, SourceDatabase, Upcast};
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
use ra_syntax::SmolStr;
|
use ra_syntax::SmolStr;
|
||||||
|
@ -12,14 +12,10 @@ use crate::{
|
||||||
body::{scope::ExprScopes, Body, BodySourceMap},
|
body::{scope::ExprScopes, Body, BodySourceMap},
|
||||||
data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData},
|
data::{ConstData, FunctionData, ImplData, StaticData, TraitData, TypeAliasData},
|
||||||
docs::Documentation,
|
docs::Documentation,
|
||||||
find_path,
|
|
||||||
generics::GenericParams,
|
generics::GenericParams,
|
||||||
import_map::ImportMap,
|
import_map::ImportMap,
|
||||||
item_scope::ItemInNs,
|
|
||||||
lang_item::{LangItemTarget, LangItems},
|
lang_item::{LangItemTarget, LangItems},
|
||||||
nameres::{raw::RawItems, CrateDefMap},
|
nameres::{raw::RawItems, CrateDefMap},
|
||||||
path::ModPath,
|
|
||||||
visibility::Visibility,
|
|
||||||
AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
|
AttrDefId, ConstId, ConstLoc, DefWithBodyId, EnumId, EnumLoc, FunctionId, FunctionLoc,
|
||||||
GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
|
GenericDefId, ImplId, ImplLoc, ModuleId, StaticId, StaticLoc, StructId, StructLoc, TraitId,
|
||||||
TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
|
TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
|
||||||
|
@ -114,16 +110,6 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
|
||||||
#[salsa::invoke(Documentation::documentation_query)]
|
#[salsa::invoke(Documentation::documentation_query)]
|
||||||
fn documentation(&self, def: AttrDefId) -> Option<Documentation>;
|
fn documentation(&self, def: AttrDefId) -> Option<Documentation>;
|
||||||
|
|
||||||
#[salsa::invoke(find_path::importable_locations_of_query)]
|
|
||||||
fn importable_locations_of(
|
|
||||||
&self,
|
|
||||||
item: ItemInNs,
|
|
||||||
krate: CrateId,
|
|
||||||
) -> Arc<[(ModuleId, Name, Visibility)]>;
|
|
||||||
|
|
||||||
#[salsa::invoke(find_path::find_path_inner_query)]
|
|
||||||
fn find_path_inner(&self, item: ItemInNs, from: ModuleId, max_len: usize) -> Option<ModPath>;
|
|
||||||
|
|
||||||
#[salsa::invoke(ImportMap::import_map_query)]
|
#[salsa::invoke(ImportMap::import_map_query)]
|
||||||
fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
|
fn import_map(&self, krate: CrateId) -> Arc<ImportMap>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
//! An algorithm to find a path to refer to a certain item.
|
//! An algorithm to find a path to refer to a certain item.
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use hir_expand::name::{known, AsName, Name};
|
use hir_expand::name::{known, AsName, Name};
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
use test_utils::mark;
|
use test_utils::mark;
|
||||||
|
@ -11,8 +9,9 @@ use crate::{
|
||||||
item_scope::ItemInNs,
|
item_scope::ItemInNs,
|
||||||
path::{ModPath, PathKind},
|
path::{ModPath, PathKind},
|
||||||
visibility::Visibility,
|
visibility::Visibility,
|
||||||
CrateId, ModuleDefId, ModuleId,
|
ModuleDefId, ModuleId,
|
||||||
};
|
};
|
||||||
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
// FIXME: handle local items
|
// FIXME: handle local items
|
||||||
|
|
||||||
|
@ -20,7 +19,7 @@ use crate::{
|
||||||
/// *from where* you're referring to the item, hence the `from` parameter.
|
/// *from where* you're referring to the item, hence the `from` parameter.
|
||||||
pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
|
pub fn find_path(db: &dyn DefDatabase, item: ItemInNs, from: ModuleId) -> Option<ModPath> {
|
||||||
let _p = profile("find_path");
|
let _p = profile("find_path");
|
||||||
db.find_path_inner(item, from, MAX_PATH_LEN)
|
find_path_inner(db, item, from, MAX_PATH_LEN)
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_PATH_LEN: usize = 15;
|
const MAX_PATH_LEN: usize = 15;
|
||||||
|
@ -38,7 +37,7 @@ impl ModPath {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn find_path_inner_query(
|
pub(crate) fn find_path_inner(
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
item: ItemInNs,
|
item: ItemInNs,
|
||||||
from: ModuleId,
|
from: ModuleId,
|
||||||
|
@ -122,21 +121,25 @@ pub(crate) fn find_path_inner_query(
|
||||||
}
|
}
|
||||||
|
|
||||||
// - otherwise, look for modules containing (reexporting) it and import it from one of those
|
// - otherwise, look for modules containing (reexporting) it and import it from one of those
|
||||||
|
|
||||||
let crate_root = ModuleId { local_id: def_map.root, krate: from.krate };
|
let crate_root = ModuleId { local_id: def_map.root, krate: from.krate };
|
||||||
let crate_attrs = db.attrs(crate_root.into());
|
let crate_attrs = db.attrs(crate_root.into());
|
||||||
let prefer_no_std = crate_attrs.by_key("no_std").exists();
|
let prefer_no_std = crate_attrs.by_key("no_std").exists();
|
||||||
let importable_locations = find_importable_locations(db, item, from);
|
|
||||||
let mut best_path = None;
|
let mut best_path = None;
|
||||||
let mut best_path_len = max_len;
|
let mut best_path_len = max_len;
|
||||||
for (module_id, name) in importable_locations {
|
|
||||||
let mut path = match db.find_path_inner(
|
if item.defining_crate(db) == Some(from.krate) {
|
||||||
|
// Item was defined in the same crate that wants to import it. It cannot be found in any
|
||||||
|
// dependency in this case.
|
||||||
|
|
||||||
|
let local_imports = find_local_import_locations(db, item, from);
|
||||||
|
for (module_id, name) in local_imports {
|
||||||
|
if let Some(mut path) = find_path_inner(
|
||||||
|
db,
|
||||||
ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
|
ItemInNs::Types(ModuleDefId::ModuleId(module_id)),
|
||||||
from,
|
from,
|
||||||
best_path_len - 1,
|
best_path_len - 1,
|
||||||
) {
|
) {
|
||||||
None => continue,
|
|
||||||
Some(path) => path,
|
|
||||||
};
|
|
||||||
path.segments.push(name);
|
path.segments.push(name);
|
||||||
|
|
||||||
let new_path = if let Some(best_path) = best_path {
|
let new_path = if let Some(best_path) = best_path {
|
||||||
|
@ -147,6 +150,32 @@ pub(crate) fn find_path_inner_query(
|
||||||
best_path_len = new_path.len();
|
best_path_len = new_path.len();
|
||||||
best_path = Some(new_path);
|
best_path = Some(new_path);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Item was defined in some upstream crate. This means that it must be exported from one,
|
||||||
|
// too (unless we can't name it at all). It could *also* be (re)exported by the same crate
|
||||||
|
// that wants to import it here, but we always prefer to use the external path here.
|
||||||
|
|
||||||
|
let crate_graph = db.crate_graph();
|
||||||
|
let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| {
|
||||||
|
let import_map = db.import_map(dep.crate_id);
|
||||||
|
import_map.path_of(item).map(|modpath| {
|
||||||
|
let mut modpath = modpath.clone();
|
||||||
|
modpath.segments.insert(0, dep.as_name());
|
||||||
|
modpath
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
for path in extern_paths {
|
||||||
|
let new_path = if let Some(best_path) = best_path {
|
||||||
|
select_best_path(best_path, path, prefer_no_std)
|
||||||
|
} else {
|
||||||
|
path
|
||||||
|
};
|
||||||
|
best_path = Some(new_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
best_path
|
best_path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,56 +203,55 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_importable_locations(
|
/// Finds locations in `from.krate` from which `item` can be imported by `from`.
|
||||||
|
fn find_local_import_locations(
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
item: ItemInNs,
|
item: ItemInNs,
|
||||||
from: ModuleId,
|
from: ModuleId,
|
||||||
) -> Vec<(ModuleId, Name)> {
|
) -> Vec<(ModuleId, Name)> {
|
||||||
let crate_graph = db.crate_graph();
|
let _p = profile("find_local_import_locations");
|
||||||
let mut result = Vec::new();
|
|
||||||
|
|
||||||
// We only look in the crate from which we are importing, and the direct
|
// `from` can import anything below `from` with visibility of at least `from`, and anything
|
||||||
// dependencies. We cannot refer to names from transitive dependencies
|
// above `from` with any visibility. That means we do not need to descend into private siblings
|
||||||
// directly (only through reexports in direct dependencies).
|
// of `from` (and similar).
|
||||||
|
|
||||||
// For the crate from which we're importing, we have to check whether any
|
let def_map = db.crate_def_map(from.krate);
|
||||||
// module visible to `from` exports the item we're looking for.
|
|
||||||
// For dependencies of the crate only `pub` items reachable through `pub`
|
// Compute the initial worklist. We start with all direct child modules of `from` as well as all
|
||||||
// modules from the crate root are relevant. For that we precompute an
|
// of its (recursive) parent modules.
|
||||||
// import map that tells us the shortest path to any importable item with a
|
let data = &def_map.modules[from.local_id];
|
||||||
// single lookup.
|
let mut worklist = data
|
||||||
for krate in Some(from.krate)
|
.children
|
||||||
.into_iter()
|
.values()
|
||||||
.chain(crate_graph[from.krate].dependencies.iter().map(|dep| dep.crate_id))
|
.map(|child| ModuleId { krate: from.krate, local_id: *child })
|
||||||
{
|
.collect::<Vec<_>>();
|
||||||
result.extend(
|
let mut parent = data.parent;
|
||||||
db.importable_locations_of(item, krate)
|
while let Some(p) = parent {
|
||||||
.iter()
|
worklist.push(ModuleId { krate: from.krate, local_id: p });
|
||||||
.filter(|(_, _, vis)| vis.is_visible_from(db, from))
|
parent = def_map.modules[p].parent;
|
||||||
.map(|(m, n, _)| (*m, n.clone())),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Collects all locations from which we might import the item in a particular
|
let mut seen: FxHashSet<_> = FxHashSet::default();
|
||||||
/// crate. These include the original definition of the item, and any
|
|
||||||
/// non-private `use`s.
|
let mut locations = Vec::new();
|
||||||
///
|
while let Some(module) = worklist.pop() {
|
||||||
/// Note that the crate doesn't need to be the one in which the item is defined;
|
if !seen.insert(module) {
|
||||||
/// it might be re-exported in other crates.
|
continue; // already processed this module
|
||||||
pub(crate) fn importable_locations_of_query(
|
}
|
||||||
db: &dyn DefDatabase,
|
|
||||||
item: ItemInNs,
|
let ext_def_map;
|
||||||
krate: CrateId,
|
let data = if module.krate == from.krate {
|
||||||
) -> Arc<[(ModuleId, Name, Visibility)]> {
|
&def_map[module.local_id]
|
||||||
let _p = profile("importable_locations_of_query");
|
} else {
|
||||||
let def_map = db.crate_def_map(krate);
|
// The crate might reexport a module defined in another crate.
|
||||||
let mut result = Vec::new();
|
ext_def_map = db.crate_def_map(module.krate);
|
||||||
for (local_id, data) in def_map.modules.iter() {
|
&ext_def_map[module.local_id]
|
||||||
|
};
|
||||||
|
|
||||||
if let Some((name, vis)) = data.scope.name_of(item) {
|
if let Some((name, vis)) = data.scope.name_of(item) {
|
||||||
|
if vis.is_visible_from(db, from) {
|
||||||
let is_private = if let Visibility::Module(private_to) = vis {
|
let is_private = if let Visibility::Module(private_to) = vis {
|
||||||
private_to.local_id == local_id
|
private_to.local_id == module.local_id
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
@ -232,19 +260,29 @@ pub(crate) fn importable_locations_of_query(
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
if is_private && !is_original_def {
|
|
||||||
// Ignore private imports. these could be used if we are
|
// Ignore private imports. these could be used if we are
|
||||||
// in a submodule of this module, but that's usually not
|
// in a submodule of this module, but that's usually not
|
||||||
// what the user wants; and if this module can import
|
// what the user wants; and if this module can import
|
||||||
// the item and we're a submodule of it, so can we.
|
// the item and we're a submodule of it, so can we.
|
||||||
// Also this keeps the cached data smaller.
|
// Also this keeps the cached data smaller.
|
||||||
continue;
|
if !is_private || is_original_def {
|
||||||
|
locations.push((module, name.clone()));
|
||||||
}
|
}
|
||||||
result.push((ModuleId { krate, local_id }, name.clone(), vis));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Arc::from(result)
|
// Descend into all modules visible from `from`.
|
||||||
|
for (_, per_ns) in data.scope.entries() {
|
||||||
|
if let Some((ModuleDefId::ModuleId(module), vis)) = per_ns.take_types_vis() {
|
||||||
|
if vis.is_visible_from(db, from) {
|
||||||
|
worklist.push(module);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
locations
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -382,6 +420,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn different_crate_renamed() {
|
fn different_crate_renamed() {
|
||||||
|
// Even if a local path exists, if the item is defined externally, prefer an external path.
|
||||||
let code = r#"
|
let code = r#"
|
||||||
//- /main.rs crate:main deps:std
|
//- /main.rs crate:main deps:std
|
||||||
extern crate std as std_renamed;
|
extern crate std as std_renamed;
|
||||||
|
@ -389,7 +428,7 @@ mod tests {
|
||||||
//- /std.rs crate:std
|
//- /std.rs crate:std
|
||||||
pub struct S;
|
pub struct S;
|
||||||
"#;
|
"#;
|
||||||
check_found_path(code, "std_renamed::S");
|
check_found_path(code, "std::S");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -6,9 +6,10 @@ use once_cell::sync::Lazy;
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, ImplId, MacroDefId, ModuleDefId,
|
db::DefDatabase, per_ns::PerNs, visibility::Visibility, AdtId, BuiltinType, HasModule, ImplId,
|
||||||
TraitId,
|
Lookup, MacroDefId, ModuleDefId, TraitId,
|
||||||
};
|
};
|
||||||
|
use ra_db::CrateId;
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq, Eq)]
|
#[derive(Debug, Default, PartialEq, Eq)]
|
||||||
pub struct ItemScope {
|
pub struct ItemScope {
|
||||||
|
@ -203,4 +204,21 @@ impl ItemInNs {
|
||||||
ItemInNs::Macros(_) => None,
|
ItemInNs::Macros(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn defining_crate(&self, db: &dyn DefDatabase) -> Option<CrateId> {
|
||||||
|
Some(match self {
|
||||||
|
ItemInNs::Types(did) | ItemInNs::Values(did) => match did {
|
||||||
|
ModuleDefId::ModuleId(id) => id.krate,
|
||||||
|
ModuleDefId::FunctionId(id) => id.lookup(db).module(db).krate,
|
||||||
|
ModuleDefId::AdtId(id) => id.module(db).krate,
|
||||||
|
ModuleDefId::EnumVariantId(id) => id.parent.lookup(db).container.module(db).krate,
|
||||||
|
ModuleDefId::ConstId(id) => id.lookup(db).container.module(db).krate,
|
||||||
|
ModuleDefId::StaticId(id) => id.lookup(db).container.module(db).krate,
|
||||||
|
ModuleDefId::TraitId(id) => id.lookup(db).container.module(db).krate,
|
||||||
|
ModuleDefId::TypeAliasId(id) => id.lookup(db).module(db).krate,
|
||||||
|
ModuleDefId::BuiltinType(_) => return None,
|
||||||
|
},
|
||||||
|
ItemInNs::Macros(id) => return id.krate,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue