From ba2efca2bbe5f4434f9a2522b2b94df873f3563b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 31 Oct 2019 18:45:10 +0300 Subject: Move CrateDefMap to hir_def --- crates/ra_hir/src/code_model.rs | 37 +- crates/ra_hir/src/code_model/src.rs | 4 +- crates/ra_hir/src/db.rs | 9 +- crates/ra_hir/src/diagnostics.rs | 21 +- crates/ra_hir/src/from_id.rs | 58 ++ crates/ra_hir/src/from_source.rs | 22 +- crates/ra_hir/src/lib.rs | 1 + crates/ra_hir/src/nameres.rs | 508 +-------------- crates/ra_hir/src/nameres/collector.rs | 837 ------------------------- crates/ra_hir/src/nameres/per_ns.rs | 80 --- crates/ra_hir/src/nameres/tests.rs | 21 +- crates/ra_hir/src/nameres/tests/globs.rs | 4 +- crates/ra_hir/src/nameres/tests/incremental.rs | 7 +- crates/ra_hir/src/nameres/tests/macros.rs | 10 +- crates/ra_hir/src/resolve.rs | 108 ++-- crates/ra_hir/src/source_binder.rs | 7 +- crates/ra_hir/src/ty/tests.rs | 2 +- 17 files changed, 183 insertions(+), 1553 deletions(-) create mode 100644 crates/ra_hir/src/from_id.rs delete mode 100644 crates/ra_hir/src/nameres/collector.rs delete mode 100644 crates/ra_hir/src/nameres/per_ns.rs (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 5b78bdfef..f03b59217 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -9,7 +9,7 @@ use hir_def::{ adt::VariantData, builtin_type::BuiltinType, type_ref::{Mutability, TypeRef}, - CrateModuleId, LocalEnumVariantId, LocalStructFieldId, ModuleId, + CrateModuleId, LocalEnumVariantId, LocalStructFieldId, ModuleId, UnionId, }; use hir_expand::{ diagnostics::DiagnosticSink, @@ -28,11 +28,11 @@ use crate::{ TypeAliasId, }, impl_block::ImplBlock, - nameres::{ImportId, ModuleScope, Namespace}, + nameres::{ImportId, Namespace}, resolve::{Resolver, Scope, TypeNs}, traits::TraitData, ty::{InferenceResult, TraitRef}, - Either, HasSource, Name, Ty, + Either, HasSource, Name, ScopeDef, Ty, }; /// hir::Crate describes a single crate. It's the main interface with which @@ -66,7 +66,7 @@ impl Crate { } pub fn root_module(self, db: &impl DefDatabase) -> Option { - let module_id = db.crate_def_map(self).root(); + let module_id = db.crate_def_map(self.crate_id).root(); Some(Module::new(self, module_id)) } @@ -120,7 +120,7 @@ impl Module { /// Name of this module. pub fn name(self, db: &impl DefDatabase) -> Option { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); let parent = def_map[self.id.module_id].parent?; def_map[parent].children.iter().find_map(|(name, module_id)| { if *module_id == self.id.module_id { @@ -151,20 +151,20 @@ impl Module { /// might be missing `krate`. This can happen if a module's file is not included /// in the module tree of any target in `Cargo.toml`. pub fn crate_root(self, db: &impl DefDatabase) -> Module { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); self.with_module_id(def_map.root()) } /// Finds a child module with the specified name. pub fn child(self, db: &impl HirDatabase, name: &Name) -> Option { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); let child_id = def_map[self.id.module_id].children.get(name)?; Some(self.with_module_id(*child_id)) } /// Iterates over all child modules. pub fn children(self, db: &impl DefDatabase) -> impl Iterator { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); let children = def_map[self.id.module_id] .children .iter() @@ -175,7 +175,7 @@ impl Module { /// Finds a parent module. pub fn parent(self, db: &impl DefDatabase) -> Option { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); let parent_id = def_map[self.id.module_id].parent?; Some(self.with_module_id(parent_id)) } @@ -191,12 +191,16 @@ impl Module { } /// Returns a `ModuleScope`: a set of items, visible in this module. - pub fn scope(self, db: &impl HirDatabase) -> ModuleScope { - db.crate_def_map(self.krate())[self.id.module_id].scope.clone() + pub fn scope(self, db: &impl HirDatabase) -> Vec<(Name, ScopeDef, Option)> { + db.crate_def_map(self.id.krate)[self.id.module_id] + .scope + .entries() + .map(|(name, res)| (name.clone(), res.def.into(), res.import)) + .collect() } pub fn diagnostics(self, db: &impl HirDatabase, sink: &mut DiagnosticSink) { - db.crate_def_map(self.krate()).add_diagnostics(db, self.id.module_id, sink); + db.crate_def_map(self.id.krate).add_diagnostics(db, self.id.module_id, sink); for decl in self.declarations(db) { match decl { crate::ModuleDef::Function(f) => f.diagnostics(db, sink), @@ -220,12 +224,12 @@ impl Module { } pub(crate) fn resolver(self, db: &impl DefDatabase) -> Resolver { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); Resolver::default().push_module_scope(def_map, self.id.module_id) } pub fn declarations(self, db: &impl DefDatabase) -> Vec { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); def_map[self.id.module_id] .scope .entries() @@ -233,6 +237,7 @@ impl Module { .flat_map(|per_ns| { per_ns.take_types().into_iter().chain(per_ns.take_values().into_iter()) }) + .map(ModuleDef::from) .collect() } @@ -336,12 +341,12 @@ impl Struct { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Union { - pub(crate) id: StructId, + pub(crate) id: UnionId, } impl Union { pub fn name(self, db: &impl DefDatabase) -> Option { - db.struct_data(self.id).name.clone() + db.union_data(self.id).name.clone() } pub fn module(self, db: &impl HirDatabase) -> Module { diff --git a/crates/ra_hir/src/code_model/src.rs b/crates/ra_hir/src/code_model/src.rs index bd0c3c226..6d116ee75 100644 --- a/crates/ra_hir/src/code_model/src.rs +++ b/crates/ra_hir/src/code_model/src.rs @@ -22,7 +22,7 @@ pub trait HasSource { impl Module { /// Returns a node which defines this module. That is, a file or a `mod foo {}` with items. pub fn definition_source(self, db: &(impl DefDatabase + AstDatabase)) -> Source { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); let decl_id = def_map[self.id.module_id].declaration; let file_id = def_map[self.id.module_id].definition; let ast = ModuleSource::new(db, file_id, decl_id); @@ -36,7 +36,7 @@ impl Module { self, db: &(impl DefDatabase + AstDatabase), ) -> Option> { - let def_map = db.crate_def_map(self.krate()); + let def_map = db.crate_def_map(self.id.krate); let decl = def_map[self.id.module_id].declaration?; let ast = decl.to_node(db); Some(Source { file_id: decl.file_id(), ast }) diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 89ca4e39f..f45804c7c 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -11,7 +11,7 @@ use crate::{ ids, impl_block::{ImplBlock, ImplSourceMap, ModuleImplBlocks}, lang_item::{LangItemTarget, LangItems}, - nameres::{CrateDefMap, Namespace}, + nameres::Namespace, traits::TraitData, ty::{ method_resolution::CrateImplBlocks, traits::Impl, CallableDef, FnSig, GenericPredicate, @@ -23,8 +23,8 @@ use crate::{ }; pub use hir_def::db::{ - DefDatabase2, DefDatabase2Storage, EnumDataQuery, InternDatabase, InternDatabaseStorage, - RawItemsQuery, RawItemsWithSourceMapQuery, StructDataQuery, + CrateDefMapQuery, DefDatabase2, DefDatabase2Storage, EnumDataQuery, InternDatabase, + InternDatabaseStorage, RawItemsQuery, RawItemsWithSourceMapQuery, StructDataQuery, }; pub use hir_expand::db::{ AstDatabase, AstDatabaseStorage, AstIdMapQuery, MacroArgQuery, MacroDefQuery, MacroExpandQuery, @@ -41,9 +41,6 @@ pub trait DefDatabase: HirDebugDatabase + DefDatabase2 { #[salsa::invoke(crate::traits::TraitItemsIndex::trait_items_index)] fn trait_items_index(&self, module: Module) -> crate::traits::TraitItemsIndex; - #[salsa::invoke(CrateDefMap::crate_def_map_query)] - fn crate_def_map(&self, krate: Crate) -> Arc; - #[salsa::invoke(ModuleImplBlocks::impls_in_module_with_source_map_query)] fn impls_in_module_with_source_map( &self, diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs index a33af8f46..1751e7be3 100644 --- a/crates/ra_hir/src/diagnostics.rs +++ b/crates/ra_hir/src/diagnostics.rs @@ -3,10 +3,10 @@ use std::any::Any; use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; -use relative_path::RelativePathBuf; use crate::{db::AstDatabase, HirFileId, Name, Source}; +pub use hir_def::diagnostics::UnresolvedModule; pub use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; #[derive(Debug)] @@ -29,25 +29,6 @@ impl Diagnostic for NoSuchField { } } -#[derive(Debug)] -pub struct UnresolvedModule { - pub file: HirFileId, - pub decl: AstPtr, - pub candidate: RelativePathBuf, -} - -impl Diagnostic for UnresolvedModule { - fn message(&self) -> String { - "unresolved module".to_string() - } - fn source(&self) -> Source { - Source { file_id: self.file, ast: self.decl.into() } - } - fn as_any(&self) -> &(dyn Any + Send + 'static) { - self - } -} - #[derive(Debug)] pub struct MissingFields { pub file: HirFileId, diff --git a/crates/ra_hir/src/from_id.rs b/crates/ra_hir/src/from_id.rs new file mode 100644 index 000000000..c08203bca --- /dev/null +++ b/crates/ra_hir/src/from_id.rs @@ -0,0 +1,58 @@ +use hir_def::{AdtId, EnumVariantId, ModuleDefId}; + +use crate::{Adt, EnumVariant, ModuleDef}; + +macro_rules! from_id { + ($(($id:path, $ty:path)),*) => {$( + impl From<$id> for $ty { + fn from(id: $id) -> $ty { + $ty { id } + } + } + )*} +} + +from_id![ + (hir_def::ModuleId, crate::Module), + (hir_def::StructId, crate::Struct), + (hir_def::UnionId, crate::Union), + (hir_def::EnumId, crate::Enum), + (hir_def::TypeAliasId, crate::TypeAlias), + (hir_def::TraitId, crate::Trait), + (hir_def::StaticId, crate::Static), + (hir_def::ConstId, crate::Const), + (hir_def::FunctionId, crate::Function), + (hir_expand::MacroDefId, crate::MacroDef) +]; + +impl From for Adt { + fn from(id: AdtId) -> Self { + match id { + AdtId::StructId(it) => Adt::Struct(it.into()), + AdtId::UnionId(it) => Adt::Union(it.into()), + AdtId::EnumId(it) => Adt::Enum(it.into()), + } + } +} + +impl From for EnumVariant { + fn from(id: EnumVariantId) -> Self { + EnumVariant { parent: id.parent.into(), id: id.local_id } + } +} + +impl From for ModuleDef { + fn from(id: ModuleDefId) -> Self { + match id { + ModuleDefId::ModuleId(it) => ModuleDef::Module(it.into()), + ModuleDefId::FunctionId(it) => ModuleDef::Function(it.into()), + ModuleDefId::AdtId(it) => ModuleDef::Adt(it.into()), + ModuleDefId::EnumVariantId(it) => ModuleDef::EnumVariant(it.into()), + ModuleDefId::ConstId(it) => ModuleDef::Const(it.into()), + ModuleDefId::StaticId(it) => ModuleDef::Static(it.into()), + ModuleDefId::TraitId(it) => ModuleDef::Trait(it.into()), + ModuleDefId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()), + ModuleDefId::BuiltinType(it) => ModuleDef::BuiltinType(it), + } + } +} diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs index a9de01455..9899bdbbc 100644 --- a/crates/ra_hir/src/from_source.rs +++ b/crates/ra_hir/src/from_source.rs @@ -149,14 +149,20 @@ impl Module { ModuleSource::SourceFile(_) => None, }; - db.relevant_crates(src.file_id.original_file(db)) - .iter() - .map(|&crate_id| Crate { crate_id }) - .find_map(|krate| { - let def_map = db.crate_def_map(krate); - let module_id = def_map.find_module_by_source(src.file_id, decl_id)?; - Some(Module::new(krate, module_id)) - }) + db.relevant_crates(src.file_id.original_file(db)).iter().find_map(|&crate_id| { + let def_map = db.crate_def_map(crate_id); + + let (module_id, _module_data) = + def_map.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(src.file_id) + } + })?; + + Some(Module::new(Crate { crate_id }, module_id)) + }) } } diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 0ba17e571..52bad2228 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -47,6 +47,7 @@ mod resolve; pub mod diagnostics; mod util; +mod from_id; mod code_model; pub mod from_source; diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 32a6ab474..bb775cfc9 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -47,512 +47,10 @@ //! path and, upon success, we run macro expansion and "collect module" phase //! on the result -mod per_ns; -mod collector; #[cfg(test)] mod tests; -use std::sync::Arc; - -use hir_def::{builtin_type::BuiltinType, CrateModuleId}; -use hir_expand::diagnostics::DiagnosticSink; -use once_cell::sync::Lazy; -use ra_arena::Arena; -use ra_db::{Edition, FileId}; -use ra_prof::profile; -use ra_syntax::ast; -use rustc_hash::{FxHashMap, FxHashSet}; -use test_utils::tested_by; - -use crate::{ - db::{AstDatabase, DefDatabase}, - ids::MacroDefId, - nameres::diagnostics::DefDiagnostic, - Adt, AstId, Crate, HirFileId, MacroDef, Module, ModuleDef, Name, Path, PathKind, Trait, +pub use hir_def::nameres::{ + per_ns::{Namespace, PerNs}, + raw::ImportId, }; - -pub use self::per_ns::{Namespace, PerNs}; - -pub use hir_def::nameres::raw::ImportId; - -/// Contains 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: CrateModuleId, - modules: Arena, - - /// Some macros are not well-behavior, which leads to infinite loop - /// e.g. macro_rules! foo { ($ty:ty) => { foo!($ty); } } - /// We mark it down and skip it in collector - /// - /// FIXME: - /// Right now it only handle a poison macro in a single crate, - /// such that if other crate try to call that macro, - /// the whole process will do again until it became poisoned in that crate. - /// We should handle this macro set globally - /// However, do we want to put it as a global variable? - poison_macros: FxHashSet, - - diagnostics: Vec, -} - -impl std::ops::Index for CrateDefMap { - type Output = ModuleData; - fn index(&self, id: CrateModuleId) -> &ModuleData { - &self.modules[id] - } -} - -#[derive(Default, Debug, PartialEq, Eq)] -pub 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, Default, PartialEq, Eq, Clone)] -pub struct ModuleScope { - items: FxHashMap, - /// Macros visable in current module in legacy textual scope - /// - /// For macros invoked by an unquatified identifier like `bar!()`, `legacy_macros` will be searched in first. - /// If it yields no result, then it turns to module scoped `macros`. - /// It macros with name quatified with a path like `crate::foo::bar!()`, `legacy_macros` will be skipped, - /// and only normal scoped `macros` will be searched in. - /// - /// Note that this automatically inherit macros defined textually before the definition of module itself. - /// - /// Module scoped macros will be inserted into `items` instead of here. - // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will - // be all resolved to the last one defined if shadowing happens. - legacy_macros: FxHashMap, -} - -static BUILTIN_SCOPE: Lazy> = Lazy::new(|| { - BuiltinType::ALL - .iter() - .map(|(name, ty)| { - (name.clone(), Resolution { def: PerNs::types(ty.clone().into()), import: None }) - }) - .collect() -}); - -/// Legacy macros can only be accessed through special methods like `get_legacy_macros`. -/// Other methods will only resolve values, types and module scoped macros only. -impl ModuleScope { - pub fn entries<'a>(&'a self) -> impl Iterator + 'a { - //FIXME: shadowing - self.items.iter().chain(BUILTIN_SCOPE.iter()) - } - - /// Iterate over all module scoped macros - pub fn macros<'a>(&'a self) -> impl Iterator + 'a { - self.items - .iter() - .filter_map(|(name, res)| res.def.get_macros().map(|macro_| (name, macro_))) - } - - /// Iterate over all legacy textual scoped macros visable at the end of the module - pub fn legacy_macros<'a>(&'a self) -> impl Iterator + 'a { - self.legacy_macros.iter().map(|(name, def)| (name, *def)) - } - - /// Get a name from current module scope, legacy macros are not included - pub fn get(&self, name: &Name) -> Option<&Resolution> { - self.items.get(name).or_else(|| BUILTIN_SCOPE.get(name)) - } - - pub fn traits<'a>(&'a self) -> impl Iterator + 'a { - self.items.values().filter_map(|r| match r.def.take_types() { - Some(ModuleDef::Trait(t)) => Some(t), - _ => None, - }) - } - - fn get_legacy_macro(&self, name: &Name) -> Option { - self.legacy_macros.get(name).copied() - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct Resolution { - /// None for unresolved - pub def: PerNs, - /// ident by which this is imported into local scope. - pub import: Option, -} - -impl Resolution { - pub(crate) fn from_macro(macro_: MacroDef) -> Self { - Resolution { def: PerNs::macros(macro_), import: None } - } -} - -#[derive(Debug, Clone)] -struct ResolvePathResult { - resolved_def: PerNs, - segment_index: Option, - reached_fixedpoint: ReachedFixedPoint, -} - -impl ResolvePathResult { - fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult { - ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None) - } - - fn with( - resolved_def: PerNs, - reached_fixedpoint: ReachedFixedPoint, - segment_index: Option, - ) -> ResolvePathResult { - ResolvePathResult { resolved_def, reached_fixedpoint, segment_index } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum ResolveMode { - Import, - Other, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum ReachedFixedPoint { - Yes, - No, -} - -impl CrateDefMap { - pub(crate) fn crate_def_map_query( - // Note that this doesn't have `+ AstDatabase`! - // This gurantess that `CrateDefMap` is stable across reparses. - db: &impl DefDatabase, - krate: Crate, - ) -> Arc { - let _p = profile("crate_def_map_query"); - 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, - poison_macros: FxHashSet::default(), - diagnostics: Vec::new(), - } - }; - let def_map = collector::collect_defs(db, def_map); - Arc::new(def_map) - } - - pub(crate) fn krate(&self) -> Crate { - self.krate - } - - pub(crate) fn root(&self) -> CrateModuleId { - self.root - } - - pub(crate) fn prelude(&self) -> Option { - self.prelude - } - - pub(crate) fn extern_prelude(&self) -> &FxHashMap { - &self.extern_prelude - } - - pub(crate) fn add_diagnostics( - &self, - db: &(impl DefDatabase + AstDatabase), - module: CrateModuleId, - sink: &mut DiagnosticSink, - ) { - self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink)) - } - - pub(crate) fn find_module_by_source( - &self, - file_id: HirFileId, - decl_id: Option>, - ) -> Option { - 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 DefDatabase, - original_module: CrateModuleId, - path: &Path, - ) -> (PerNs, Option) { - let res = self.resolve_path_fp_with_macro(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_with_macro( - &self, - db: &impl DefDatabase, - mode: ResolveMode, - original_module: CrateModuleId, - path: &Path, - ) -> ResolvePathResult { - let mut segments = path.segments.iter().enumerate(); - let mut curr_per_ns: PerNs = match path.kind { - PathKind::DollarCrate(crate_id) => { - let krate = Crate { crate_id }; - if krate == self.krate { - tested_by!(macro_dollar_crate_self); - PerNs::types(Module::new(self.krate, self.root).into()) - } else { - match krate.root_module(db) { - Some(module) => { - tested_by!(macro_dollar_crate_other); - PerNs::types(module.into()) - } - None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), - } - } - } - PathKind::Crate => PerNs::types(Module::new(self.krate, self.root).into()), - PathKind::Self_ => PerNs::types(Module::new(self.krate, 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) - // FIXME 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::new(self.krate, 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 - } - } - PathKind::Type(_) => { - // This is handled in `infer::infer_path_expr` - // The result returned here does not matter - return ResolvePathResult::empty(ReachedFixedPoint::Yes); - } - }; - - for (i, segment) in segments { - let curr = match curr_per_ns.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..].to_vec(), 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.id.module_id, &path); - return ResolvePathResult::with( - def, - ReachedFixedPoint::Yes, - s.map(|s| s + i), - ); - } - - // Since it is a qualified path here, it should not contains legacy macros - match self[module.id.module_id].scope.get(&segment.name) { - Some(res) => res.def, - _ => { - log::debug!("path segment {:?} not found", segment.name); - return ResolvePathResult::empty(ReachedFixedPoint::No); - } - } - } - ModuleDef::Adt(Adt::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), - 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.get(name).map_or_else(PerNs::none, |res| res.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 DefDatabase, - module: CrateModuleId, - name: &Name, - ) -> PerNs { - // Resolve in: - // - legacy scope of macro - // - current module / scope - // - extern prelude - // - std prelude - let from_legacy_macro = - self[module].scope.get_legacy_macro(name).map_or_else(PerNs::none, PerNs::macros); - let from_scope = self[module].scope.get(name).map_or_else(PerNs::none, |res| res.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_legacy_macro.or(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 DefDatabase, name: &Name) -> PerNs { - if let Some(prelude) = self.prelude { - let keep; - let def_map = if prelude.krate() == self.krate { - self - } else { - // Extend lifetime - keep = db.crate_def_map(prelude.krate()); - &keep - }; - def_map[prelude.id.module_id].scope.get(name).map_or_else(PerNs::none, |res| res.def) - } else { - PerNs::none() - } - } -} - -mod diagnostics { - use hir_expand::diagnostics::DiagnosticSink; - use ra_syntax::{ast, AstPtr}; - use relative_path::RelativePathBuf; - - use crate::{ - db::{AstDatabase, DefDatabase}, - diagnostics::UnresolvedModule, - nameres::CrateModuleId, - AstId, - }; - - #[derive(Debug, PartialEq, Eq)] - pub(super) enum DefDiagnostic { - UnresolvedModule { - module: CrateModuleId, - declaration: AstId, - candidate: RelativePathBuf, - }, - } - - impl DefDiagnostic { - pub(super) fn add_to( - &self, - db: &(impl DefDatabase + AstDatabase), - target_module: CrateModuleId, - sink: &mut DiagnosticSink, - ) { - match self { - DefDiagnostic::UnresolvedModule { module, declaration, candidate } => { - if *module != target_module { - return; - } - let decl = declaration.to_node(db); - sink.push(UnresolvedModule { - file: declaration.file_id(), - decl: AstPtr::new(&decl), - candidate: candidate.clone(), - }) - } - } - } - } -} diff --git a/crates/ra_hir/src/nameres/collector.rs b/crates/ra_hir/src/nameres/collector.rs deleted file mode 100644 index ee0a4c99f..000000000 --- a/crates/ra_hir/src/nameres/collector.rs +++ /dev/null @@ -1,837 +0,0 @@ -//! FIXME: write short doc here - -use hir_def::{ - attr::Attr, - nameres::{mod_resolution::ModDir, raw}, -}; -use hir_expand::name; -use ra_cfg::CfgOptions; -use ra_db::FileId; -use ra_syntax::{ast, SmolStr}; -use rustc_hash::FxHashMap; -use test_utils::tested_by; - -use crate::{ - db::DefDatabase, - ids::{AstItemDef, LocationCtx, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind}, - nameres::{ - diagnostics::DefDiagnostic, Crate, CrateDefMap, CrateModuleId, ModuleData, ModuleDef, - PerNs, ReachedFixedPoint, Resolution, ResolveMode, - }, - Adt, AstId, Const, Enum, Function, HirFileId, MacroDef, Module, Name, Path, PathKind, Static, - Struct, Trait, TypeAlias, Union, -}; - -pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> CrateDefMap { - // populate external prelude - for dep in def_map.krate.dependencies(db) { - log::debug!("crate dep {:?} -> {:?}", dep.name, dep.krate); - if let Some(module) = dep.krate.root_module(db) { - def_map.extern_prelude.insert(dep.name.clone(), module.into()); - } - // look for the prelude - if def_map.prelude.is_none() { - let map = db.crate_def_map(dep.krate); - if map.prelude.is_some() { - def_map.prelude = map.prelude; - } - } - } - - let crate_graph = db.crate_graph(); - let cfg_options = crate_graph.cfg_options(def_map.krate().crate_id()); - - let mut collector = DefCollector { - db, - def_map, - glob_imports: FxHashMap::default(), - unresolved_imports: Vec::new(), - unexpanded_macros: Vec::new(), - mod_dirs: FxHashMap::default(), - macro_stack_monitor: MacroStackMonitor::default(), - cfg_options, - }; - collector.collect(); - collector.finish() -} - -#[derive(Default)] -struct MacroStackMonitor { - counts: FxHashMap, - - /// Mainly use for test - validator: Option bool>>, -} - -impl MacroStackMonitor { - fn increase(&mut self, macro_def_id: MacroDefId) { - *self.counts.entry(macro_def_id).or_default() += 1; - } - - fn decrease(&mut self, macro_def_id: MacroDefId) { - *self.counts.entry(macro_def_id).or_default() -= 1; - } - - fn is_poison(&self, macro_def_id: MacroDefId) -> bool { - let cur = *self.counts.get(¯o_def_id).unwrap_or(&0); - - if let Some(validator) = &self.validator { - validator(cur) - } else { - cur > 100 - } - } -} - -/// Walks the tree of module recursively -struct DefCollector<'a, DB> { - db: &'a DB, - def_map: CrateDefMap, - glob_imports: FxHashMap>, - unresolved_imports: Vec<(CrateModuleId, raw::ImportId, raw::ImportData)>, - unexpanded_macros: Vec<(CrateModuleId, AstId, Path)>, - mod_dirs: FxHashMap, - - /// Some macro use `$tt:tt which mean we have to handle the macro perfectly - /// To prevent stack overflow, we add a deep counter here for prevent that. - macro_stack_monitor: MacroStackMonitor, - - cfg_options: &'a CfgOptions, -} - -impl DefCollector<'_, DB> -where - DB: DefDatabase, -{ - fn collect(&mut self) { - let crate_graph = self.db.crate_graph(); - let file_id = crate_graph.crate_root(self.def_map.krate.crate_id()); - let raw_items = self.db.raw_items(file_id.into()); - let module_id = self.def_map.root; - self.def_map.modules[module_id].definition = Some(file_id); - ModCollector { - def_collector: &mut *self, - module_id, - file_id: file_id.into(), - raw_items: &raw_items, - mod_dir: ModDir::root(), - } - .collect(raw_items.items()); - - // main name resolution fixed-point loop. - let mut i = 0; - loop { - self.db.check_canceled(); - match (self.resolve_imports(), self.resolve_macros()) { - (ReachedFixedPoint::Yes, ReachedFixedPoint::Yes) => break, - _ => i += 1, - } - if i == 1000 { - log::error!("name resolution is stuck"); - break; - } - } - - let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); - // show unresolved imports in completion, etc - for (module_id, import, import_data) in unresolved_imports { - self.record_resolved_import(module_id, PerNs::none(), import, &import_data) - } - } - - /// Define a macro with `macro_rules`. - /// - /// It will define the macro in legacy textual scope, and if it has `#[macro_export]`, - /// then it is also defined in the root module scope. - /// You can `use` or invoke it by `crate::macro_name` anywhere, before or after the definition. - /// - /// It is surprising that the macro will never be in the current module scope. - /// These code fails with "unresolved import/macro", - /// ```rust,compile_fail - /// mod m { macro_rules! foo { () => {} } } - /// use m::foo as bar; - /// ``` - /// - /// ```rust,compile_fail - /// macro_rules! foo { () => {} } - /// self::foo!(); - /// crate::foo!(); - /// ``` - /// - /// Well, this code compiles, bacause the plain path `foo` in `use` is searched - /// in the legacy textual scope only. - /// ```rust - /// macro_rules! foo { () => {} } - /// use foo as bar; - /// ``` - fn define_macro( - &mut self, - module_id: CrateModuleId, - name: Name, - macro_: MacroDef, - export: bool, - ) { - // Textual scoping - self.define_legacy_macro(module_id, name.clone(), macro_); - - // Module scoping - // In Rust, `#[macro_export]` macros are unconditionally visible at the - // crate root, even if the parent modules is **not** visible. - if export { - self.update(self.def_map.root, None, &[(name, Resolution::from_macro(macro_))]); - } - } - - /// Define a legacy textual scoped macro in module - /// - /// We use a map `legacy_macros` to store all legacy textual scoped macros visable per module. - /// It will clone all macros from parent legacy scope, whose definition is prior to - /// the definition of current module. - /// And also, `macro_use` on a module will import all legacy macros visable inside to - /// current legacy scope, with possible shadowing. - fn define_legacy_macro(&mut self, module_id: CrateModuleId, name: Name, macro_: MacroDef) { - // Always shadowing - self.def_map.modules[module_id].scope.legacy_macros.insert(name, macro_); - } - - /// Import macros from `#[macro_use] extern crate`. - fn import_macros_from_extern_crate( - &mut self, - current_module_id: CrateModuleId, - import: &raw::ImportData, - ) { - log::debug!( - "importing macros from extern crate: {:?} ({:?})", - import, - self.def_map.edition, - ); - - let res = self.def_map.resolve_name_in_extern_prelude( - &import - .path - .as_ident() - .expect("extern crate should have been desugared to one-element path"), - ); - - if let Some(ModuleDef::Module(m)) = res.take_types() { - tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use); - self.import_all_macros_exported(current_module_id, m.krate()); - } - } - - /// Import all exported macros from another crate - /// - /// Exported macros are just all macros in the root module scope. - /// Note that it contains not only all `#[macro_export]` macros, but also all aliases - /// created by `use` in the root module, ignoring the visibility of `use`. - fn import_all_macros_exported(&mut self, current_module_id: CrateModuleId, krate: Crate) { - let def_map = self.db.crate_def_map(krate); - for (name, def) in def_map[def_map.root].scope.macros() { - // `macro_use` only bring things into legacy scope. - self.define_legacy_macro(current_module_id, name.clone(), def); - } - } - - fn resolve_imports(&mut self) -> ReachedFixedPoint { - let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new()); - let mut resolved = Vec::new(); - imports.retain(|(module_id, import, import_data)| { - let (def, fp) = self.resolve_import(*module_id, import_data); - if fp == ReachedFixedPoint::Yes { - resolved.push((*module_id, def, *import, import_data.clone())) - } - fp == ReachedFixedPoint::No - }); - self.unresolved_imports = imports; - // Resolves imports, filling-in module scopes - let result = - if resolved.is_empty() { ReachedFixedPoint::Yes } else { ReachedFixedPoint::No }; - for (module_id, def, import, import_data) in resolved { - self.record_resolved_import(module_id, def, import, &import_data) - } - result - } - - fn resolve_import( - &self, - module_id: CrateModuleId, - import: &raw::ImportData, - ) -> (PerNs, ReachedFixedPoint) { - log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition); - if import.is_extern_crate { - let res = self.def_map.resolve_name_in_extern_prelude( - &import - .path - .as_ident() - .expect("extern crate should have been desugared to one-element path"), - ); - (res, ReachedFixedPoint::Yes) - } else { - let res = self.def_map.resolve_path_fp_with_macro( - self.db, - ResolveMode::Import, - module_id, - &import.path, - ); - - (res.resolved_def, res.reached_fixedpoint) - } - } - - fn record_resolved_import( - &mut self, - module_id: CrateModuleId, - def: PerNs, - import_id: raw::ImportId, - import: &raw::ImportData, - ) { - if import.is_glob { - log::debug!("glob import: {:?}", import); - match def.take_types() { - Some(ModuleDef::Module(m)) => { - if import.is_prelude { - tested_by!(std_prelude); - self.def_map.prelude = Some(m); - } else if m.krate() != self.def_map.krate { - tested_by!(glob_across_crates); - // glob import from other crate => we can just import everything once - let item_map = self.db.crate_def_map(m.krate()); - let scope = &item_map[m.id.module_id].scope; - - // Module scoped macros is included - let items = scope - .items - .iter() - .map(|(name, res)| (name.clone(), res.clone())) - .collect::>(); - - self.update(module_id, Some(import_id), &items); - } else { - // glob import from same crate => we do an initial - // import, and then need to propagate any further - // additions - let scope = &self.def_map[m.id.module_id].scope; - - // Module scoped macros is included - let items = scope - .items - .iter() - .map(|(name, res)| (name.clone(), res.clone())) - .collect::>(); - - self.update(module_id, Some(import_id), &items); - // record the glob import in case we add further items - self.glob_imports - .entry(m.id.module_id) - .or_default() - .push((module_id, import_id)); - } - } - Some(ModuleDef::Adt(Adt::Enum(e))) => { - tested_by!(glob_enum); - // glob import from enum => just import all the variants - let variants = e.variants(self.db); - let resolutions = variants - .into_iter() - .filter_map(|variant| { - let res = Resolution { - def: PerNs::both(variant.into(), variant.into()), - import: Some(import_id), - }; - let name = variant.name(self.db)?; - Some((name, res)) - }) - .collect::>(); - self.update(module_id, Some(import_id), &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 { - match import.path.segments.last() { - Some(last_segment) => { - 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 import.is_extern_crate && module_id == self.def_map.root { - if let Some(def) = def.take_types() { - self.def_map.extern_prelude.insert(name.clone(), def); - } - } - - let resolution = Resolution { def, import: Some(import_id) }; - self.update(module_id, Some(import_id), &[(name, resolution)]); - } - None => tested_by!(bogus_paths), - } - } - } - - fn update( - &mut self, - module_id: CrateModuleId, - import: Option, - resolutions: &[(Name, Resolution)], - ) { - self.update_recursive(module_id, import, resolutions, 0) - } - - fn update_recursive( - &mut self, - module_id: CrateModuleId, - import: Option, - resolutions: &[(Name, Resolution)], - depth: usize, - ) { - if depth > 100 { - // prevent stack overflows (but this shouldn't be possible) - panic!("infinite recursion in glob imports!"); - } - let module_items = &mut self.def_map.modules[module_id].scope; - let mut changed = false; - for (name, res) in resolutions { - let existing = module_items.items.entry(name.clone()).or_default(); - - if existing.def.types.is_none() && res.def.types.is_some() { - existing.def.types = res.def.types; - existing.import = import.or(res.import); - changed = true; - } - if existing.def.values.is_none() && res.def.values.is_some() { - existing.def.values = res.def.values; - existing.import = import.or(res.import); - changed = true; - } - if existing.def.macros.is_none() && res.def.macros.is_some() { - existing.def.macros = res.def.macros; - existing.import = import.or(res.import); - changed = true; - } - - if existing.def.is_none() - && res.def.is_none() - && existing.import.is_none() - && res.import.is_some() - { - existing.import = res.import; - } - } - - if !changed { - return; - } - let glob_imports = self - .glob_imports - .get(&module_id) - .into_iter() - .flat_map(|v| v.iter()) - .cloned() - .collect::>(); - for (glob_importing_module, glob_import) in glob_imports { - // We pass the glob import so that the tracked import in those modules is that glob import - self.update_recursive(glob_importing_module, Some(glob_import), resolutions, depth + 1); - } - } - - fn resolve_macros(&mut self) -> ReachedFixedPoint { - let mut macros = std::mem::replace(&mut self.unexpanded_macros, Vec::new()); - let mut resolved = Vec::new(); - let mut res = ReachedFixedPoint::Yes; - macros.retain(|(module_id, ast_id, path)| { - let resolved_res = self.def_map.resolve_path_fp_with_macro( - self.db, - ResolveMode::Other, - *module_id, - path, - ); - - if let Some(def) = resolved_res.resolved_def.get_macros() { - let call_id = self.db.intern_macro(MacroCallLoc { def: def.id, ast_id: *ast_id }); - resolved.push((*module_id, call_id, def.id)); - res = ReachedFixedPoint::No; - return false; - } - - true - }); - - self.unexpanded_macros = macros; - - for (module_id, macro_call_id, macro_def_id) in resolved { - self.collect_macro_expansion(module_id, macro_call_id, macro_def_id); - } - - res - } - - fn collect_macro_expansion( - &mut self, - module_id: CrateModuleId, - macro_call_id: MacroCallId, - macro_def_id: MacroDefId, - ) { - if self.def_map.poison_macros.contains(¯o_def_id) { - return; - } - - self.macro_stack_monitor.increase(macro_def_id); - - if !self.macro_stack_monitor.is_poison(macro_def_id) { - let file_id: HirFileId = macro_call_id.as_file(MacroFileKind::Items); - let raw_items = self.db.raw_items(file_id); - let mod_dir = self.mod_dirs[&module_id].clone(); - ModCollector { - def_collector: &mut *self, - file_id, - module_id, - raw_items: &raw_items, - mod_dir, - } - .collect(raw_items.items()); - } else { - log::error!("Too deep macro expansion: {:?}", macro_call_id); - self.def_map.poison_macros.insert(macro_def_id); - } - - self.macro_stack_monitor.decrease(macro_def_id); - } - - fn finish(self) -> CrateDefMap { - self.def_map - } -} - -/// Walks a single module, populating defs, imports and macros -struct ModCollector<'a, D> { - def_collector: D, - module_id: CrateModuleId, - file_id: HirFileId, - raw_items: &'a raw::RawItems, - mod_dir: ModDir, -} - -impl ModCollector<'_, &'_ mut DefCollector<'_, DB>> -where - DB: DefDatabase, -{ - fn collect(&mut self, items: &[raw::RawItem]) { - // Note: don't assert that inserted value is fresh: it's simply not true - // for macros. - self.def_collector.mod_dirs.insert(self.module_id, self.mod_dir.clone()); - - // Prelude module is always considered to be `#[macro_use]`. - if let Some(prelude_module) = self.def_collector.def_map.prelude { - if prelude_module.krate() != self.def_collector.def_map.krate { - tested_by!(prelude_is_macro_use); - self.def_collector - .import_all_macros_exported(self.module_id, prelude_module.krate()); - } - } - - // This should be processed eagerly instead of deferred to resolving. - // `#[macro_use] extern crate` is hoisted to imports macros before collecting - // any other items. - for item in items { - if self.is_cfg_enabled(item.attrs()) { - if let raw::RawItemKind::Import(import_id) = item.kind { - let import = self.raw_items[import_id].clone(); - if import.is_extern_crate && import.is_macro_use { - self.def_collector.import_macros_from_extern_crate(self.module_id, &import); - } - } - } - } - - for item in items { - if self.is_cfg_enabled(item.attrs()) { - match item.kind { - raw::RawItemKind::Module(m) => { - self.collect_module(&self.raw_items[m], item.attrs()) - } - raw::RawItemKind::Import(import_id) => self - .def_collector - .unresolved_imports - .push((self.module_id, import_id, self.raw_items[import_id].clone())), - raw::RawItemKind::Def(def) => self.define_def(&self.raw_items[def]), - raw::RawItemKind::Macro(mac) => self.collect_macro(&self.raw_items[mac]), - } - } - } - } - - fn collect_module(&mut self, module: &raw::ModuleData, attrs: &[Attr]) { - let path_attr = self.path_attr(attrs); - let is_macro_use = self.is_macro_use(attrs); - match module { - // inline module, just recurse - raw::ModuleData::Definition { name, items, ast_id } => { - let module_id = - self.push_child_module(name.clone(), AstId::new(self.file_id, *ast_id), None); - - ModCollector { - def_collector: &mut *self.def_collector, - module_id, - file_id: self.file_id, - raw_items: self.raw_items, - mod_dir: self.mod_dir.descend_into_definition(name, path_attr), - } - .collect(&*items); - if is_macro_use { - self.import_all_legacy_macros(module_id); - } - } - // out of line module, resolve, parse and recurse - raw::ModuleData::Declaration { name, ast_id } => { - let ast_id = AstId::new(self.file_id, *ast_id); - match self.mod_dir.resolve_declaration( - self.def_collector.db, - self.file_id, - name, - path_attr, - ) { - Ok((file_id, mod_dir)) => { - let module_id = self.push_child_module(name.clone(), ast_id, Some(file_id)); - let raw_items = self.def_collector.db.raw_items(file_id.into()); - ModCollector { - def_collector: &mut *self.def_collector, - module_id, - file_id: file_id.into(), - raw_items: &raw_items, - mod_dir, - } - .collect(raw_items.items()); - if is_macro_use { - self.import_all_legacy_macros(module_id); - } - } - Err(candidate) => self.def_collector.def_map.diagnostics.push( - DefDiagnostic::UnresolvedModule { - module: self.module_id, - declaration: ast_id, - candidate, - }, - ), - }; - } - } - } - - fn push_child_module( - &mut self, - name: Name, - declaration: AstId, - definition: Option, - ) -> CrateModuleId { - let modules = &mut self.def_collector.def_map.modules; - let res = modules.alloc(ModuleData::default()); - modules[res].parent = Some(self.module_id); - modules[res].declaration = Some(declaration); - modules[res].definition = definition; - modules[res].scope.legacy_macros = modules[self.module_id].scope.legacy_macros.clone(); - modules[self.module_id].children.insert(name.clone(), res); - let resolution = Resolution { - def: PerNs::types(Module::new(self.def_collector.def_map.krate, res).into()), - import: None, - }; - self.def_collector.update(self.module_id, None, &[(name, resolution)]); - res - } - - fn define_def(&mut self, def: &raw::DefData) { - let module = Module::new(self.def_collector.def_map.krate, self.module_id); - let ctx = LocationCtx::new(self.def_collector.db, module.id, self.file_id); - - macro_rules! def { - ($kind:ident, $ast_id:ident) => { - $kind { id: AstItemDef::from_ast_id(ctx, $ast_id) }.into() - }; - } - let name = def.name.clone(); - let def: PerNs = match def.kind { - raw::DefKind::Function(ast_id) => PerNs::values(def!(Function, ast_id)), - raw::DefKind::Struct(ast_id) => { - let s = def!(Struct, ast_id); - PerNs::both(s, s) - } - raw::DefKind::Union(ast_id) => { - let s = def!(Union, ast_id); - PerNs::both(s, s) - } - raw::DefKind::Enum(ast_id) => PerNs::types(def!(Enum, ast_id)), - raw::DefKind::Const(ast_id) => PerNs::values(def!(Const, ast_id)), - raw::DefKind::Static(ast_id) => PerNs::values(def!(Static, ast_id)), - raw::DefKind::Trait(ast_id) => PerNs::types(def!(Trait, ast_id)), - raw::DefKind::TypeAlias(ast_id) => PerNs::types(def!(TypeAlias, ast_id)), - }; - let resolution = Resolution { def, import: None }; - self.def_collector.update(self.module_id, None, &[(name, resolution)]) - } - - fn collect_macro(&mut self, mac: &raw::MacroData) { - let ast_id = AstId::new(self.file_id, mac.ast_id); - - // Case 1: macro rules, define a macro in crate-global mutable scope - if is_macro_rules(&mac.path) { - if let Some(name) = &mac.name { - let macro_id = - MacroDefId { ast_id, krate: self.def_collector.def_map.krate.crate_id }; - let macro_ = MacroDef { id: macro_id }; - self.def_collector.define_macro(self.module_id, name.clone(), macro_, mac.export); - } - return; - } - - // Case 2: try to resolve in legacy scope and expand macro_rules, triggering - // recursive item collection. - if let Some(macro_def) = mac.path.as_ident().and_then(|name| { - self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) - }) { - let def = macro_def.id; - let macro_call_id = self.def_collector.db.intern_macro(MacroCallLoc { def, ast_id }); - - self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, def); - return; - } - - // Case 3: resolve in module scope, expand during name resolution. - // We rewrite simple path `macro_name` to `self::macro_name` to force resolve in module scope only. - let mut path = mac.path.clone(); - if path.is_ident() { - path.kind = PathKind::Self_; - } - self.def_collector.unexpanded_macros.push((self.module_id, ast_id, path)); - } - - fn import_all_legacy_macros(&mut self, module_id: CrateModuleId) { - let macros = self.def_collector.def_map[module_id].scope.legacy_macros.clone(); - for (name, macro_) in macros { - self.def_collector.define_legacy_macro(self.module_id, name.clone(), macro_); - } - } - - fn is_cfg_enabled(&self, attrs: &[Attr]) -> bool { - attrs.iter().all(|attr| attr.is_cfg_enabled(&self.def_collector.cfg_options) != Some(false)) - } - - fn path_attr<'a>(&self, attrs: &'a [Attr]) -> Option<&'a SmolStr> { - attrs.iter().find_map(|attr| attr.as_path()) - } - - fn is_macro_use<'a>(&self, attrs: &'a [Attr]) -> bool { - attrs.iter().any(|attr| attr.is_simple_atom("macro_use")) - } -} - -fn is_macro_rules(path: &Path) -> bool { - path.as_ident() == Some(&name::MACRO_RULES) -} - -#[cfg(test)] -mod tests { - use ra_db::SourceDatabase; - - use super::*; - use crate::{db::DefDatabase, mock::MockDatabase, Crate}; - use ra_arena::Arena; - use rustc_hash::FxHashSet; - - fn do_collect_defs( - db: &impl DefDatabase, - def_map: CrateDefMap, - monitor: MacroStackMonitor, - ) -> CrateDefMap { - let mut collector = DefCollector { - db, - def_map, - glob_imports: FxHashMap::default(), - unresolved_imports: Vec::new(), - unexpanded_macros: Vec::new(), - mod_dirs: FxHashMap::default(), - macro_stack_monitor: monitor, - cfg_options: &CfgOptions::default(), - }; - collector.collect(); - collector.finish() - } - - fn do_limited_resolve(code: &str, limit: u32, poison_limit: u32) -> CrateDefMap { - let (db, _source_root, _) = MockDatabase::with_single_file(&code); - let crate_id = db.crate_graph().iter().next().unwrap(); - let krate = Crate { crate_id }; - - 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, - poison_macros: FxHashSet::default(), - diagnostics: Vec::new(), - } - }; - - let mut monitor = MacroStackMonitor::default(); - monitor.validator = Some(Box::new(move |count| { - assert!(count < limit); - count >= poison_limit - })); - - do_collect_defs(&db, def_map, monitor) - } - - #[test] - fn test_macro_expand_limit_width() { - do_limited_resolve( - r#" - macro_rules! foo { - ($($ty:ty)*) => { foo!($($ty)*, $($ty)*); } - } -foo!(KABOOM); - "#, - 16, - 1000, - ); - } - - #[test] - fn test_macro_expand_poisoned() { - let def = do_limited_resolve( - r#" - macro_rules! foo { - ($ty:ty) => { foo!($ty); } - } -foo!(KABOOM); - "#, - 100, - 16, - ); - - assert_eq!(def.poison_macros.len(), 1); - } - - #[test] - fn test_macro_expand_normal() { - let def = do_limited_resolve( - r#" - macro_rules! foo { - ($ident:ident) => { struct $ident {} } - } -foo!(Bar); - "#, - 16, - 16, - ); - - assert_eq!(def.poison_macros.len(), 0); - } -} diff --git a/crates/ra_hir/src/nameres/per_ns.rs b/crates/ra_hir/src/nameres/per_ns.rs deleted file mode 100644 index 0da6789de..000000000 --- a/crates/ra_hir/src/nameres/per_ns.rs +++ /dev/null @@ -1,80 +0,0 @@ -//! FIXME: write short doc here - -use crate::{MacroDef, ModuleDef}; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Namespace { - Types, - Values, - // Note that only type inference uses this enum, and it doesn't care about macros. - // Macro, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct PerNs { - pub types: Option, - pub values: Option, - /// Since macros has different type, many methods simply ignore it. - /// We can only use special method like `get_macros` to access it. - pub macros: Option, -} - -impl Default for PerNs { - fn default() -> Self { - PerNs { types: None, values: None, macros: None } - } -} - -impl PerNs { - pub fn none() -> PerNs { - PerNs { types: None, values: None, macros: None } - } - - pub fn values(t: ModuleDef) -> PerNs { - PerNs { types: None, values: Some(t), macros: None } - } - - pub fn types(t: ModuleDef) -> PerNs { - PerNs { types: Some(t), values: None, macros: None } - } - - pub fn both(types: ModuleDef, values: ModuleDef) -> PerNs { - PerNs { types: Some(types), values: Some(values), macros: None } - } - - pub fn macros(macro_: MacroDef) -> PerNs { - PerNs { types: None, values: None, macros: Some(macro_) } - } - - pub fn is_none(&self) -> bool { - self.types.is_none() && self.values.is_none() && self.macros.is_none() - } - - pub fn is_all(&self) -> bool { - self.types.is_some() && self.values.is_some() && self.macros.is_some() - } - - pub fn take_types(self) -> Option { - self.types - } - - pub fn take_values(self) -> Option { - self.values - } - - pub fn get_macros(&self) -> Option { - self.macros - } - - pub fn only_macros(&self) -> PerNs { - PerNs { types: None, values: None, macros: self.macros } - } - - pub fn or(self, other: PerNs) -> PerNs { - PerNs { - types: self.types.or(other.types), - values: self.values.or(other.values), - macros: self.macros.or(other.macros), - } - } -} diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index 8c6b40aaf..02db91a86 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs @@ -6,30 +6,25 @@ mod mod_resolution; use std::sync::Arc; +use hir_def::{db::DefDatabase2, nameres::*, CrateModuleId}; use insta::assert_snapshot; use ra_db::SourceDatabase; -use test_utils::covers; +// use test_utils::covers; -use crate::{ - mock::{CrateGraphFixture, MockDatabase}, - Crate, -}; - -use super::*; +use crate::mock::{CrateGraphFixture, MockDatabase}; fn compute_crate_def_map(fixture: &str, graph: Option) -> Arc { let mut db = MockDatabase::with_files(fixture); if let Some(graph) = graph { db.set_crate_graph_from_fixture(graph); } - let crate_id = db.crate_graph().iter().next().unwrap(); - let krate = Crate { crate_id }; + let krate = db.crate_graph().iter().next().unwrap(); db.crate_def_map(krate) } fn render_crate_def_map(map: &CrateDefMap) -> String { let mut buf = String::new(); - go(&mut buf, map, "\ncrate", map.root); + go(&mut buf, map, "\ncrate", map.root()); return buf.trim().to_string(); fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: CrateModuleId) { @@ -118,7 +113,7 @@ fn crate_def_map_smoke_test() { #[test] fn bogus_paths() { - covers!(bogus_paths); + // covers!(bogus_paths); let map = def_map( " //- /lib.rs @@ -233,7 +228,7 @@ fn re_exports() { #[test] fn std_prelude() { - covers!(std_prelude); + // covers!(std_prelude); let map = def_map_with_crate_graph( " //- /main.rs @@ -261,7 +256,7 @@ fn std_prelude() { #[test] fn can_import_enum_variant() { - covers!(can_import_enum_variant); + // covers!(can_import_enum_variant); let map = def_map( " //- /lib.rs diff --git a/crates/ra_hir/src/nameres/tests/globs.rs b/crates/ra_hir/src/nameres/tests/globs.rs index 7ac22b47b..b3e4d8d94 100644 --- a/crates/ra_hir/src/nameres/tests/globs.rs +++ b/crates/ra_hir/src/nameres/tests/globs.rs @@ -75,7 +75,7 @@ fn glob_2() { #[test] fn glob_across_crates() { - covers!(glob_across_crates); + // covers!(glob_across_crates); let map = def_map_with_crate_graph( " //- /main.rs @@ -98,7 +98,7 @@ fn glob_across_crates() { #[test] fn glob_enum() { - covers!(glob_enum); + // covers!(glob_enum); let map = def_map( " //- /lib.rs diff --git a/crates/ra_hir/src/nameres/tests/incremental.rs b/crates/ra_hir/src/nameres/tests/incremental.rs index af9c39760..723ece7b0 100644 --- a/crates/ra_hir/src/nameres/tests/incremental.rs +++ b/crates/ra_hir/src/nameres/tests/incremental.rs @@ -1,13 +1,12 @@ -use super::*; - use std::sync::Arc; use ra_db::{SourceDatabase, SourceDatabaseExt}; +use super::*; + fn check_def_map_is_not_recomputed(initial: &str, file_change: &str) { let (mut db, pos) = MockDatabase::with_position(initial); - let crate_id = db.crate_graph().iter().next().unwrap(); - let krate = Crate { crate_id }; + let krate = db.crate_graph().iter().next().unwrap(); { let events = db.log_executed(|| { db.crate_def_map(krate); diff --git a/crates/ra_hir/src/nameres/tests/macros.rs b/crates/ra_hir/src/nameres/tests/macros.rs index 4f52ad2c5..78bb0eb0d 100644 --- a/crates/ra_hir/src/nameres/tests/macros.rs +++ b/crates/ra_hir/src/nameres/tests/macros.rs @@ -187,7 +187,7 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() { #[test] fn macro_rules_from_other_crates_are_visible_with_macro_use() { - covers!(macro_rules_from_other_crates_are_visible_with_macro_use); + // covers!(macro_rules_from_other_crates_are_visible_with_macro_use); let map = def_map_with_crate_graph( " //- /main.rs @@ -241,7 +241,7 @@ fn macro_rules_from_other_crates_are_visible_with_macro_use() { #[test] fn prelude_is_macro_use() { - covers!(prelude_is_macro_use); + // covers!(prelude_is_macro_use); let map = def_map_with_crate_graph( " //- /main.rs @@ -531,8 +531,8 @@ fn path_qualified_macros() { #[test] fn macro_dollar_crate_is_correct_in_item() { - covers!(macro_dollar_crate_self); - covers!(macro_dollar_crate_other); + // covers!(macro_dollar_crate_self); + // covers!(macro_dollar_crate_other); let map = def_map_with_crate_graph( " //- /main.rs @@ -594,7 +594,7 @@ fn macro_dollar_crate_is_correct_in_item() { #[test] fn macro_dollar_crate_is_correct_in_indirect_deps() { - covers!(macro_dollar_crate_other); + // covers!(macro_dollar_crate_other); // From std let map = def_map_with_crate_graph( r#" diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index 75b24d386..3e3f8c252 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs @@ -3,8 +3,9 @@ use std::sync::Arc; use hir_def::{ builtin_type::BuiltinType, + nameres::CrateDefMap, path::{Path, PathKind}, - CrateModuleId, + AdtId, CrateModuleId, ModuleDefId, }; use hir_expand::name::{self, Name}; use rustc_hash::FxHashSet; @@ -18,7 +19,7 @@ use crate::{ }, generics::GenericParams, impl_block::ImplBlock, - nameres::{CrateDefMap, PerNs}, + nameres::PerNs, Adt, Const, Enum, EnumVariant, Function, MacroDef, ModuleDef, Static, Struct, Trait, TypeAlias, }; @@ -90,7 +91,7 @@ impl Resolver { pub(crate) fn resolve_known_trait(&self, db: &impl HirDatabase, path: &Path) -> Option { let res = self.resolve_module_path(db, path).take_types()?; match res { - ModuleDef::Trait(it) => Some(it), + ModuleDefId::TraitId(it) => Some(it.into()), _ => None, } } @@ -103,7 +104,7 @@ impl Resolver { ) -> Option { let res = self.resolve_module_path(db, path).take_types()?; match res { - ModuleDef::Adt(Adt::Struct(it)) => Some(it), + ModuleDefId::AdtId(AdtId::StructId(it)) => Some(it.into()), _ => None, } } @@ -112,7 +113,7 @@ impl Resolver { pub(crate) fn resolve_known_enum(&self, db: &impl HirDatabase, path: &Path) -> Option { let res = self.resolve_module_path(db, path).take_types()?; match res { - ModuleDef::Adt(Adt::Enum(it)) => Some(it), + ModuleDefId::AdtId(AdtId::EnumId(it)) => Some(it.into()), _ => None, } } @@ -166,18 +167,18 @@ impl Resolver { Scope::ModuleScope(m) => { let (module_def, idx) = m.crate_def_map.resolve_path(db, m.module_id, path); let res = match module_def.take_types()? { - ModuleDef::Adt(it) => TypeNs::Adt(it), - ModuleDef::EnumVariant(it) => TypeNs::EnumVariant(it), + ModuleDefId::AdtId(it) => TypeNs::Adt(it.into()), + ModuleDefId::EnumVariantId(it) => TypeNs::EnumVariant(it.into()), - ModuleDef::TypeAlias(it) => TypeNs::TypeAlias(it), - ModuleDef::BuiltinType(it) => TypeNs::BuiltinType(it), + ModuleDefId::TypeAliasId(it) => TypeNs::TypeAlias(it.into()), + ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it), - ModuleDef::Trait(it) => TypeNs::Trait(it), + ModuleDefId::TraitId(it) => TypeNs::Trait(it.into()), - ModuleDef::Function(_) - | ModuleDef::Const(_) - | ModuleDef::Static(_) - | ModuleDef::Module(_) => return None, + ModuleDefId::FunctionId(_) + | ModuleDefId::ConstId(_) + | ModuleDefId::StaticId(_) + | ModuleDefId::ModuleId(_) => return None, }; return Some((res, idx)); } @@ -261,33 +262,35 @@ impl Resolver { return match idx { None => { let value = match module_def.take_values()? { - ModuleDef::Function(it) => ValueNs::Function(it), - ModuleDef::Adt(Adt::Struct(it)) => ValueNs::Struct(it), - ModuleDef::EnumVariant(it) => ValueNs::EnumVariant(it), - ModuleDef::Const(it) => ValueNs::Const(it), - ModuleDef::Static(it) => ValueNs::Static(it), - - ModuleDef::Adt(Adt::Enum(_)) - | ModuleDef::Adt(Adt::Union(_)) - | ModuleDef::Trait(_) - | ModuleDef::TypeAlias(_) - | ModuleDef::BuiltinType(_) - | ModuleDef::Module(_) => return None, + ModuleDefId::FunctionId(it) => ValueNs::Function(it.into()), + ModuleDefId::AdtId(AdtId::StructId(it)) => { + ValueNs::Struct(it.into()) + } + ModuleDefId::EnumVariantId(it) => ValueNs::EnumVariant(it.into()), + ModuleDefId::ConstId(it) => ValueNs::Const(it.into()), + ModuleDefId::StaticId(it) => ValueNs::Static(it.into()), + + ModuleDefId::AdtId(AdtId::EnumId(_)) + | ModuleDefId::AdtId(AdtId::UnionId(_)) + | ModuleDefId::TraitId(_) + | ModuleDefId::TypeAliasId(_) + | ModuleDefId::BuiltinType(_) + | ModuleDefId::ModuleId(_) => return None, }; Some(ResolveValueResult::ValueNs(value)) } Some(idx) => { let ty = match module_def.take_types()? { - ModuleDef::Adt(it) => TypeNs::Adt(it), - ModuleDef::Trait(it) => TypeNs::Trait(it), - ModuleDef::TypeAlias(it) => TypeNs::TypeAlias(it), - ModuleDef::BuiltinType(it) => TypeNs::BuiltinType(it), - - ModuleDef::Module(_) - | ModuleDef::Function(_) - | ModuleDef::EnumVariant(_) - | ModuleDef::Const(_) - | ModuleDef::Static(_) => return None, + ModuleDefId::AdtId(it) => TypeNs::Adt(it.into()), + ModuleDefId::TraitId(it) => TypeNs::Trait(it.into()), + ModuleDefId::TypeAliasId(it) => TypeNs::TypeAlias(it.into()), + ModuleDefId::BuiltinType(it) => TypeNs::BuiltinType(it), + + ModuleDefId::ModuleId(_) + | ModuleDefId::FunctionId(_) + | ModuleDefId::EnumVariantId(_) + | ModuleDefId::ConstId(_) + | ModuleDefId::StaticId(_) => return None, }; Some(ResolveValueResult::Partial(ty, idx)) } @@ -315,7 +318,7 @@ impl Resolver { path: &Path, ) -> Option { let (item_map, module) = self.module()?; - item_map.resolve_path(db, module, path).0.get_macros() + item_map.resolve_path(db, module, path).0.get_macros().map(MacroDef::from) } pub(crate) fn process_all_names( @@ -333,10 +336,11 @@ impl Resolver { for scope in &self.scopes { if let Scope::ModuleScope(m) = scope { if let Some(prelude) = m.crate_def_map.prelude() { - let prelude_def_map = db.crate_def_map(prelude.krate()); - traits.extend(prelude_def_map[prelude.id.module_id].scope.traits()); + let prelude_def_map = db.crate_def_map(prelude.krate); + traits + .extend(prelude_def_map[prelude.module_id].scope.traits().map(Trait::from)); } - traits.extend(m.crate_def_map[m.module_id].scope.traits()); + traits.extend(m.crate_def_map[m.module_id].scope.traits().map(Trait::from)); } } traits @@ -351,7 +355,7 @@ impl Resolver { } pub(crate) fn krate(&self) -> Option { - self.module().map(|t| t.0.krate()) + self.module().map(|t| Crate { crate_id: t.0.krate() }) } pub(crate) fn where_predicates_in_scope<'a>( @@ -420,8 +424,10 @@ impl From for ScopeDef { fn from(def: PerNs) -> Self { def.take_types() .or_else(|| def.take_values()) - .map(ScopeDef::ModuleDef) - .or_else(|| def.get_macros().map(ScopeDef::MacroDef)) + .map(|module_def_id| ScopeDef::ModuleDef(module_def_id.into())) + .or_else(|| { + def.get_macros().map(|macro_def_id| ScopeDef::MacroDef(macro_def_id.into())) + }) .unwrap_or(ScopeDef::Unknown) } } @@ -441,18 +447,16 @@ impl Scope { f(name.clone(), res.def.into()); }); m.crate_def_map[m.module_id].scope.legacy_macros().for_each(|(name, macro_)| { - f(name.clone(), ScopeDef::MacroDef(macro_)); + f(name.clone(), ScopeDef::MacroDef(macro_.into())); }); - m.crate_def_map.extern_prelude().iter().for_each(|(name, def)| { - f(name.clone(), ScopeDef::ModuleDef(*def)); + m.crate_def_map.extern_prelude().iter().for_each(|(name, &def)| { + f(name.clone(), ScopeDef::ModuleDef(def.into())); }); if let Some(prelude) = m.crate_def_map.prelude() { - let prelude_def_map = db.crate_def_map(prelude.krate()); - prelude_def_map[prelude.id.module_id].scope.entries().for_each( - |(name, res)| { - f(name.clone(), res.def.into()); - }, - ); + let prelude_def_map = db.crate_def_map(prelude.krate); + prelude_def_map[prelude.module_id].scope.entries().for_each(|(name, res)| { + f(name.clone(), res.def.into()); + }); } } Scope::GenericParams(gp) => { diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index a4ca59bba..66cb4b357 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -253,8 +253,11 @@ impl SourceAnalyzer { Some(res) }); - let items = - self.resolver.resolve_module_path(db, &path).take_types().map(PathResolution::Def); + let items = self + .resolver + .resolve_module_path(db, &path) + .take_types() + .map(|it| PathResolution::Def(it.into())); types.or(values).or(items).or_else(|| { self.resolver.resolve_path_as_macro(db, &path).map(|def| PathResolution::Macro(def)) }) diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs index bfef48b16..f27155737 100644 --- a/crates/ra_hir/src/ty/tests.rs +++ b/crates/ra_hir/src/ty/tests.rs @@ -3403,7 +3403,7 @@ fn test() { S.foo()<|>; } #[test] fn infer_macro_with_dollar_crate_is_correct_in_expr() { - covers!(macro_dollar_crate_other); + // covers!(macro_dollar_crate_other); let (mut db, pos) = MockDatabase::with_position( r#" //- /main.rs -- cgit v1.2.3