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

474 lines
17 KiB
Rust
Raw Normal View History

2019-03-16 14:17:50 +00:00
/// This module implements import-resolution/macro expansion algorithm.
///
/// The result of this module is `CrateDefMap`: a datastructure which contains:
///
/// * a tree of modules for the crate
/// * for each module, a set of items visible in the module (directly declared
/// or imported)
///
/// Note that `CrateDefMap` contains fully macro expanded code.
///
/// Computing `CrateDefMap` can be partitioned into several logically
/// independent "phases". The phases are mutually recursive though, there's no
2019-03-17 09:43:25 +00:00
/// strict ordering.
2019-03-16 14:17:50 +00:00
///
/// ## Collecting RawItems
///
/// This happens in the `raw` module, which parses a single source file into a
2019-03-17 09:43:25 +00:00
/// set of top-level items. Nested imports are desugared to flat imports in
2019-03-16 14:17:50 +00:00
/// this phase. Macro calls are represented as a triple of (Path, Option<Name>,
/// TokenTree).
///
/// ## Collecting Modules
///
/// This happens in the `collector` module. In this phase, we recursively walk
/// tree of modules, collect raw items from submodules, populate module scopes
/// with defined items (so, we assign item ids in this phase) and record the set
2019-03-17 09:43:25 +00:00
/// of unresolved imports and macros.
2019-03-16 14:17:50 +00:00
///
2019-03-17 09:43:25 +00:00
/// While we walk tree of modules, we also record macro_rules definitions and
2019-03-16 14:17:50 +00:00
/// expand calls to macro_rules defined macros.
///
/// ## Resolving Imports
///
2019-03-17 09:43:25 +00:00
/// We maintain a list of currently unresolved imports. On every iteration, we
/// try to resolve some imports from this list. If the import is resolved, we
/// record it, by adding an item to current module scope and, if necessary, by
/// recursively populating glob imports.
2019-03-16 14:17:50 +00:00
///
/// ## Resolving Macros
///
2019-03-17 09:43:25 +00:00
/// macro_rules from the same crate use a global mutable namespace. We expand
/// them immediately, when we collect modules.
2019-03-16 14:17:50 +00:00
///
2019-03-17 09:43:25 +00:00
/// Macros from other crates (including proc-macros) can be used with
/// `foo::bar!` syntax. We handle them similarly to imports. There's a list of
/// unexpanded macros. On every iteration, we try to resolve each macro call
/// path and, upon success, we run macro expansion and "collect module" phase
/// on the result
2019-03-16 14:17:50 +00:00
mod per_ns;
mod raw;
mod collector;
#[cfg(test)]
mod tests;
use std::sync::Arc;
2019-01-23 05:21:29 +00:00
2019-03-14 09:54:03 +00:00
use rustc_hash::FxHashMap;
2019-03-16 14:17:50 +00:00
use ra_arena::{Arena, RawId, impl_arena_id};
use ra_db::{FileId, Edition};
use test_utils::tested_by;
2018-11-28 00:42:26 +00:00
use crate::{
2019-03-26 11:54:05 +00:00
ModuleDef, Name, Crate, Module, SourceItemId,
2019-03-24 16:36:15 +00:00
DefDatabase, Path, PathKind, HirFileId, Trait,
2019-03-26 13:47:52 +00:00
ids::MacroDefId,
2019-03-23 17:41:59 +00:00
diagnostics::DiagnosticSink,
nameres::diagnostics::DefDiagnostic,
2018-11-28 00:42:26 +00:00
};
2019-03-16 14:17:50 +00:00
pub(crate) use self::raw::{RawItems, ImportId, ImportSourceMap};
pub use self::per_ns::{PerNs, Namespace};
/// Contans all top-level defs from a macro-expanded crate
#[derive(Debug, PartialEq, Eq)]
pub struct CrateDefMap {
krate: Crate,
edition: Edition,
/// The prelude module for this crate. This either comes from an import
/// marked with the `prelude_import` attribute, or (in the normal case) from
/// a dependency (`std` or `core`).
prelude: Option<Module>,
extern_prelude: FxHashMap<Name, ModuleDef>,
2019-03-16 15:57:53 +00:00
root: CrateModuleId,
modules: Arena<CrateModuleId, ModuleData>,
public_macros: FxHashMap<Name, MacroDefId>,
2019-03-23 15:35:14 +00:00
diagnostics: Vec<DefDiagnostic>,
2019-03-16 14:17:50 +00:00
}
2019-03-16 15:57:53 +00:00
impl std::ops::Index<CrateModuleId> for CrateDefMap {
2019-03-16 14:17:50 +00:00
type Output = ModuleData;
2019-03-16 15:57:53 +00:00
fn index(&self, id: CrateModuleId) -> &ModuleData {
2019-03-16 14:17:50 +00:00
&self.modules[id]
}
}
/// An ID of a module, **local** to a specific crate
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
2019-03-16 16:40:41 +00:00
pub(crate) struct CrateModuleId(RawId);
2019-03-16 15:57:53 +00:00
impl_arena_id!(CrateModuleId);
2019-03-16 14:17:50 +00:00
#[derive(Default, Debug, PartialEq, Eq)]
pub(crate) struct ModuleData {
2019-03-16 15:57:53 +00:00
pub(crate) parent: Option<CrateModuleId>,
pub(crate) children: FxHashMap<Name, CrateModuleId>,
2019-03-16 14:17:50 +00:00
pub(crate) scope: ModuleScope,
/// None for root
pub(crate) declaration: Option<SourceItemId>,
/// None for inline modules.
///
/// Note that non-inline modules, by definition, live inside non-macro file.
pub(crate) definition: Option<FileId>,
}
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-03-16 14:17:50 +00:00
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)
}
2019-03-24 16:36:15 +00:00
pub fn traits<'a>(&'a self) -> impl Iterator<Item = Trait> + 'a {
self.items.values().filter_map(|r| match r.def.take_types() {
Some(ModuleDef::Trait(t)) => Some(t),
_ => None,
})
}
2018-11-28 00:42:26 +00:00
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
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
}
2019-03-16 14:17:50 +00:00
#[derive(Debug, Clone)]
struct ResolvePathResult {
resolved_def: PerNs<ModuleDef>,
segment_index: Option<usize>,
reached_fixedpoint: ReachedFixedPoint,
}
2019-03-16 14:17:50 +00:00
impl ResolvePathResult {
fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
}
2019-03-16 14:17:50 +00:00
fn with(
resolved_def: PerNs<ModuleDef>,
reached_fixedpoint: ReachedFixedPoint,
segment_index: Option<usize>,
) -> ResolvePathResult {
ResolvePathResult { resolved_def, reached_fixedpoint, segment_index }
2019-01-27 19:50:57 +00:00
}
}
2019-03-16 14:17:50 +00:00
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ResolveMode {
Import,
Other,
}
2019-03-16 14:17:50 +00:00
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ReachedFixedPoint {
Yes,
No,
}
2019-03-16 14:17:50 +00:00
impl CrateDefMap {
pub(crate) fn crate_def_map_query(db: &impl DefDatabase, krate: Crate) -> Arc<CrateDefMap> {
2019-03-16 14:52:26 +00:00
let start = std::time::Instant::now();
2019-03-16 14:17:50 +00:00
let def_map = {
let edition = krate.edition(db);
2019-03-16 15:57:53 +00:00
let mut modules: Arena<CrateModuleId, ModuleData> = Arena::default();
2019-03-16 14:17:50 +00:00
let root = modules.alloc(ModuleData::default());
CrateDefMap {
krate,
edition,
2019-03-23 18:20:49 +00:00
extern_prelude: FxHashMap::default(),
2019-03-16 14:17:50 +00:00
prelude: None,
root,
modules,
public_macros: FxHashMap::default(),
2019-03-23 15:35:14 +00:00
diagnostics: Vec::new(),
2019-03-16 14:17:50 +00:00
}
};
let def_map = collector::collect_defs(db, def_map);
2019-03-16 14:52:26 +00:00
log::info!("crate_def_map_query: {:?}", start.elapsed());
2019-03-16 14:17:50 +00:00
Arc::new(def_map)
}
2019-03-16 15:57:53 +00:00
pub(crate) fn root(&self) -> CrateModuleId {
2019-03-16 14:17:50 +00:00
self.root
}
2019-03-16 15:57:53 +00:00
pub(crate) fn mk_module(&self, module_id: CrateModuleId) -> Module {
2019-03-16 14:17:50 +00:00
Module { krate: self.krate, module_id }
2019-01-26 21:52:04 +00:00
}
2019-03-16 14:17:50 +00:00
pub(crate) fn prelude(&self) -> Option<Module> {
self.prelude
}
2019-03-16 14:17:50 +00:00
pub(crate) fn extern_prelude(&self) -> &FxHashMap<Name, ModuleDef> {
&self.extern_prelude
}
2019-03-23 15:35:14 +00:00
pub(crate) fn add_diagnostics(
&self,
db: &impl DefDatabase,
module: CrateModuleId,
2019-03-23 17:41:59 +00:00
sink: &mut DiagnosticSink,
2019-03-23 15:35:14 +00:00
) {
self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink))
}
2019-03-16 14:17:50 +00:00
pub(crate) fn find_module_by_source(
&self,
file_id: HirFileId,
2019-03-26 11:54:05 +00:00
decl_id: Option<SourceItemId>,
2019-03-16 15:57:53 +00:00
) -> Option<CrateModuleId> {
2019-03-16 14:17:50 +00:00
let (module_id, _module_data) = self.modules.iter().find(|(_module_id, module_data)| {
if decl_id.is_some() {
module_data.declaration == decl_id
} else {
module_data.definition.map(|it| it.into()) == Some(file_id)
}
})?;
Some(module_id)
}
2018-11-28 00:42:26 +00:00
2019-03-16 14:17:50 +00:00
pub(crate) fn resolve_path(
&self,
db: &impl DefDatabase,
2019-03-16 15:57:53 +00:00
original_module: CrateModuleId,
2019-03-16 14:17:50 +00:00
path: &Path,
) -> (PerNs<ModuleDef>, Option<usize>) {
let res = self.resolve_path_fp(db, ResolveMode::Other, original_module, path);
(res.resolved_def, res.segment_index)
}
2019-03-16 14:17:50 +00:00
// Returns Yes if we are sure that additions to `ItemMap` wouldn't change
// the result.
fn resolve_path_fp(
&self,
db: &impl DefDatabase,
2019-03-16 14:17:50 +00:00
mode: ResolveMode,
2019-03-16 15:57:53 +00:00
original_module: CrateModuleId,
2019-03-16 14:17:50 +00:00
path: &Path,
) -> ResolvePathResult {
let mut segments = path.segments.iter().enumerate();
let mut curr_per_ns: PerNs<ModuleDef> = match path.kind {
PathKind::Crate => {
PerNs::types(Module { krate: self.krate, module_id: self.root }.into())
}
PathKind::Self_ => {
PerNs::types(Module { krate: self.krate, module_id: original_module }.into())
}
// plain import or absolute path in 2015: crate-relative with
// fallback to extern prelude (with the simplification in
// rust-lang/rust#57745)
2019-03-23 07:53:48 +00:00
// FIXME there must be a nicer way to write this condition
2019-03-16 14:17:50 +00:00
PathKind::Plain | PathKind::Abs
if self.edition == Edition::Edition2015
&& (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
{
let segment = match segments.next() {
Some((_, segment)) => segment,
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
};
log::debug!("resolving {:?} in crate root (+ extern prelude)", segment);
self.resolve_name_in_crate_root_or_extern_prelude(&segment.name)
}
PathKind::Plain => {
let segment = match segments.next() {
Some((_, segment)) => segment,
None => return ResolvePathResult::empty(ReachedFixedPoint::Yes),
};
log::debug!("resolving {:?} in module", segment);
self.resolve_name_in_module(db, original_module, &segment.name)
}
PathKind::Super => {
if let Some(p) = self.modules[original_module].parent {
PerNs::types(Module { krate: self.krate, module_id: p }.into())
} else {
log::debug!("super path in root module");
return ResolvePathResult::empty(ReachedFixedPoint::Yes);
}
}
PathKind::Abs => {
// 2018-style absolute path -- only extern prelude
let segment = match segments.next() {
Some((_, segment)) => segment,
None => return ResolvePathResult::empty(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 ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude
}
}
};
2019-03-16 14:17:50 +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 ResolvePathResult::empty(ReachedFixedPoint::No);
}
};
// resolve segment in curr
2019-01-26 21:52:04 +00:00
2019-03-16 14:17:50 +00:00
curr_per_ns = match curr {
ModuleDef::Module(module) => {
if module.krate != self.krate {
let path = Path {
segments: path.segments[i..].iter().cloned().collect(),
kind: PathKind::Self_,
};
log::debug!("resolving {:?} in other crate", path);
let defp_map = db.crate_def_map(module.krate);
let (def, s) = defp_map.resolve_path(db, module.module_id, &path);
return ResolvePathResult::with(
def,
ReachedFixedPoint::Yes,
s.map(|s| s + i),
);
}
2019-03-16 14:17:50 +00:00
match self[module.module_id].scope.items.get(&segment.name) {
Some(res) if !res.def.is_none() => res.def,
_ => {
log::debug!("path segment {:?} not found", segment.name);
return ResolvePathResult::empty(ReachedFixedPoint::No);
}
}
}
ModuleDef::Enum(e) => {
// enum variant
tested_by!(can_import_enum_variant);
match e.variant(db, &segment.name) {
Some(variant) => PerNs::both(variant.into(), variant.into()),
None => {
return ResolvePathResult::with(
PerNs::types((*e).into()),
ReachedFixedPoint::Yes,
Some(i),
);
}
}
}
s => {
// could be an inherent method call in UFCS form
// (`Struct::method`), or some other kind of associated item
log::debug!(
"path segment {:?} resolved to non-module {:?}, but is not last",
segment.name,
curr,
);
return ResolvePathResult::with(
PerNs::types((*s).into()),
ReachedFixedPoint::Yes,
Some(i),
);
}
};
}
ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
}
2018-11-28 00:42:26 +00:00
2019-03-16 14:17:50 +00:00
fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> {
let from_crate_root =
self[self.root].scope.items.get(name).map_or(PerNs::none(), |it| it.def);
let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
2019-03-16 14:17:50 +00:00
from_crate_root.or(from_extern_prelude)
}
2019-03-16 14:17:50 +00:00
pub(crate) fn resolve_name_in_module(
&self,
db: &impl DefDatabase,
2019-03-16 15:57:53 +00:00
module: CrateModuleId,
2019-03-16 14:17:50 +00:00
name: &Name,
) -> PerNs<ModuleDef> {
// Resolve in:
// - current module / scope
// - extern prelude
// - std prelude
let from_scope = self[module].scope.items.get(name).map_or(PerNs::none(), |it| it.def);
let from_extern_prelude =
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
let from_prelude = self.resolve_in_prelude(db, name);
from_scope.or(from_extern_prelude).or(from_prelude)
}
2019-03-16 14:17:50 +00:00
fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> {
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it))
}
2019-02-11 22:11:12 +00:00
fn resolve_in_prelude(&self, db: &impl DefDatabase, name: &Name) -> PerNs<ModuleDef> {
2019-03-16 14:17:50 +00:00
if let Some(prelude) = self.prelude {
let resolution = if prelude.krate == self.krate {
self[prelude.module_id].scope.items.get(name).cloned()
} else {
db.crate_def_map(prelude.krate)[prelude.module_id].scope.items.get(name).cloned()
};
resolution.map(|r| r.def).unwrap_or_else(PerNs::none)
} else {
PerNs::none()
}
}
}
2019-03-23 15:35:14 +00:00
2019-03-23 17:41:59 +00:00
mod diagnostics {
use relative_path::RelativePathBuf;
use ra_syntax::{AstPtr, AstNode, ast};
use crate::{
SourceItemId, DefDatabase,
nameres::CrateModuleId,
diagnostics::{DiagnosticSink, UnresolvedModule},
};
2019-03-23 15:35:14 +00:00
2019-03-23 17:41:59 +00:00
#[derive(Debug, PartialEq, Eq)]
pub(super) enum DefDiagnostic {
UnresolvedModule {
module: CrateModuleId,
declaration: SourceItemId,
candidate: RelativePathBuf,
},
}
impl DefDiagnostic {
pub(super) fn add_to(
&self,
db: &impl DefDatabase,
target_module: CrateModuleId,
sink: &mut DiagnosticSink,
) {
match self {
DefDiagnostic::UnresolvedModule { module, declaration, candidate } => {
if *module != target_module {
return;
}
let syntax = db.file_item(*declaration);
let decl = ast::Module::cast(&syntax).unwrap();
sink.push(UnresolvedModule {
file: declaration.file_id,
decl: AstPtr::new(&decl),
candidate: candidate.clone(),
})
2019-03-23 15:35:14 +00:00
}
}
}
}
2019-03-23 17:41:59 +00:00
2019-03-23 15:35:14 +00:00
}