diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index c918ec9f6ef..c3124480b3d 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -8,8 +8,7 @@ use crate::{ Name, ScopesWithSourceMap, Ty, HirFileId, HirDatabase, PersistentHirDatabase, type_ref::TypeRef, - nameres::{ModuleScope, Namespace, lower::ImportId}, - nameres::crate_def_map::ModuleId, + nameres::{ModuleScope, Namespace, ImportId, ModuleId}, expr::{Body, BodySourceMap}, ty::InferenceResult, adt::{EnumVariantId, StructFieldId, VariantDef}, diff --git a/crates/ra_hir/src/code_model_impl/module.rs b/crates/ra_hir/src/code_model_impl/module.rs index aa4e62518a8..9f4448475c5 100644 --- a/crates/ra_hir/src/code_model_impl/module.rs +++ b/crates/ra_hir/src/code_model_impl/module.rs @@ -3,8 +3,7 @@ use ra_syntax::{ast, SyntaxNode, TreeArc, AstNode}; use crate::{ Module, ModuleSource, Problem, Name, - nameres::crate_def_map::ModuleId, - nameres::lower::ImportId, + nameres::{ModuleId, ImportId}, HirDatabase, PersistentHirDatabase, HirFileId, SourceItemId, }; diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index b8c0a68a686..d2cc19b0f92 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -10,7 +10,7 @@ use crate::{ Struct, Enum, StructField, Const, ConstSignature, Static, macros::MacroExpansion, - nameres::{Namespace, lower::{ImportSourceMap}, crate_def_map::{RawItems, CrateDefMap}}, + nameres::{Namespace, ImportSourceMap, RawItems, CrateDefMap}, ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef, CallableDef, FnSig}, adt::{StructData, EnumData}, impl_block::{ModuleImplBlocks, ImplSourceMap}, diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 2248842def7..30e959c0442 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -1,35 +1,128 @@ -//! 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 -//! 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 -//! containing only directly defined items, and then iteratively resolve -//! imports. -//! -//! To make this work nicely in the IDE scenario, we place `InputModuleItems` -//! 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 -//! structure itself is modified. -pub(crate) mod lower; -pub(crate) mod crate_def_map; +/// 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 +/// stric ordering. +/// +/// ## Collecting RawItems +/// +/// This happens in the `raw` module, which parses a single source file into a +/// set of top-level items. Nested importa are desugared to flat imports in +/// this phase. Macro calls are represented as a triple of (Path, Option, +/// 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 +/// of unresovled imports and macros. +/// +/// While we walk tree of modules, we also record macro_rules defenitions and +/// expand calls to macro_rules defined macros. +/// +/// ## Resolving Imports +/// +/// TBD +/// +/// ## Resolving Macros +/// +/// While macro_rules from the same crate use a global mutable namespace, macros +/// from other crates (including proc-macros) can be used with `foo::bar!` +/// syntax. +/// +/// TBD; + +mod per_ns; +mod raw; +mod collector; +#[cfg(test)] +mod tests; + +use std::sync::Arc; use rustc_hash::FxHashMap; -use ra_db::Edition; +use ra_arena::{Arena, RawId, impl_arena_id}; +use ra_db::{FileId, Edition}; +use test_utils::tested_by; use crate::{ - ModuleDef, Name, - nameres::lower::ImportId, + ModuleDef, Name, Crate, Module, Problem, + PersistentHirDatabase, Path, PathKind, HirFileId, + ids::{SourceItemId, SourceFileItemId}, }; -pub(crate) use self::crate_def_map::{CrateDefMap, ModuleId}; +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, + extern_prelude: FxHashMap, + root: ModuleId, + modules: Arena, + public_macros: FxHashMap, + problems: CrateDefMapProblems, +} + +impl std::ops::Index for CrateDefMap { + type Output = ModuleData; + fn index(&self, id: ModuleId) -> &ModuleData { + &self.modules[id] + } +} + +/// An ID of a module, **local** to a specific crate +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub(crate) struct ModuleId(RawId); +impl_arena_id!(ModuleId); + +#[derive(Default, Debug, PartialEq, Eq)] +pub(crate) struct ModuleData { + pub(crate) parent: Option, + pub(crate) children: FxHashMap, + pub(crate) scope: ModuleScope, + /// None for root + pub(crate) declaration: Option, + /// None for inline modules. + /// + /// Note that non-inline modules, by definition, live inside non-macro file. + pub(crate) definition: Option, +} + +#[derive(Default, Debug, PartialEq, Eq)] +pub(crate) struct CrateDefMapProblems { + problems: Vec<(SourceItemId, Problem)>, +} + +impl CrateDefMapProblems { + fn add(&mut self, source_item_id: SourceItemId, problem: Problem) { + self.problems.push((source_item_id, problem)) + } + + pub(crate) fn iter<'a>(&'a self) -> impl Iterator + 'a { + self.problems.iter().map(|(s, p)| (s, p)) + } +} #[derive(Debug, Default, PartialEq, Eq, Clone)] pub struct ModuleScope { - pub(crate) items: FxHashMap, + items: FxHashMap, } impl ModuleScope { @@ -41,8 +134,6 @@ impl ModuleScope { } } -/// `Resolution` is basically `DefId` atm, but it should account for stuff like -/// multiple namespaces, ambiguity and errors. #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct Resolution { /// None for unresolved @@ -51,85 +142,6 @@ pub struct Resolution { pub import: Option, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Namespace { - Types, - Values, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct PerNs { - pub types: Option, - pub values: Option, -} - -impl Default for PerNs { - fn default() -> Self { - PerNs { types: None, values: None } - } -} - -impl PerNs { - pub fn none() -> PerNs { - PerNs { types: None, values: None } - } - - pub fn values(t: T) -> PerNs { - PerNs { types: None, values: Some(t) } - } - - pub fn types(t: T) -> PerNs { - PerNs { types: Some(t), values: None } - } - - pub fn both(types: T, values: T) -> PerNs { - PerNs { types: Some(types), values: Some(values) } - } - - pub fn is_none(&self) -> bool { - self.types.is_none() && self.values.is_none() - } - - pub fn is_both(&self) -> bool { - self.types.is_some() && self.values.is_some() - } - - pub fn take(self, namespace: Namespace) -> Option { - match namespace { - Namespace::Types => self.types, - Namespace::Values => self.values, - } - } - - pub fn take_types(self) -> Option { - self.take(Namespace::Types) - } - - pub fn take_values(self) -> Option { - self.take(Namespace::Values) - } - - pub fn get(&self, namespace: Namespace) -> Option<&T> { - self.as_ref().take(namespace) - } - - pub fn as_ref(&self) -> PerNs<&T> { - PerNs { types: self.types.as_ref(), values: self.values.as_ref() } - } - - pub fn or(self, other: PerNs) -> PerNs { - PerNs { types: self.types.or(other.types), values: self.values.or(other.values) } - } - - pub fn and_then(self, f: impl Fn(T) -> Option) -> PerNs { - PerNs { types: self.types.and_then(&f), values: self.values.and_then(&f) } - } - - pub fn map(self, f: impl Fn(T) -> U) -> PerNs { - PerNs { types: self.types.map(&f), values: self.values.map(&f) } - } -} - #[derive(Debug, Clone)] struct ResolvePathResult { resolved_def: PerNs, @@ -162,3 +174,253 @@ enum ReachedFixedPoint { Yes, No, } + +impl CrateDefMap { + pub(crate) fn crate_def_map_query( + db: &impl PersistentHirDatabase, + krate: Crate, + ) -> Arc { + let def_map = { + let edition = krate.edition(db); + let mut modules: Arena = Arena::default(); + let root = modules.alloc(ModuleData::default()); + CrateDefMap { + krate, + edition, + extern_prelude: FxHashMap::default(), + prelude: None, + root, + modules, + public_macros: FxHashMap::default(), + problems: CrateDefMapProblems::default(), + } + }; + let def_map = collector::collect_defs(db, def_map); + Arc::new(def_map) + } + + pub(crate) fn root(&self) -> ModuleId { + self.root + } + + pub(crate) fn problems(&self) -> &CrateDefMapProblems { + &self.problems + } + + pub(crate) fn mk_module(&self, module_id: ModuleId) -> Module { + Module { krate: self.krate, module_id } + } + + pub(crate) fn prelude(&self) -> Option { + self.prelude + } + + pub(crate) fn extern_prelude(&self) -> &FxHashMap { + &self.extern_prelude + } + + pub(crate) fn find_module_by_source( + &self, + file_id: HirFileId, + decl_id: Option, + ) -> Option { + let decl_id = decl_id.map(|it| it.with_file_id(file_id)); + 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) + } + + pub(crate) fn resolve_path( + &self, + db: &impl PersistentHirDatabase, + original_module: ModuleId, + path: &Path, + ) -> (PerNs, Option) { + let res = self.resolve_path_fp(db, ResolveMode::Other, original_module, path); + (res.resolved_def, res.segment_index) + } + + // Returns Yes if we are sure that additions to `ItemMap` wouldn't change + // the result. + fn resolve_path_fp( + &self, + db: &impl PersistentHirDatabase, + mode: ResolveMode, + original_module: ModuleId, + path: &Path, + ) -> ResolvePathResult { + let mut segments = path.segments.iter().enumerate(); + let mut curr_per_ns: PerNs = 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) + // TODO there must be a nicer way to write this condition + 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 + } + } + }; + + 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 + + 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), + ); + } + + 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) + } + + fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs { + 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); + + from_crate_root.or(from_extern_prelude) + } + + pub(crate) fn resolve_name_in_module( + &self, + db: &impl PersistentHirDatabase, + module: ModuleId, + name: &Name, + ) -> PerNs { + // 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) + } + + fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { + self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)) + } + + fn resolve_in_prelude(&self, db: &impl PersistentHirDatabase, name: &Name) -> PerNs { + 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() + } + } +} diff --git a/crates/ra_hir/src/nameres/crate_def_map/collector.rs b/crates/ra_hir/src/nameres/collector.rs similarity index 99% rename from crates/ra_hir/src/nameres/crate_def_map/collector.rs rename to crates/ra_hir/src/nameres/collector.rs index 0f500ce42b4..cbe850ba449 100644 --- a/crates/ra_hir/src/nameres/crate_def_map/collector.rs +++ b/crates/ra_hir/src/nameres/collector.rs @@ -8,11 +8,11 @@ use crate::{ Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias, PersistentHirDatabase, HirFileId, Name, Path, Problem, KnownName, - nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode}, + nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode, raw}, ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId}, }; -use super::{CrateDefMap, ModuleId, ModuleData, raw}; +use super::{CrateDefMap, ModuleId, ModuleData}; pub(super) fn collect_defs( db: &impl PersistentHirDatabase, diff --git a/crates/ra_hir/src/nameres/crate_def_map.rs b/crates/ra_hir/src/nameres/crate_def_map.rs deleted file mode 100644 index cc495505356..00000000000 --- a/crates/ra_hir/src/nameres/crate_def_map.rs +++ /dev/null @@ -1,368 +0,0 @@ -/// This module implements new 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 -/// stric ordering. -/// -/// ## Collecting RawItems -/// -/// This happens in the `raw` module, which parses a single source file into a -/// set of top-level items. Nested importa are desugared to flat imports in -/// this phase. Macro calls are represented as a triple of (Path, Option, -/// 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 -/// of unresovled imports and macros. -/// -/// While we walk tree of modules, we also record macro_rules defenitions and -/// expand calls to macro_rules defined macros. -/// -/// ## Resolving Imports -/// -/// TBD -/// -/// ## Resolving Macros -/// -/// While macro_rules from the same crate use a global mutable namespace, macros -/// from other crates (including proc-macros) can be used with `foo::bar!` -/// syntax. -/// -/// TBD; - -mod raw; -mod collector; -#[cfg(test)] -mod tests; - -use rustc_hash::FxHashMap; -use test_utils::tested_by; -use ra_arena::{Arena, RawId, impl_arena_id}; -use ra_db::FileId; - -use std::sync::Arc; - -use crate::{ - Name, Module, Path, PathKind, ModuleDef, Crate, Problem, HirFileId, - PersistentHirDatabase, - nameres::{ModuleScope, ResolveMode, ResolvePathResult, PerNs, Edition, ReachedFixedPoint}, - ids::{SourceItemId, SourceFileItemId}, -}; - -pub(crate) use self::raw::RawItems; - -#[derive(Default, Debug, PartialEq, Eq)] -pub(crate) struct ModuleData { - pub(crate) parent: Option, - pub(crate) children: FxHashMap, - pub(crate) scope: ModuleScope, - /// None for root - pub(crate) declaration: Option, - /// None for inline modules. - /// - /// Note that non-inline modules, by definition, live inside non-macro file. - pub(crate) definition: Option, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub(crate) struct ModuleId(RawId); -impl_arena_id!(ModuleId); - -/// 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, - extern_prelude: FxHashMap, - root: ModuleId, - modules: Arena, - public_macros: FxHashMap, - problems: CrateDefMapProblems, -} - -#[derive(Default, Debug, PartialEq, Eq)] -pub(crate) struct CrateDefMapProblems { - problems: Vec<(SourceItemId, Problem)>, -} - -impl CrateDefMapProblems { - fn add(&mut self, source_item_id: SourceItemId, problem: Problem) { - self.problems.push((source_item_id, problem)) - } - - pub(crate) fn iter<'a>(&'a self) -> impl Iterator + 'a { - self.problems.iter().map(|(s, p)| (s, p)) - } -} - -impl std::ops::Index for CrateDefMap { - type Output = ModuleData; - fn index(&self, id: ModuleId) -> &ModuleData { - &self.modules[id] - } -} - -impl CrateDefMap { - pub(crate) fn crate_def_map_query( - db: &impl PersistentHirDatabase, - krate: Crate, - ) -> Arc { - let def_map = { - let edition = krate.edition(db); - let mut modules: Arena = Arena::default(); - let root = modules.alloc(ModuleData::default()); - CrateDefMap { - krate, - edition, - extern_prelude: FxHashMap::default(), - prelude: None, - root, - modules, - public_macros: FxHashMap::default(), - problems: CrateDefMapProblems::default(), - } - }; - let def_map = collector::collect_defs(db, def_map); - Arc::new(def_map) - } - - pub(crate) fn root(&self) -> ModuleId { - self.root - } - - pub(crate) fn problems(&self) -> &CrateDefMapProblems { - &self.problems - } - - pub(crate) fn mk_module(&self, module_id: ModuleId) -> Module { - Module { krate: self.krate, module_id } - } - - pub(crate) fn prelude(&self) -> Option { - self.prelude - } - - pub(crate) fn extern_prelude(&self) -> &FxHashMap { - &self.extern_prelude - } - - pub(crate) fn find_module_by_source( - &self, - file_id: HirFileId, - decl_id: Option, - ) -> Option { - let decl_id = decl_id.map(|it| it.with_file_id(file_id)); - 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) - } - - pub(crate) fn resolve_path( - &self, - db: &impl PersistentHirDatabase, - original_module: ModuleId, - path: &Path, - ) -> (PerNs, Option) { - let res = self.resolve_path_fp(db, ResolveMode::Other, original_module, path); - (res.resolved_def, res.segment_index) - } - - // Returns Yes if we are sure that additions to `ItemMap` wouldn't change - // the result. - fn resolve_path_fp( - &self, - db: &impl PersistentHirDatabase, - mode: ResolveMode, - original_module: ModuleId, - path: &Path, - ) -> ResolvePathResult { - let mut segments = path.segments.iter().enumerate(); - let mut curr_per_ns: PerNs = 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) - // TODO there must be a nicer way to write this condition - 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 - } - } - }; - - 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 - - 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), - ); - } - - 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) - } - - fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs { - 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); - - from_crate_root.or(from_extern_prelude) - } - - pub(crate) fn resolve_name_in_module( - &self, - db: &impl PersistentHirDatabase, - module: ModuleId, - name: &Name, - ) -> PerNs { - // 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) - } - - fn resolve_name_in_extern_prelude(&self, name: &Name) -> PerNs { - self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)) - } - - fn resolve_in_prelude(&self, db: &impl PersistentHirDatabase, name: &Name) -> PerNs { - 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() - } - } -} diff --git a/crates/ra_hir/src/nameres/lower.rs b/crates/ra_hir/src/nameres/lower.rs deleted file mode 100644 index d4c7f24814e..00000000000 --- a/crates/ra_hir/src/nameres/lower.rs +++ /dev/null @@ -1,40 +0,0 @@ -use ra_syntax::{ - AstNode, SourceFile, TreeArc, AstPtr, - ast, -}; -use ra_arena::{RawId, impl_arena_id, map::ArenaMap}; - -use crate::{Path, ModuleSource, Name}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct ImportId(RawId); -impl_arena_id!(ImportId); - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ImportData { - pub(super) path: Path, - pub(super) alias: Option, - pub(super) is_glob: bool, - pub(super) is_prelude: bool, - pub(super) is_extern_crate: bool, -} - -#[derive(Debug, Default, PartialEq, Eq)] -pub struct ImportSourceMap { - map: ArenaMap>, -} - -impl ImportSourceMap { - pub(crate) fn insert(&mut self, import: ImportId, segment: &ast::PathSegment) { - self.map.insert(import, AstPtr::new(segment)) - } - - pub fn get(&self, source: &ModuleSource, import: ImportId) -> TreeArc { - let file = match source { - ModuleSource::SourceFile(file) => &*file, - ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(), - }; - - self.map[import].to_node(file).to_owned() - } -} diff --git a/crates/ra_hir/src/nameres/per_ns.rs b/crates/ra_hir/src/nameres/per_ns.rs new file mode 100644 index 00000000000..c40a3ff9d93 --- /dev/null +++ b/crates/ra_hir/src/nameres/per_ns.rs @@ -0,0 +1,78 @@ +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum Namespace { + Types, + Values, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct PerNs { + pub types: Option, + pub values: Option, +} + +impl Default for PerNs { + fn default() -> Self { + PerNs { types: None, values: None } + } +} + +impl PerNs { + pub fn none() -> PerNs { + PerNs { types: None, values: None } + } + + pub fn values(t: T) -> PerNs { + PerNs { types: None, values: Some(t) } + } + + pub fn types(t: T) -> PerNs { + PerNs { types: Some(t), values: None } + } + + pub fn both(types: T, values: T) -> PerNs { + PerNs { types: Some(types), values: Some(values) } + } + + pub fn is_none(&self) -> bool { + self.types.is_none() && self.values.is_none() + } + + pub fn is_both(&self) -> bool { + self.types.is_some() && self.values.is_some() + } + + pub fn take(self, namespace: Namespace) -> Option { + match namespace { + Namespace::Types => self.types, + Namespace::Values => self.values, + } + } + + pub fn take_types(self) -> Option { + self.take(Namespace::Types) + } + + pub fn take_values(self) -> Option { + self.take(Namespace::Values) + } + + pub fn get(&self, namespace: Namespace) -> Option<&T> { + self.as_ref().take(namespace) + } + + pub fn as_ref(&self) -> PerNs<&T> { + PerNs { types: self.types.as_ref(), values: self.values.as_ref() } + } + + pub fn or(self, other: PerNs) -> PerNs { + PerNs { types: self.types.or(other.types), values: self.values.or(other.values) } + } + + pub fn and_then(self, f: impl Fn(T) -> Option) -> PerNs { + PerNs { types: self.types.and_then(&f), values: self.values.and_then(&f) } + } + + pub fn map(self, f: impl Fn(T) -> U) -> PerNs { + PerNs { types: self.types.map(&f), values: self.values.map(&f) } + } +} diff --git a/crates/ra_hir/src/nameres/crate_def_map/raw.rs b/crates/ra_hir/src/nameres/raw.rs similarity index 89% rename from crates/ra_hir/src/nameres/crate_def_map/raw.rs rename to crates/ra_hir/src/nameres/raw.rs index dca86e39413..3226bbf0dff 100644 --- a/crates/ra_hir/src/nameres/crate_def_map/raw.rs +++ b/crates/ra_hir/src/nameres/raw.rs @@ -5,16 +5,15 @@ use std::{ use test_utils::tested_by; use ra_db::FileId; -use ra_arena::{Arena, impl_arena_id, RawId}; +use ra_arena::{Arena, impl_arena_id, RawId, map::ArenaMap}; use ra_syntax::{ - AstNode, SourceFile, + AstNode, SourceFile, AstPtr, TreeArc, ast::{self, NameOwner, AttrsOwner}, }; use crate::{ - PersistentHirDatabase, Name, AsName, Path, HirFileId, + PersistentHirDatabase, Name, AsName, Path, HirFileId, ModuleSource, ids::{SourceFileItemId, SourceFileItems}, - nameres::lower::ImportSourceMap, }; #[derive(Debug, Default, PartialEq, Eq)] @@ -27,6 +26,26 @@ pub struct RawItems { items: Vec, } +#[derive(Debug, Default, PartialEq, Eq)] +pub struct ImportSourceMap { + map: ArenaMap>, +} + +impl ImportSourceMap { + pub(crate) fn insert(&mut self, import: ImportId, segment: &ast::PathSegment) { + self.map.insert(import, AstPtr::new(segment)) + } + + pub fn get(&self, source: &ModuleSource, import: ImportId) -> TreeArc { + let file = match source { + ModuleSource::SourceFile(file) => &*file, + ModuleSource::Module(m) => m.syntax().ancestors().find_map(SourceFile::cast).unwrap(), + }; + + self.map[import].to_node(file).to_owned() + } +} + impl RawItems { pub(crate) fn raw_items_query( db: &impl PersistentHirDatabase, @@ -113,8 +132,18 @@ pub(crate) enum ModuleData { Definition { name: Name, source_item_id: SourceFileItemId, items: Vec }, } -pub(crate) use crate::nameres::lower::ImportId; -pub(super) use crate::nameres::lower::ImportData; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ImportId(RawId); +impl_arena_id!(ImportId); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ImportData { + pub(crate) path: Path, + pub(crate) alias: Option, + pub(crate) is_glob: bool, + pub(crate) is_prelude: bool, + pub(crate) is_extern_crate: bool, +} #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub(crate) struct Def(RawId); diff --git a/crates/ra_hir/src/nameres/crate_def_map/tests.rs b/crates/ra_hir/src/nameres/tests.rs similarity index 100% rename from crates/ra_hir/src/nameres/crate_def_map/tests.rs rename to crates/ra_hir/src/nameres/tests.rs diff --git a/crates/ra_hir/src/nameres/crate_def_map/tests/globs.rs b/crates/ra_hir/src/nameres/tests/globs.rs similarity index 100% rename from crates/ra_hir/src/nameres/crate_def_map/tests/globs.rs rename to crates/ra_hir/src/nameres/tests/globs.rs diff --git a/crates/ra_hir/src/nameres/crate_def_map/tests/incremental.rs b/crates/ra_hir/src/nameres/tests/incremental.rs similarity index 100% rename from crates/ra_hir/src/nameres/crate_def_map/tests/incremental.rs rename to crates/ra_hir/src/nameres/tests/incremental.rs diff --git a/crates/ra_hir/src/nameres/crate_def_map/tests/macros.rs b/crates/ra_hir/src/nameres/tests/macros.rs similarity index 100% rename from crates/ra_hir/src/nameres/crate_def_map/tests/macros.rs rename to crates/ra_hir/src/nameres/tests/macros.rs diff --git a/crates/ra_hir/src/ty/method_resolution.rs b/crates/ra_hir/src/ty/method_resolution.rs index 7c77474b0aa..1adf9eef94e 100644 --- a/crates/ra_hir/src/ty/method_resolution.rs +++ b/crates/ra_hir/src/ty/method_resolution.rs @@ -11,7 +11,7 @@ use crate::{ ids::TraitId, impl_block::{ImplId, ImplBlock, ImplItem}, ty::{AdtDef, Ty}, - nameres::crate_def_map::ModuleId, + nameres::ModuleId, };