rust-analyzer/crates/ra_hir/src/nameres.rs

492 lines
18 KiB
Rust
Raw Normal View History

//! Name resolution algorithm. The end result of the algorithm is an `ItemMap`:
//! a map which maps each module to its scope: the set of items visible in the
2018-11-28 00:42:26 +00:00
//! module. That is, we only resolve imports here, name resolution of item
//! bodies will be done in a separate step.
//!
//! Like Rustc, we use an interactive per-crate algorithm: we start with scopes
2018-11-28 00:42:26 +00:00
//! containing only directly defined items, and then iteratively resolve
//! imports.
//!
//! To make this work nicely in the IDE scenario, we place `InputModuleItems`
2018-11-28 00:42:26 +00:00
//! in between raw syntax and name resolution. `InputModuleItems` are computed
//! using only the module's syntax, and it is all directly defined items plus
//! imports. The plan is to make `InputModuleItems` independent of local
//! modifications (that is, typing inside a function should not change IMIs),
//! so that the results of name resolution can be preserved unless the module
2018-11-28 00:42:26 +00:00
//! structure itself is modified.
pub(crate) mod lower;
2019-01-23 05:21:29 +00:00
2019-01-30 19:18:17 +00:00
use std::{time, sync::Arc};
2018-11-28 00:42:26 +00:00
2019-01-25 07:29:00 +00:00
use ra_arena::map::ArenaMap;
use test_utils::tested_by;
use rustc_hash::{FxHashMap, FxHashSet};
2018-11-28 00:42:26 +00:00
use crate::{
Module, ModuleDef,
2019-01-26 21:52:04 +00:00
Path, PathKind, PersistentHirDatabase,
2019-01-27 16:23:49 +00:00
Crate, Name,
2019-01-06 14:33:27 +00:00
module_tree::{ModuleId, ModuleTree},
2019-01-25 07:29:55 +00:00
nameres::lower::{ImportId, LoweredModule, ImportData},
2018-11-28 00:42:26 +00:00
};
2019-01-19 20:23:26 +00:00
/// `ItemMap` is the result of module name resolution. It contains, for each
2018-11-28 00:42:26 +00:00
/// module, the set of visible items.
#[derive(Default, Debug, PartialEq, Eq)]
2018-11-28 01:09:44 +00:00
pub struct ItemMap {
2019-02-04 21:09:56 +00:00
pub(crate) extern_prelude: FxHashMap<Name, ModuleDef>,
2019-01-25 07:29:00 +00:00
per_module: ArenaMap<ModuleId, ModuleScope>,
}
impl std::ops::Index<ModuleId> for ItemMap {
type Output = ModuleScope;
fn index(&self, id: ModuleId) -> &ModuleScope {
&self.per_module[id]
}
2018-11-28 00:42:26 +00:00
}
#[derive(Debug, Default, PartialEq, Eq, Clone)]
2018-11-28 01:09:44 +00:00
pub struct ModuleScope {
2019-01-26 21:52:04 +00:00
pub(crate) items: FxHashMap<Name, Resolution>,
2018-11-28 00:42:26 +00:00
}
impl ModuleScope {
2018-12-27 17:07:21 +00:00
pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, &'a Resolution)> + 'a {
2018-11-28 00:42:26 +00:00
self.items.iter()
}
2018-12-27 17:07:21 +00:00
pub fn get(&self, name: &Name) -> Option<&Resolution> {
2018-11-28 00:42:26 +00:00
self.items.get(name)
}
}
/// `Resolution` is basically `DefId` atm, but it should account for stuff like
2018-11-28 00:42:26 +00:00
/// multiple namespaces, ambiguity and errors.
#[derive(Debug, Clone, PartialEq, Eq)]
2018-11-28 01:09:44 +00:00
pub struct Resolution {
2018-11-28 00:42:26 +00:00
/// None for unresolved
2019-01-25 07:16:28 +00:00
pub def: PerNs<ModuleDef>,
2019-01-23 05:21:29 +00:00
/// ident by which this is imported into local scope.
2019-01-18 13:56:02 +00:00
pub import: Option<ImportId>,
2018-11-28 00:42:26 +00:00
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Namespace {
Types,
Values,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct PerNs<T> {
pub types: Option<T>,
pub values: Option<T>,
}
2019-01-27 19:50:57 +00:00
impl<T> Default for PerNs<T> {
fn default() -> Self {
2019-02-08 11:49:43 +00:00
PerNs { types: None, values: None }
2019-01-27 19:50:57 +00:00
}
}
impl<T> PerNs<T> {
pub fn none() -> PerNs<T> {
2019-02-08 11:49:43 +00:00
PerNs { types: None, values: None }
}
pub fn values(t: T) -> PerNs<T> {
2019-02-08 11:49:43 +00:00
PerNs { types: None, values: Some(t) }
}
pub fn types(t: T) -> PerNs<T> {
2019-02-08 11:49:43 +00:00
PerNs { types: Some(t), values: None }
}
pub fn both(types: T, values: T) -> PerNs<T> {
2019-02-08 11:49:43 +00:00
PerNs { types: Some(types), values: Some(values) }
}
pub fn is_none(&self) -> bool {
self.types.is_none() && self.values.is_none()
}
2019-01-26 21:52:04 +00:00
pub fn is_both(&self) -> bool {
self.types.is_some() && self.values.is_some()
}
pub fn take(self, namespace: Namespace) -> Option<T> {
match namespace {
Namespace::Types => self.types,
Namespace::Values => self.values,
}
}
pub fn take_types(self) -> Option<T> {
2019-01-06 11:05:03 +00:00
self.take(Namespace::Types)
}
pub fn take_values(self) -> Option<T> {
2019-01-06 11:05:03 +00:00
self.take(Namespace::Values)
}
2018-11-28 00:42:26 +00:00
pub fn get(&self, namespace: Namespace) -> Option<&T> {
self.as_ref().take(namespace)
}
pub fn as_ref(&self) -> PerNs<&T> {
2019-02-08 11:49:43 +00:00
PerNs { types: self.types.as_ref(), values: self.values.as_ref() }
}
2019-01-26 21:52:04 +00:00
pub fn combine(self, other: PerNs<T>) -> PerNs<T> {
2019-02-08 11:49:43 +00:00
PerNs { types: self.types.or(other.types), values: self.values.or(other.values) }
2019-01-26 21:52:04 +00:00
}
pub fn and_then<U>(self, f: impl Fn(T) -> Option<U>) -> PerNs<U> {
2019-02-08 11:49:43 +00:00
PerNs { types: self.types.and_then(&f), values: self.values.and_then(&f) }
}
pub fn map<U>(self, f: impl Fn(T) -> U) -> PerNs<U> {
2019-02-08 11:49:43 +00:00
PerNs { types: self.types.map(&f), values: self.values.map(&f) }
}
}
2018-11-28 00:42:26 +00:00
2019-01-30 19:18:17 +00:00
struct Resolver<'a, DB> {
2018-12-09 09:24:52 +00:00
db: &'a DB,
2019-01-18 13:36:56 +00:00
input: &'a FxHashMap<ModuleId, Arc<LoweredModule>>,
2019-01-30 19:23:14 +00:00
krate: Crate,
2018-12-09 09:24:52 +00:00
module_tree: Arc<ModuleTree>,
2019-01-18 13:56:02 +00:00
processed_imports: FxHashSet<(ModuleId, ImportId)>,
2018-12-09 09:24:52 +00:00
result: ItemMap,
2018-11-28 00:42:26 +00:00
}
impl<'a, DB> Resolver<'a, DB>
where
2019-02-01 10:33:41 +00:00
DB: PersistentHirDatabase,
2018-11-28 00:42:26 +00:00
{
2019-01-30 19:18:17 +00:00
fn new(
2018-12-09 09:24:52 +00:00
db: &'a DB,
2019-01-18 13:36:56 +00:00
input: &'a FxHashMap<ModuleId, Arc<LoweredModule>>,
2019-01-30 19:23:14 +00:00
krate: Crate,
2018-12-09 09:24:52 +00:00
) -> Resolver<'a, DB> {
let module_tree = db.module_tree(krate);
2018-12-09 09:24:52 +00:00
Resolver {
2018-12-09 09:45:47 +00:00
db,
input,
krate,
2018-12-09 09:24:52 +00:00
module_tree,
processed_imports: FxHashSet::default(),
2018-12-09 09:24:52 +00:00
result: ItemMap::default(),
}
}
2019-01-15 16:15:01 +00:00
pub(crate) fn resolve(mut self) -> ItemMap {
self.populate_extern_prelude();
2018-11-28 00:42:26 +00:00
for (&module_id, items) in self.input.iter() {
2019-01-15 16:15:01 +00:00
self.populate_module(module_id, Arc::clone(items));
2018-11-28 00:42:26 +00:00
}
let mut iter = 0;
loop {
iter += 1;
if iter > 1000 {
panic!("failed to reach fixedpoint after 1000 iters")
}
let processed_imports_count = self.processed_imports.len();
for &module_id in self.input.keys() {
2019-01-15 12:45:48 +00:00
self.db.check_canceled();
2019-01-15 16:15:01 +00:00
self.resolve_imports(module_id);
}
if processed_imports_count == self.processed_imports.len() {
// no new imports resolved
break;
}
2018-11-28 00:42:26 +00:00
}
2019-01-15 16:15:01 +00:00
self.result
2018-11-28 00:42:26 +00:00
}
fn populate_extern_prelude(&mut self) {
for dep in self.krate.dependencies(self.db) {
log::debug!("crate dep {:?} -> {:?}", dep.name, dep.krate);
if let Some(module) = dep.krate.root_module(self.db) {
2019-02-08 11:49:43 +00:00
self.result.extern_prelude.insert(dep.name.clone(), module.into());
}
}
}
2019-01-18 13:36:56 +00:00
fn populate_module(&mut self, module_id: ModuleId, input: Arc<LoweredModule>) {
2018-11-28 00:42:26 +00:00
let mut module_items = ModuleScope::default();
2019-01-18 13:36:56 +00:00
for (import_id, import_data) in input.imports.iter() {
2019-02-01 23:23:59 +00:00
if let Some(last_segment) = import_data.path.segments.iter().last() {
2019-01-18 13:36:56 +00:00
if !import_data.is_glob {
2019-02-08 11:49:43 +00:00
let name =
import_data.alias.clone().unwrap_or_else(|| last_segment.name.clone());
module_items
.items
.insert(name, Resolution { def: PerNs::none(), import: Some(import_id) });
2018-11-28 00:42:26 +00:00
}
}
}
// Populate explicitly declared items, except modules
2019-01-25 07:16:28 +00:00
for (name, &def) in input.declarations.iter() {
let resolution = Resolution { def, import: None };
2019-01-23 16:49:11 +00:00
module_items.items.insert(name.clone(), resolution);
2018-11-28 00:42:26 +00:00
}
2018-12-09 10:49:54 +00:00
// Populate modules
2018-12-04 20:01:53 +00:00
for (name, module_id) in module_id.children(&self.module_tree) {
2019-02-08 11:49:43 +00:00
let module = Module { module_id, krate: self.krate };
self.add_module_item(&mut module_items, name, PerNs::types(module.into()));
2018-11-28 00:42:26 +00:00
}
self.result.per_module.insert(module_id, module_items);
2018-12-09 10:49:54 +00:00
}
2019-01-25 07:16:28 +00:00
fn add_module_item(&self, module_items: &mut ModuleScope, name: Name, def: PerNs<ModuleDef>) {
let resolution = Resolution { def, import: None };
2018-12-09 10:49:54 +00:00
module_items.items.insert(name, resolution);
2018-11-28 00:42:26 +00:00
}
2019-01-15 16:15:01 +00:00
fn resolve_imports(&mut self, module_id: ModuleId) {
2019-01-18 13:36:56 +00:00
for (import_id, import_data) in self.input[&module_id].imports.iter() {
if self.processed_imports.contains(&(module_id, import_id)) {
// already done
continue;
}
if self.resolve_import(module_id, import_id, import_data) == ReachedFixedPoint::Yes {
2019-01-18 13:36:56 +00:00
log::debug!("import {:?} resolved (or definite error)", import_id);
self.processed_imports.insert((module_id, import_id));
}
2018-11-28 00:42:26 +00:00
}
}
2019-01-18 13:36:56 +00:00
fn resolve_import(
&mut self,
module_id: ModuleId,
2019-01-18 13:56:02 +00:00
import_id: ImportId,
2019-01-18 13:36:56 +00:00
import: &ImportData,
) -> ReachedFixedPoint {
log::debug!("resolving import: {:?}", import);
2019-02-08 11:49:43 +00:00
let original_module = Module { krate: self.krate, module_id };
2019-01-25 07:16:28 +00:00
let (def, reached_fixedpoint) =
2019-02-08 11:49:43 +00:00
self.result.resolve_path_fp(self.db, original_module, &import.path);
2019-02-10 14:34:04 +00:00
if reached_fixedpoint != ReachedFixedPoint::Yes {
return reached_fixedpoint;
}
if import.is_glob {
log::debug!("glob import: {:?}", import);
match def.take_types() {
Some(ModuleDef::Module(m)) => {
if m.krate != self.krate {
tested_by!(glob_across_crates);
// glob import from other crate => we can just import everything once
let item_map = self.db.item_map(m.krate);
let scope = &item_map[m.module_id];
self.update(module_id, |items| {
// TODO: handle shadowing and visibility
items.items.extend(
scope.items.iter().map(|(name, res)| (name.clone(), res.clone())),
);
});
}
2019-02-10 14:34:04 +00:00
}
Some(ModuleDef::Enum(e)) => {
tested_by!(glob_enum);
// glob import from enum => just import all the variants
2019-02-10 14:34:04 +00:00
let variants = e.variants(self.db);
let resolutions = variants
.into_iter()
2019-02-10 14:34:04 +00:00
.filter_map(|variant| {
let res = Resolution {
def: PerNs::both(variant.into(), e.into()),
import: Some(import_id),
};
let name = variant.name(self.db)?;
Some((name, res))
})
.collect::<Vec<_>>();
self.update(module_id, |items| {
items.items.extend(resolutions);
});
}
Some(d) => {
log::debug!("glob import {:?} from non-module/enum {:?}", import, d);
}
None => {
log::debug!("glob import {:?} didn't resolve as type", import);
}
}
} else {
let last_segment = import.path.segments.last().unwrap();
2019-02-08 11:49:43 +00:00
let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone());
log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
// extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
if let Some(root_module) = self.krate.root_module(self.db) {
if import.is_extern_crate && module_id == root_module.module_id {
if let Some(def) = def.take_types() {
self.result.extern_prelude.insert(name.clone(), def);
}
}
}
self.update(module_id, |items| {
2019-02-08 11:49:43 +00:00
let res = Resolution { def, import: Some(import_id) };
2019-02-01 23:23:59 +00:00
items.items.insert(name, res);
});
}
reached_fixedpoint
}
fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
2019-01-25 07:29:00 +00:00
let module_items = self.result.per_module.get_mut(module_id).unwrap();
f(module_items)
}
}
2018-11-28 00:42:26 +00:00
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ReachedFixedPoint {
Yes,
No,
}
impl ItemMap {
2019-02-01 10:33:41 +00:00
pub(crate) fn item_map_query(db: &impl PersistentHirDatabase, krate: Crate) -> Arc<ItemMap> {
2019-01-30 19:18:17 +00:00
let start = time::Instant::now();
2019-01-30 19:23:14 +00:00
let module_tree = db.module_tree(krate);
2019-01-30 19:18:17 +00:00
let input = module_tree
.modules()
2019-02-08 11:49:43 +00:00
.map(|module_id| (module_id, db.lower_module_module(Module { krate, module_id })))
2019-01-30 19:18:17 +00:00
.collect::<FxHashMap<_, _>>();
2019-01-30 19:23:14 +00:00
let resolver = Resolver::new(db, &input, krate);
2019-01-30 19:18:17 +00:00
let res = resolver.resolve();
let elapsed = start.elapsed();
log::info!("item_map: {:?}", elapsed);
Arc::new(res)
}
2019-01-25 07:15:10 +00:00
pub(crate) fn resolve_path(
&self,
2019-02-01 10:33:41 +00:00
db: &impl PersistentHirDatabase,
2019-01-25 07:15:10 +00:00
original_module: Module,
path: &Path,
) -> PerNs<ModuleDef> {
self.resolve_path_fp(db, original_module, path).0
}
2019-01-25 07:17:50 +00:00
// Returns Yes if we are sure that additions to `ItemMap` wouldn't change
// the result.
2019-01-25 07:15:10 +00:00
fn resolve_path_fp(
&self,
2019-02-01 10:33:41 +00:00
db: &impl PersistentHirDatabase,
original_module: Module,
path: &Path,
) -> (PerNs<ModuleDef>, ReachedFixedPoint) {
let mut segments = path.segments.iter().enumerate();
let mut curr_per_ns: PerNs<ModuleDef> = match path.kind {
PathKind::Crate => PerNs::types(original_module.crate_root(db).into()),
PathKind::Self_ => PerNs::types(original_module.into()),
PathKind::Plain => {
let segment = match segments.next() {
Some((_, segment)) => segment,
None => return (PerNs::none(), ReachedFixedPoint::Yes),
};
// Resolve in:
// - current module / scope
// - extern prelude
match self[original_module.module_id].items.get(&segment.name) {
Some(res) if !res.def.is_none() => res.def,
_ => {
if let Some(def) = self.extern_prelude.get(&segment.name) {
PerNs::types(*def)
} else {
return (PerNs::none(), ReachedFixedPoint::No);
}
}
}
}
2018-11-28 00:42:26 +00:00
PathKind::Super => {
if let Some(p) = original_module.parent(db) {
PerNs::types(p.into())
} else {
log::debug!("super path in root module");
return (PerNs::none(), ReachedFixedPoint::Yes);
2018-11-28 00:42:26 +00:00
}
}
2019-01-23 05:21:29 +00:00
PathKind::Abs => {
// 2018-style absolute path -- only extern prelude
let segment = match segments.next() {
Some((_, segment)) => segment,
None => return (PerNs::none(), ReachedFixedPoint::Yes),
};
if let Some(def) = self.extern_prelude.get(&segment.name) {
log::debug!("absolute path {:?} resolved to crate {:?}", path, def);
PerNs::types(*def)
} else {
return (PerNs::none(), ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
}
2019-01-23 05:21:29 +00:00
}
};
for (i, segment) in segments {
let curr = match curr_per_ns.as_ref().take_types() {
Some(r) => r,
None => {
// we still have path segments left, but the path so far
// didn't resolve in the types namespace => no resolution
// (don't break here because curr_per_ns might contain
// something in the value namespace, and it would be wrong
// to return that)
return (PerNs::none(), ReachedFixedPoint::No);
}
2018-11-28 00:42:26 +00:00
};
// resolve segment in curr
curr_per_ns = match curr {
ModuleDef::Module(module) => {
if module.krate != original_module.krate {
let path = Path {
segments: path.segments[i..].iter().cloned().collect(),
2019-01-26 21:52:04 +00:00
kind: PathKind::Self_,
};
log::debug!("resolving {:?} in other crate", path);
2019-01-26 21:52:04 +00:00
let item_map = db.item_map(module.krate);
let def = item_map.resolve_path(db, *module, &path);
2019-01-25 07:16:28 +00:00
return (def, ReachedFixedPoint::Yes);
}
2018-11-28 00:42:26 +00:00
2019-01-25 07:29:00 +00:00
match self[module.module_id].items.get(&segment.name) {
2019-01-25 07:16:28 +00:00
Some(res) if !res.def.is_none() => res.def,
_ => {
log::debug!("path segment {:?} not found", segment.name);
return (PerNs::none(), ReachedFixedPoint::No);
2018-12-19 15:04:17 +00:00
}
}
}
ModuleDef::Enum(e) => {
// enum variant
tested_by!(item_map_enum_importing);
2019-01-25 09:41:23 +00:00
match e.variant(db, &segment.name) {
Some(variant) => PerNs::both(variant.into(), (*e).into()),
None => PerNs::none(),
}
2018-11-28 00:42:26 +00:00
}
_ => {
// could be an inherent method call in UFCS form
// (`Struct::method`), or some other kind of associated
// item... Which we currently don't handle (TODO)
log::debug!(
"path segment {:?} resolved to non-module {:?}, but is not last",
segment.name,
curr,
);
return (PerNs::none(), ReachedFixedPoint::Yes);
}
};
2018-11-28 00:42:26 +00:00
}
(curr_per_ns, ReachedFixedPoint::Yes)
2018-11-28 00:42:26 +00:00
}
}
2018-11-28 13:19:01 +00:00
#[cfg(test)]
2018-12-09 09:48:55 +00:00
mod tests;