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 +- crates/ra_hir_def/src/adt.rs | 14 +- crates/ra_hir_def/src/db.rs | 15 +- crates/ra_hir_def/src/diagnostics.rs | 26 + crates/ra_hir_def/src/lib.rs | 5 +- crates/ra_hir_def/src/nameres.rs | 488 +++++++++++++ crates/ra_hir_def/src/nameres/collector.rs | 845 ++++++++++++++++++++++ crates/ra_hir_def/src/nameres/per_ns.rs | 82 +++ crates/ra_ide_api/src/completion/complete_path.rs | 8 +- 25 files changed, 1656 insertions(+), 1563 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 create mode 100644 crates/ra_hir_def/src/diagnostics.rs create mode 100644 crates/ra_hir_def/src/nameres/collector.rs create mode 100644 crates/ra_hir_def/src/nameres/per_ns.rs (limited to 'crates') 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 diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index 22bd469f0..8f41e55d2 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs @@ -8,7 +8,7 @@ use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner}; use crate::{ db::DefDatabase2, type_ref::TypeRef, AstItemDef, EnumId, LocalEnumVariantId, - LocalStructFieldId, StructId, + LocalStructFieldId, StructId, UnionId, }; /// Note that we use `StructData` for unions as well! @@ -56,6 +56,13 @@ impl StructData { let variant_data = Arc::new(variant_data); Arc::new(StructData { name, variant_data }) } + pub(crate) fn union_data_query(db: &impl DefDatabase2, struct_: UnionId) -> Arc { + let src = struct_.source(db); + let name = src.ast.name().map(|n| n.as_name()); + let variant_data = VariantData::new(src.ast.kind()); + let variant_data = Arc::new(variant_data); + Arc::new(StructData { name, variant_data }) + } } impl EnumData { @@ -74,6 +81,11 @@ impl EnumData { .collect(); Arc::new(EnumData { name, variants }) } + + pub(crate) fn variant(&self, name: &Name) -> Option { + let (id, _) = self.variants.iter().find(|(_id, data)| data.name.as_ref() == Some(name))?; + Some(id) + } } impl VariantData { diff --git a/crates/ra_hir_def/src/db.rs b/crates/ra_hir_def/src/db.rs index f6027013f..a42348101 100644 --- a/crates/ra_hir_def/src/db.rs +++ b/crates/ra_hir_def/src/db.rs @@ -2,13 +2,16 @@ use std::sync::Arc; use hir_expand::{db::AstDatabase, HirFileId}; -use ra_db::{salsa, SourceDatabase}; +use ra_db::{salsa, CrateId, SourceDatabase}; use ra_syntax::ast; use crate::{ adt::{EnumData, StructData}, - nameres::raw::{ImportSourceMap, RawItems}, - EnumId, StructId, + nameres::{ + raw::{ImportSourceMap, RawItems}, + CrateDefMap, + }, + EnumId, StructId, UnionId, }; #[salsa::query_group(InternDatabaseStorage)] @@ -42,9 +45,15 @@ pub trait DefDatabase2: InternDatabase + AstDatabase { #[salsa::invoke(RawItems::raw_items_query)] fn raw_items(&self, file_id: HirFileId) -> Arc; + #[salsa::invoke(CrateDefMap::crate_def_map_query)] + fn crate_def_map(&self, krate: CrateId) -> Arc; + #[salsa::invoke(StructData::struct_data_query)] fn struct_data(&self, s: StructId) -> Arc; + #[salsa::invoke(StructData::union_data_query)] + fn union_data(&self, s: UnionId) -> Arc; + #[salsa::invoke(EnumData::enum_data_query)] fn enum_data(&self, e: EnumId) -> Arc; } diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs new file mode 100644 index 000000000..637184c58 --- /dev/null +++ b/crates/ra_hir_def/src/diagnostics.rs @@ -0,0 +1,26 @@ +use std::any::Any; + +use hir_expand::diagnostics::Diagnostic; +use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; +use relative_path::RelativePathBuf; + +use hir_expand::{HirFileId, Source}; + +#[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 + } +} diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index 6d66f481d..42e080a72 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs @@ -13,6 +13,7 @@ pub mod path; pub mod type_ref; pub mod builtin_type; pub mod adt; +pub mod diagnostics; // FIXME: this should be private pub mod nameres; @@ -237,8 +238,8 @@ impl AstItemDef for EnumId { // FIXME: rename to `VariantId`, only enums can ave variants #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct EnumVariantId { - parent: EnumId, - local_id: LocalEnumVariantId, + pub parent: EnumId, + pub local_id: LocalEnumVariantId, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs index 11ba8a777..db59344aa 100644 --- a/crates/ra_hir_def/src/nameres.rs +++ b/crates/ra_hir_def/src/nameres.rs @@ -2,4 +2,492 @@ // FIXME: review privacy of submodules pub mod raw; +pub mod per_ns; +pub mod collector; pub mod mod_resolution; + +use std::sync::Arc; + +use hir_expand::{diagnostics::DiagnosticSink, name::Name, MacroDefId}; +use once_cell::sync::Lazy; +use ra_arena::Arena; +use ra_db::{CrateId, Edition, FileId}; +use ra_prof::profile; +use ra_syntax::ast; +use rustc_hash::{FxHashMap, FxHashSet}; +// use test_utils::tested_by; + +use crate::{ + builtin_type::BuiltinType, + db::DefDatabase2, + nameres::{diagnostics::DefDiagnostic, per_ns::PerNs, raw::ImportId}, + path::{Path, PathKind}, + AdtId, AstId, CrateModuleId, EnumVariantId, ModuleDefId, ModuleId, TraitId, +}; + +/// Contains all top-level defs from a macro-expanded crate +#[derive(Debug, PartialEq, Eq)] +pub struct CrateDefMap { + krate: CrateId, + 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, + pub 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 parent: Option, + pub children: FxHashMap, + pub scope: ModuleScope, + /// None for root + pub declaration: Option>, + /// None for inline modules. + /// + /// Note that non-inline modules, by definition, live inside non-macro file. + pub definition: Option, +} + +#[derive(Debug, Default, PartialEq, Eq, Clone)] +pub struct ModuleScope { + pub 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(ModuleDefId::TraitId(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_: MacroDefId) -> 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 DefDatabase2, + krate: CrateId, + ) -> Arc { + let _p = profile("crate_def_map_query"); + let def_map = { + let crate_graph = db.crate_graph(); + let edition = crate_graph.edition(krate); + 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 fn krate(&self) -> CrateId { + self.krate + } + + pub fn root(&self) -> CrateModuleId { + self.root + } + + pub fn prelude(&self) -> Option { + self.prelude + } + + pub fn extern_prelude(&self) -> &FxHashMap { + &self.extern_prelude + } + + pub fn add_diagnostics( + &self, + db: &impl DefDatabase2, + module: CrateModuleId, + sink: &mut DiagnosticSink, + ) { + self.diagnostics.iter().for_each(|it| it.add_to(db, module, sink)) + } + + pub fn resolve_path( + &self, + db: &impl DefDatabase2, + 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 DefDatabase2, + 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(krate) => { + if krate == self.krate { + // tested_by!(macro_dollar_crate_self); + PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into()) + } else { + let def_map = db.crate_def_map(krate); + let module = ModuleId { krate, module_id: def_map.root }; + // tested_by!(macro_dollar_crate_other); + PerNs::types(module.into()) + } + } + PathKind::Crate => { + PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into()) + } + PathKind::Self_ => { + PerNs::types(ModuleId { 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) + // 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(ModuleId { 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 + } + } + 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 { + ModuleDefId::ModuleId(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.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.module_id].scope.get(&segment.name) { + Some(res) => res.def, + _ => { + log::debug!("path segment {:?} not found", segment.name); + return ResolvePathResult::empty(ReachedFixedPoint::No); + } + } + } + ModuleDefId::AdtId(AdtId::EnumId(e)) => { + // enum variant + // tested_by!(can_import_enum_variant); + let enum_data = db.enum_data(e); + match enum_data.variant(&segment.name) { + Some(local_id) => { + let variant = EnumVariantId { parent: e, local_id }; + 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 DefDatabase2, + 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 DefDatabase2, 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.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::DefDatabase2, 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 DefDatabase2, + 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_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs new file mode 100644 index 000000000..8a96d3d31 --- /dev/null +++ b/crates/ra_hir_def/src/nameres/collector.rs @@ -0,0 +1,845 @@ +//! FIXME: write short doc here + +use hir_expand::{ + name::{self, AsName, Name}, + HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFileKind, +}; +use ra_cfg::CfgOptions; +use ra_db::{CrateId, FileId}; +use ra_syntax::{ast, SmolStr}; +use rustc_hash::FxHashMap; +// use test_utils::tested_by; + +use crate::{ + attr::Attr, + db::DefDatabase2, + nameres::{ + diagnostics::DefDiagnostic, mod_resolution::ModDir, per_ns::PerNs, raw, CrateDefMap, + ModuleData, ReachedFixedPoint, Resolution, ResolveMode, + }, + path::{Path, PathKind}, + AdtId, AstId, AstItemDef, ConstId, CrateModuleId, EnumId, EnumVariantId, FunctionId, + LocationCtx, ModuleDefId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, UnionId, +}; + +pub(super) fn collect_defs(db: &impl DefDatabase2, mut def_map: CrateDefMap) -> CrateDefMap { + let crate_graph = db.crate_graph(); + + // populate external prelude + for dep in crate_graph.dependencies(def_map.krate) { + let dep_def_map = db.crate_def_map(dep.crate_id); + log::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id); + def_map.extern_prelude.insert( + dep.as_name(), + ModuleId { krate: dep.crate_id, module_id: dep_def_map.root }.into(), + ); + + // look for the prelude + if def_map.prelude.is_none() { + let map = db.crate_def_map(dep.crate_id); + if map.prelude.is_some() { + def_map.prelude = map.prelude; + } + } + } + + let cfg_options = crate_graph.cfg_options(def_map.krate); + + 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: DefDatabase2, +{ + fn collect(&mut self) { + let crate_graph = self.db.crate_graph(); + let file_id = crate_graph.crate_root(self.def_map.krate); + 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_: MacroDefId, + 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_: MacroDefId) { + // 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(ModuleDefId::ModuleId(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: CrateId) { + 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(ModuleDefId::ModuleId(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.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.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.module_id) + .or_default() + .push((module_id, import_id)); + } + } + Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => { + // tested_by!(glob_enum); + // glob import from enum => just import all the variants + let enum_data = self.db.enum_data(e); + let resolutions = enum_data + .variants + .iter() + .filter_map(|(local_id, variant_data)| { + let name = variant_data.name.clone()?; + let variant = EnumVariantId { parent: e, local_id }; + let res = Resolution { + def: PerNs::both(variant.into(), variant.into()), + import: Some(import_id), + }; + 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)]); + } + // tested_by!(bogus_paths), + None => (), + } + } + } + + 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, ast_id: *ast_id }); + resolved.push((*module_id, call_id, def)); + 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: DefDatabase2, +{ + 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( + ModuleId { krate: self.def_collector.def_map.krate, module_id: 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 = + ModuleId { krate: self.def_collector.def_map.krate, module_id: self.module_id }; + let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id); + + let name = def.name.clone(); + let def: PerNs = match def.kind { + raw::DefKind::Function(ast_id) => { + PerNs::values(FunctionId::from_ast_id(ctx, ast_id).into()) + } + raw::DefKind::Struct(ast_id) => { + let s = StructId::from_ast_id(ctx, ast_id).into(); + PerNs::both(s, s) + } + raw::DefKind::Union(ast_id) => { + let s = UnionId::from_ast_id(ctx, ast_id).into(); + PerNs::both(s, s) + } + raw::DefKind::Enum(ast_id) => PerNs::types(EnumId::from_ast_id(ctx, ast_id).into()), + raw::DefKind::Const(ast_id) => PerNs::values(ConstId::from_ast_id(ctx, ast_id).into()), + raw::DefKind::Static(ast_id) => { + PerNs::values(StaticId::from_ast_id(ctx, ast_id).into()) + } + raw::DefKind::Trait(ast_id) => PerNs::types(TraitId::from_ast_id(ctx, ast_id).into()), + raw::DefKind::TypeAlias(ast_id) => { + PerNs::types(TypeAliasId::from_ast_id(ctx, ast_id).into()) + } + }; + 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 }; + self.def_collector.define_macro(self.module_id, name.clone(), macro_id, 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 macro_call_id = + self.def_collector.db.intern_macro(MacroCallLoc { def: macro_def, ast_id }); + + self.def_collector.collect_macro_expansion(self.module_id, macro_call_id, macro_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(never)] +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_def/src/nameres/per_ns.rs b/crates/ra_hir_def/src/nameres/per_ns.rs new file mode 100644 index 000000000..298b0b0c7 --- /dev/null +++ b/crates/ra_hir_def/src/nameres/per_ns.rs @@ -0,0 +1,82 @@ +//! FIXME: write short doc here + +use hir_expand::MacroDefId; + +use crate::ModuleDefId; + +#[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: ModuleDefId) -> PerNs { + PerNs { types: None, values: Some(t), macros: None } + } + + pub fn types(t: ModuleDefId) -> PerNs { + PerNs { types: Some(t), values: None, macros: None } + } + + pub fn both(types: ModuleDefId, values: ModuleDefId) -> PerNs { + PerNs { types: Some(types), values: Some(values), macros: None } + } + + pub fn macros(macro_: MacroDefId) -> 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_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs index 9ac9768af..09ca40179 100644 --- a/crates/ra_ide_api/src/completion/complete_path.rs +++ b/crates/ra_ide_api/src/completion/complete_path.rs @@ -18,15 +18,15 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { match def { hir::ModuleDef::Module(module) => { let module_scope = module.scope(ctx.db); - for (name, res) in module_scope.entries() { - if let Some(hir::ModuleDef::BuiltinType(..)) = res.def.take_types() { + for (name, def, import) in module_scope { + if let hir::ScopeDef::ModuleDef(hir::ModuleDef::BuiltinType(..)) = def { if ctx.use_item_syntax.is_some() { tested_by!(dont_complete_primitive_in_use); continue; } } if Some(module) == ctx.module { - if let Some(import) = res.import { + if let Some(import) = import { if let Either::A(use_tree) = module.import_source(ctx.db, import) { if use_tree.syntax().text_range().contains_inclusive(ctx.offset) { // for `use self::foo<|>`, don't suggest `foo` as a completion @@ -36,7 +36,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { } } } - acc.add_resolution(ctx, name.to_string(), &res.def.into()); + acc.add_resolution(ctx, name.to_string(), &def); } } hir::ModuleDef::Adt(_) | hir::ModuleDef::TypeAlias(_) => { -- cgit v1.2.3 From 0933d914a37c4ab57fda6fe95464d194dab6f80c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 3 Nov 2019 20:53:17 +0300 Subject: Introduce ra_db::fixture fixture module The goal here is to share more testing infrastructure between crates. --- crates/ra_db/Cargo.toml | 1 + crates/ra_db/src/fixture.rs | 40 ++++++++++++++++++++++++++++++ crates/ra_db/src/lib.rs | 3 ++- crates/ra_hir_def/src/lib.rs | 3 +++ crates/ra_hir_def/src/nameres/collector.rs | 20 +++++++-------- crates/ra_hir_def/src/test_db.rs | 40 ++++++++++++++++++++++++++++++ 6 files changed, 96 insertions(+), 11 deletions(-) create mode 100644 crates/ra_db/src/fixture.rs create mode 100644 crates/ra_hir_def/src/test_db.rs (limited to 'crates') diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml index 3394ae8ce..bf1f7920c 100644 --- a/crates/ra_db/Cargo.toml +++ b/crates/ra_db/Cargo.toml @@ -12,3 +12,4 @@ rustc-hash = "1.0" ra_syntax = { path = "../ra_syntax" } ra_cfg = { path = "../ra_cfg" } ra_prof = { path = "../ra_prof" } +test_utils = { path = "../test_utils" } diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs new file mode 100644 index 000000000..469251fe9 --- /dev/null +++ b/crates/ra_db/src/fixture.rs @@ -0,0 +1,40 @@ +//! FIXME: write short doc here + +use std::sync::Arc; + +use ra_cfg::CfgOptions; + +use crate::{ + CrateGraph, Edition, FileId, RelativePathBuf, SourceDatabaseExt, SourceRoot, SourceRootId, +}; + +pub const WORKSPACE: SourceRootId = SourceRootId(0); + +pub trait WithFixture: Default + SourceDatabaseExt + 'static { + fn with_single_file(text: &str) -> (Self, FileId) { + let mut db = Self::default(); + let file_id = with_single_file(&mut db, text); + (db, file_id) + } +} + +impl WithFixture for DB {} + +fn with_single_file(db: &mut dyn SourceDatabaseExt, text: &str) -> FileId { + let file_id = FileId(0); + let rel_path: RelativePathBuf = "/main.rs".into(); + + let mut source_root = SourceRoot::default(); + source_root.insert_file(rel_path.clone(), file_id); + + let mut crate_graph = CrateGraph::default(); + crate_graph.add_crate_root(file_id, Edition::Edition2018, CfgOptions::default()); + + db.set_file_text(file_id, Arc::new(text.to_string())); + db.set_file_relative_path(file_id, rel_path); + db.set_file_source_root(file_id, WORKSPACE); + db.set_source_root(WORKSPACE, Arc::new(source_root)); + db.set_crate_graph(Arc::new(crate_graph)); + + file_id +} diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index 0d1ab4843..b6bfd531d 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs @@ -1,17 +1,18 @@ //! ra_db defines basic database traits. The concrete DB is defined by ra_ide_api. mod cancellation; mod input; +pub mod fixture; use std::{panic, sync::Arc}; use ra_prof::profile; use ra_syntax::{ast, Parse, SourceFile, TextRange, TextUnit}; -use relative_path::{RelativePath, RelativePathBuf}; pub use crate::{ cancellation::Canceled, input::{CrateGraph, CrateId, Dependency, Edition, FileId, SourceRoot, SourceRootId}, }; +pub use relative_path::{RelativePath, RelativePathBuf}; pub use salsa; pub trait CheckCanceled { diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index 42e080a72..02bd808fc 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs @@ -15,6 +15,9 @@ pub mod builtin_type; pub mod adt; pub mod diagnostics; +#[cfg(test)] +mod test_db; + // FIXME: this should be private pub mod nameres; diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 8a96d3d31..0bc36910c 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs @@ -739,17 +739,18 @@ fn is_macro_rules(path: &Path) -> bool { path.as_ident() == Some(&name::MACRO_RULES) } -#[cfg(never)] +#[cfg(test)] mod tests { - use ra_db::SourceDatabase; - - use super::*; - use crate::{db::DefDatabase, mock::MockDatabase, Crate}; use ra_arena::Arena; + use ra_db::{fixture::WithFixture, SourceDatabase}; use rustc_hash::FxHashSet; + use crate::{db::DefDatabase2, test_db::TestDB}; + + use super::*; + fn do_collect_defs( - db: &impl DefDatabase, + db: &impl DefDatabase2, def_map: CrateDefMap, monitor: MacroStackMonitor, ) -> CrateDefMap { @@ -768,12 +769,11 @@ mod tests { } 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 (db, _file_id) = TestDB::with_single_file(&code); + let krate = db.crate_graph().iter().next().unwrap(); let def_map = { - let edition = krate.edition(&db); + let edition = db.crate_graph().edition(krate); let mut modules: Arena = Arena::default(); let root = modules.alloc(ModuleData::default()); CrateDefMap { diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs new file mode 100644 index 000000000..67714c68e --- /dev/null +++ b/crates/ra_hir_def/src/test_db.rs @@ -0,0 +1,40 @@ +use std::{panic, sync::Arc}; + +use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate}; +use relative_path::RelativePath; + +#[salsa::database( + ra_db::SourceDatabaseExtStorage, + ra_db::SourceDatabaseStorage, + hir_expand::db::AstDatabaseStorage, + crate::db::InternDatabaseStorage, + crate::db::DefDatabase2Storage +)] +#[derive(Debug, Default)] +pub struct TestDB { + runtime: salsa::Runtime, +} + +impl salsa::Database for TestDB { + fn salsa_runtime(&self) -> &salsa::Runtime { + &self.runtime + } +} + +impl panic::RefUnwindSafe for TestDB {} + +impl FileLoader for TestDB { + fn file_text(&self, file_id: FileId) -> Arc { + FileLoaderDelegate(self).file_text(file_id) + } + fn resolve_relative_path( + &self, + anchor: FileId, + relative_path: &RelativePath, + ) -> Option { + FileLoaderDelegate(self).resolve_relative_path(anchor, relative_path) + } + fn relevant_crates(&self, file_id: FileId) -> Arc> { + FileLoaderDelegate(self).relevant_crates(file_id) + } +} -- cgit v1.2.3 From 6fba51c5fc05264abcbf971dcf28142746588d74 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 3 Nov 2019 23:35:48 +0300 Subject: move crate_def_map tests to hir_def --- crates/ra_db/src/fixture.rs | 148 +++- crates/ra_db/src/input.rs | 1 + crates/ra_hir/src/nameres.rs | 3 - crates/ra_hir/src/nameres/tests.rs | 573 --------------- crates/ra_hir/src/nameres/tests/globs.rs | 118 ---- crates/ra_hir/src/nameres/tests/incremental.rs | 140 ---- crates/ra_hir/src/nameres/tests/macros.rs | 635 ----------------- crates/ra_hir/src/nameres/tests/mod_resolution.rs | 759 -------------------- crates/ra_hir/src/nameres/tests/primitives.rs | 24 - crates/ra_hir_def/Cargo.toml | 4 + crates/ra_hir_def/src/nameres.rs | 3 + crates/ra_hir_def/src/nameres/tests.rs | 522 ++++++++++++++ crates/ra_hir_def/src/nameres/tests/globs.rs | 114 +++ crates/ra_hir_def/src/nameres/tests/incremental.rs | 133 ++++ crates/ra_hir_def/src/nameres/tests/macros.rs | 602 ++++++++++++++++ .../ra_hir_def/src/nameres/tests/mod_resolution.rs | 782 +++++++++++++++++++++ crates/ra_hir_def/src/nameres/tests/primitives.rs | 24 + crates/ra_hir_def/src/test_db.rs | 36 +- 18 files changed, 2367 insertions(+), 2254 deletions(-) delete mode 100644 crates/ra_hir/src/nameres/tests.rs delete mode 100644 crates/ra_hir/src/nameres/tests/globs.rs delete mode 100644 crates/ra_hir/src/nameres/tests/incremental.rs delete mode 100644 crates/ra_hir/src/nameres/tests/macros.rs delete mode 100644 crates/ra_hir/src/nameres/tests/mod_resolution.rs delete mode 100644 crates/ra_hir/src/nameres/tests/primitives.rs create mode 100644 crates/ra_hir_def/src/nameres/tests.rs create mode 100644 crates/ra_hir_def/src/nameres/tests/globs.rs create mode 100644 crates/ra_hir_def/src/nameres/tests/incremental.rs create mode 100644 crates/ra_hir_def/src/nameres/tests/macros.rs create mode 100644 crates/ra_hir_def/src/nameres/tests/mod_resolution.rs create mode 100644 crates/ra_hir_def/src/nameres/tests/primitives.rs (limited to 'crates') diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs index 469251fe9..f5dd59f84 100644 --- a/crates/ra_db/src/fixture.rs +++ b/crates/ra_db/src/fixture.rs @@ -3,9 +3,12 @@ use std::sync::Arc; use ra_cfg::CfgOptions; +use rustc_hash::FxHashMap; +use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; use crate::{ - CrateGraph, Edition, FileId, RelativePathBuf, SourceDatabaseExt, SourceRoot, SourceRootId, + CrateGraph, Edition, FileId, FilePosition, RelativePathBuf, SourceDatabaseExt, SourceRoot, + SourceRootId, }; pub const WORKSPACE: SourceRootId = SourceRootId(0); @@ -16,6 +19,19 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { let file_id = with_single_file(&mut db, text); (db, file_id) } + + fn with_files(fixture: &str) -> Self { + let mut db = Self::default(); + let pos = with_files(&mut db, fixture); + assert!(pos.is_none()); + db + } + + fn with_position(fixture: &str) -> (Self, FilePosition) { + let mut db = Self::default(); + let pos = with_files(&mut db, fixture); + (db, pos.unwrap()) + } } impl WithFixture for DB {} @@ -38,3 +54,133 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, text: &str) -> FileId { file_id } + +fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option { + let fixture = parse_fixture(fixture); + + let mut crate_graph = CrateGraph::default(); + let mut crates = FxHashMap::default(); + let mut crate_deps = Vec::new(); + let mut default_crate_root: Option = None; + + let mut source_root = SourceRoot::default(); + let mut source_root_id = WORKSPACE; + let mut source_root_prefix: RelativePathBuf = "/".into(); + let mut file_id = FileId(0); + + let mut file_position = None; + + for entry in fixture.iter() { + let meta = match parse_meta(&entry.meta) { + ParsedMeta::Root { path } => { + let source_root = std::mem::replace(&mut source_root, SourceRoot::default()); + db.set_source_root(source_root_id, Arc::new(source_root)); + source_root_id.0 += 1; + source_root_prefix = path; + continue; + } + ParsedMeta::File(it) => it, + }; + assert!(meta.path.starts_with(&source_root_prefix)); + + if let Some(krate) = meta.krate { + let crate_id = crate_graph.add_crate_root(file_id, meta.edition, meta.cfg); + let prev = crates.insert(krate.clone(), crate_id); + assert!(prev.is_none()); + for dep in meta.deps { + crate_deps.push((krate.clone(), dep)) + } + } else if meta.path == "/main.rs" || meta.path == "/lib.rs" { + assert!(default_crate_root.is_none()); + default_crate_root = Some(file_id); + } + + let text = if entry.text.contains(CURSOR_MARKER) { + let (offset, text) = extract_offset(&entry.text); + assert!(file_position.is_none()); + file_position = Some(FilePosition { file_id, offset }); + text.to_string() + } else { + entry.text.to_string() + }; + + db.set_file_text(file_id, Arc::new(text)); + db.set_file_relative_path(file_id, meta.path.clone()); + db.set_file_source_root(file_id, source_root_id); + source_root.insert_file(meta.path, file_id); + + file_id.0 += 1; + } + + if crates.is_empty() { + let crate_root = default_crate_root.unwrap(); + crate_graph.add_crate_root(crate_root, Edition::Edition2018, CfgOptions::default()); + } else { + for (from, to) in crate_deps { + let from_id = crates[&from]; + let to_id = crates[&to]; + crate_graph.add_dep(from_id, to.into(), to_id).unwrap(); + } + } + + db.set_source_root(source_root_id, Arc::new(source_root)); + db.set_crate_graph(Arc::new(crate_graph)); + + file_position +} + +enum ParsedMeta { + Root { path: RelativePathBuf }, + File(FileMeta), +} + +struct FileMeta { + path: RelativePathBuf, + krate: Option, + deps: Vec, + cfg: CfgOptions, + edition: Edition, +} + +//- /lib.rs crate:foo deps:bar,baz +fn parse_meta(meta: &str) -> ParsedMeta { + let components = meta.split_ascii_whitespace().collect::>(); + + if components[0] == "root" { + let path: RelativePathBuf = components[1].into(); + assert!(path.starts_with("/") && path.ends_with("/")); + return ParsedMeta::Root { path }; + } + + let path: RelativePathBuf = components[0].into(); + assert!(path.starts_with("/")); + + let mut krate = None; + let mut deps = Vec::new(); + let mut edition = Edition::Edition2018; + let mut cfg = CfgOptions::default(); + for component in components[1..].iter() { + let (key, value) = split1(component, ':').unwrap(); + match key { + "crate" => krate = Some(value.to_string()), + "deps" => deps = value.split(',').map(|it| it.to_string()).collect(), + "edition" => edition = Edition::from_string(&value), + "cfg" => { + for key in value.split(',') { + match split1(key, '=') { + None => cfg.insert_atom(key.into()), + Some((k, v)) => cfg.insert_key_value(k.into(), v.into()), + } + } + } + _ => panic!("bad component: {:?}", component), + } + } + + ParsedMeta::File(FileMeta { path, krate, deps, edition, cfg }) +} + +fn split1(haystack: &str, delim: char) -> Option<(&str, &str)> { + let idx = haystack.find(delim)?; + Some((&haystack[..idx], &haystack[idx + delim.len_utf8()..])) +} diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index eafa95921..7c8dac1d3 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs @@ -97,6 +97,7 @@ pub enum Edition { } impl Edition { + //FIXME: replace with FromStr with proper error handling pub fn from_string(s: &str) -> Edition { match s { "2015" => Edition::Edition2015, diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index bb775cfc9..875addc84 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -47,9 +47,6 @@ //! path and, upon success, we run macro expansion and "collect module" phase //! on the result -#[cfg(test)] -mod tests; - pub use hir_def::nameres::{ per_ns::{Namespace, PerNs}, raw::ImportId, diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs deleted file mode 100644 index 02db91a86..000000000 --- a/crates/ra_hir/src/nameres/tests.rs +++ /dev/null @@ -1,573 +0,0 @@ -mod macros; -mod globs; -mod incremental; -mod primitives; -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 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 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()); - return buf.trim().to_string(); - - fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: CrateModuleId) { - *buf += path; - *buf += "\n"; - - let mut entries = map.modules[module] - .scope - .items - .iter() - .map(|(name, res)| (name, res.def)) - .collect::>(); - entries.sort_by_key(|(name, _)| *name); - - for (name, res) in entries { - *buf += &format!("{}:", name); - - if res.types.is_some() { - *buf += " t"; - } - if res.values.is_some() { - *buf += " v"; - } - if res.macros.is_some() { - *buf += " m"; - } - if res.is_none() { - *buf += " _"; - } - - *buf += "\n"; - } - - for (name, child) in map.modules[module].children.iter() { - let path = path.to_string() + &format!("::{}", name); - go(buf, map, &path, *child); - } - } -} - -fn def_map(fixtute: &str) -> String { - let dm = compute_crate_def_map(fixtute, None); - render_crate_def_map(&dm) -} - -fn def_map_with_crate_graph(fixture: &str, graph: CrateGraphFixture) -> String { - let dm = compute_crate_def_map(fixture, Some(graph)); - render_crate_def_map(&dm) -} - -#[test] -fn crate_def_map_smoke_test() { - let map = def_map( - " - //- /lib.rs - mod foo; - struct S; - use crate::foo::bar::E; - use self::E::V; - - //- /foo/mod.rs - pub mod bar; - fn f() {} - - //- /foo/bar.rs - pub struct Baz; - enum E { V } - ", - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮E: t - ⋮S: t v - ⋮V: t v - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮bar: t - ⋮f: v - ⋮ - ⋮crate::foo::bar - ⋮Baz: t v - ⋮E: t - "###) -} - -#[test] -fn bogus_paths() { - // covers!(bogus_paths); - let map = def_map( - " - //- /lib.rs - mod foo; - struct S; - use self; - - //- /foo/mod.rs - use super; - use crate; - - ", - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮S: t v - ⋮foo: t - ⋮ - ⋮crate::foo - "### - ) -} - -#[test] -fn use_as() { - let map = def_map( - " - //- /lib.rs - mod foo; - - use crate::foo::Baz as Foo; - - //- /foo/mod.rs - pub struct Baz; - ", - ); - assert_snapshot!(map, - @r###" - ⋮crate - ⋮Foo: t v - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮Baz: t v - "### - ); -} - -#[test] -fn use_trees() { - let map = def_map( - " - //- /lib.rs - mod foo; - - use crate::foo::bar::{Baz, Quux}; - - //- /foo/mod.rs - pub mod bar; - - //- /foo/bar.rs - pub struct Baz; - pub enum Quux {}; - ", - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Baz: t v - ⋮Quux: t - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮bar: t - ⋮ - ⋮crate::foo::bar - ⋮Baz: t v - ⋮Quux: t - "###); -} - -#[test] -fn re_exports() { - let map = def_map( - " - //- /lib.rs - mod foo; - - use self::foo::Baz; - - //- /foo/mod.rs - pub mod bar; - - pub use self::bar::Baz; - - //- /foo/bar.rs - pub struct Baz; - ", - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Baz: t v - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮Baz: t v - ⋮bar: t - ⋮ - ⋮crate::foo::bar - ⋮Baz: t v - "###); -} - -#[test] -fn std_prelude() { - // covers!(std_prelude); - let map = def_map_with_crate_graph( - " - //- /main.rs - use Foo::*; - - //- /lib.rs - mod prelude; - #[prelude_import] - use prelude::*; - - //- /prelude.rs - pub enum Foo { Bar, Baz }; - ", - crate_graph! { - "main": ("/main.rs", ["test_crate"]), - "test_crate": ("/lib.rs", []), - }, - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Bar: t v - ⋮Baz: t v - "###); -} - -#[test] -fn can_import_enum_variant() { - // covers!(can_import_enum_variant); - let map = def_map( - " - //- /lib.rs - enum E { V } - use self::E::V; - ", - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮E: t - ⋮V: t v - "### - ); -} - -#[test] -fn edition_2015_imports() { - let map = def_map_with_crate_graph( - " - //- /main.rs - mod foo; - mod bar; - - //- /bar.rs - struct Bar; - - //- /foo.rs - use bar::Bar; - use other_crate::FromLib; - - //- /lib.rs - struct FromLib; - ", - crate_graph! { - "main": ("/main.rs", "2015", ["other_crate"]), - "other_crate": ("/lib.rs", "2018", []), - }, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮bar: t - ⋮foo: t - ⋮ - ⋮crate::bar - ⋮Bar: t v - ⋮ - ⋮crate::foo - ⋮Bar: t v - ⋮FromLib: t v - "###); -} - -#[test] -fn item_map_using_self() { - let map = def_map( - " - //- /lib.rs - mod foo; - use crate::foo::bar::Baz::{self}; - //- /foo/mod.rs - pub mod bar; - //- /foo/bar.rs - pub struct Baz; - ", - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Baz: t v - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮bar: t - ⋮ - ⋮crate::foo::bar - ⋮Baz: t v - "###); -} - -#[test] -fn item_map_across_crates() { - let map = def_map_with_crate_graph( - " - //- /main.rs - use test_crate::Baz; - - //- /lib.rs - pub struct Baz; - ", - crate_graph! { - "main": ("/main.rs", ["test_crate"]), - "test_crate": ("/lib.rs", []), - }, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮Baz: t v - "###); -} - -#[test] -fn extern_crate_rename() { - let map = def_map_with_crate_graph( - " - //- /main.rs - extern crate alloc as alloc_crate; - - mod alloc; - mod sync; - - //- /sync.rs - use alloc_crate::Arc; - - //- /lib.rs - struct Arc; - ", - crate_graph! { - "main": ("/main.rs", ["alloc"]), - "alloc": ("/lib.rs", []), - }, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮alloc_crate: t - ⋮sync: t - ⋮ - ⋮crate::sync - ⋮Arc: t v - "###); -} - -#[test] -fn extern_crate_rename_2015_edition() { - let map = def_map_with_crate_graph( - " - //- /main.rs - extern crate alloc as alloc_crate; - - mod alloc; - mod sync; - - //- /sync.rs - use alloc_crate::Arc; - - //- /lib.rs - struct Arc; - ", - crate_graph! { - "main": ("/main.rs", "2015", ["alloc"]), - "alloc": ("/lib.rs", []), - }, - ); - - assert_snapshot!(map, - @r###" - ⋮crate - ⋮alloc_crate: t - ⋮sync: t - ⋮ - ⋮crate::sync - ⋮Arc: t v - "### - ); -} - -#[test] -fn import_across_source_roots() { - let map = def_map_with_crate_graph( - " - //- /lib.rs - pub mod a { - pub mod b { - pub struct C; - } - } - - //- root /main/ - - //- /main/main.rs - use test_crate::a::b::C; - ", - crate_graph! { - "main": ("/main/main.rs", ["test_crate"]), - "test_crate": ("/lib.rs", []), - }, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮C: t v - "###); -} - -#[test] -fn reexport_across_crates() { - let map = def_map_with_crate_graph( - " - //- /main.rs - use test_crate::Baz; - - //- /lib.rs - pub use foo::Baz; - - mod foo; - - //- /foo.rs - pub struct Baz; - ", - crate_graph! { - "main": ("/main.rs", ["test_crate"]), - "test_crate": ("/lib.rs", []), - }, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮Baz: t v - "###); -} - -#[test] -fn values_dont_shadow_extern_crates() { - let map = def_map_with_crate_graph( - " - //- /main.rs - fn foo() {} - use foo::Bar; - - //- /foo/lib.rs - pub struct Bar; - ", - crate_graph! { - "main": ("/main.rs", ["foo"]), - "foo": ("/foo/lib.rs", []), - }, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮Bar: t v - ⋮foo: v - "###); -} - -#[test] -fn cfg_not_test() { - let map = def_map_with_crate_graph( - r#" - //- /main.rs - use {Foo, Bar, Baz}; - //- /lib.rs - #[prelude_import] - pub use self::prelude::*; - mod prelude { - #[cfg(test)] - pub struct Foo; - #[cfg(not(test))] - pub struct Bar; - #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] - pub struct Baz; - } - "#, - crate_graph! { - "main": ("/main.rs", ["std"]), - "std": ("/lib.rs", []), - }, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮Bar: t v - ⋮Baz: _ - ⋮Foo: _ - "###); -} - -#[test] -fn cfg_test() { - let map = def_map_with_crate_graph( - r#" - //- /main.rs - use {Foo, Bar, Baz}; - //- /lib.rs - #[prelude_import] - pub use self::prelude::*; - mod prelude { - #[cfg(test)] - pub struct Foo; - #[cfg(not(test))] - pub struct Bar; - #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] - pub struct Baz; - } - "#, - crate_graph! { - "main": ("/main.rs", ["std"]), - "std": ("/lib.rs", [], cfg = { - "test", - "feature" = "foo", - "feature" = "bar", - "opt" = "42", - }), - }, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮Bar: _ - ⋮Baz: t v - ⋮Foo: t v - "###); -} diff --git a/crates/ra_hir/src/nameres/tests/globs.rs b/crates/ra_hir/src/nameres/tests/globs.rs deleted file mode 100644 index b3e4d8d94..000000000 --- a/crates/ra_hir/src/nameres/tests/globs.rs +++ /dev/null @@ -1,118 +0,0 @@ -use super::*; - -#[test] -fn glob_1() { - let map = def_map( - " - //- /lib.rs - mod foo; - use foo::*; - - //- /foo/mod.rs - pub mod bar; - pub use self::bar::Baz; - pub struct Foo; - - //- /foo/bar.rs - pub struct Baz; - ", - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Baz: t v - ⋮Foo: t v - ⋮bar: t - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮Baz: t v - ⋮Foo: t v - ⋮bar: t - ⋮ - ⋮crate::foo::bar - ⋮Baz: t v - "### - ); -} - -#[test] -fn glob_2() { - let map = def_map( - " - //- /lib.rs - mod foo; - use foo::*; - - //- /foo/mod.rs - pub mod bar; - pub use self::bar::*; - pub struct Foo; - - //- /foo/bar.rs - pub struct Baz; - pub use super::*; - ", - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Baz: t v - ⋮Foo: t v - ⋮bar: t - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮Baz: t v - ⋮Foo: t v - ⋮bar: t - ⋮ - ⋮crate::foo::bar - ⋮Baz: t v - ⋮Foo: t v - ⋮bar: t - "### - ); -} - -#[test] -fn glob_across_crates() { - // covers!(glob_across_crates); - let map = def_map_with_crate_graph( - " - //- /main.rs - use test_crate::*; - - //- /lib.rs - pub struct Baz; - ", - crate_graph! { - "main": ("/main.rs", ["test_crate"]), - "test_crate": ("/lib.rs", []), - }, - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Baz: t v - "### - ); -} - -#[test] -fn glob_enum() { - // covers!(glob_enum); - let map = def_map( - " - //- /lib.rs - enum Foo { - Bar, Baz - } - use self::Foo::*; - ", - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Bar: t v - ⋮Baz: t v - ⋮Foo: t - "### - ); -} diff --git a/crates/ra_hir/src/nameres/tests/incremental.rs b/crates/ra_hir/src/nameres/tests/incremental.rs deleted file mode 100644 index 723ece7b0..000000000 --- a/crates/ra_hir/src/nameres/tests/incremental.rs +++ /dev/null @@ -1,140 +0,0 @@ -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 krate = db.crate_graph().iter().next().unwrap(); - { - let events = db.log_executed(|| { - db.crate_def_map(krate); - }); - assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) - } - db.set_file_text(pos.file_id, Arc::new(file_change.to_string())); - - { - let events = db.log_executed(|| { - db.crate_def_map(krate); - }); - assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) - } -} - -#[test] -fn typing_inside_a_function_should_not_invalidate_def_map() { - check_def_map_is_not_recomputed( - " - //- /lib.rs - mod foo;<|> - - use crate::foo::bar::Baz; - - fn foo() -> i32 { - 1 + 1 - } - //- /foo/mod.rs - pub mod bar; - - //- /foo/bar.rs - pub struct Baz; - ", - " - mod foo; - - use crate::foo::bar::Baz; - - fn foo() -> i32 { 92 } - ", - ); -} - -#[test] -fn adding_inner_items_should_not_invalidate_def_map() { - check_def_map_is_not_recomputed( - " - //- /lib.rs - struct S { a: i32} - enum E { A } - trait T { - fn a() {} - } - mod foo;<|> - impl S { - fn a() {} - } - use crate::foo::bar::Baz; - //- /foo/mod.rs - pub mod bar; - - //- /foo/bar.rs - pub struct Baz; - ", - " - struct S { a: i32, b: () } - enum E { A, B } - trait T { - fn a() {} - fn b() {} - } - mod foo;<|> - impl S { - fn a() {} - fn b() {} - } - use crate::foo::bar::Baz; - ", - ); -} - -#[test] -fn typing_inside_a_macro_should_not_invalidate_def_map() { - let (mut db, pos) = MockDatabase::with_position( - " - //- /lib.rs - macro_rules! m { - ($ident:ident) => { - fn f() { - $ident + $ident; - }; - } - } - mod foo; - - //- /foo/mod.rs - pub mod bar; - - //- /foo/bar.rs - <|> - m!(X); - ", - ); - { - let events = db.log_executed(|| { - let src = crate::Source { - file_id: pos.file_id.into(), - ast: crate::ModuleSource::new(&db, Some(pos.file_id), None), - }; - let module = crate::Module::from_definition(&db, src).unwrap(); - let decls = module.declarations(&db); - assert_eq!(decls.len(), 18); - }); - assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) - } - db.set_file_text(pos.file_id, Arc::new("m!(Y);".to_string())); - - { - let events = db.log_executed(|| { - let src = crate::Source { - file_id: pos.file_id.into(), - ast: crate::ModuleSource::new(&db, Some(pos.file_id), None), - }; - let module = crate::Module::from_definition(&db, src).unwrap(); - let decls = module.declarations(&db); - assert_eq!(decls.len(), 18); - }); - assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) - } -} diff --git a/crates/ra_hir/src/nameres/tests/macros.rs b/crates/ra_hir/src/nameres/tests/macros.rs deleted file mode 100644 index 78bb0eb0d..000000000 --- a/crates/ra_hir/src/nameres/tests/macros.rs +++ /dev/null @@ -1,635 +0,0 @@ -use super::*; - -#[test] -fn macro_rules_are_globally_visible() { - let map = def_map( - " - //- /lib.rs - macro_rules! structs { - ($($i:ident),*) => { - $(struct $i { field: u32 } )* - } - } - structs!(Foo); - mod nested; - - //- /nested.rs - structs!(Bar, Baz); - ", - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Foo: t v - ⋮nested: t - ⋮ - ⋮crate::nested - ⋮Bar: t v - ⋮Baz: t v - "###); -} - -#[test] -fn macro_rules_can_define_modules() { - let map = def_map( - " - //- /lib.rs - macro_rules! m { - ($name:ident) => { mod $name; } - } - m!(n1); - - mod m { - m!(n3) - } - - //- /n1.rs - m!(n2) - //- /n1/n2.rs - struct X; - //- /m/n3.rs - struct Y; - ", - ); - assert_snapshot!(map, @r###" - crate - m: t - n1: t - - crate::m - n3: t - - crate::m::n3 - Y: t v - - crate::n1 - n2: t - - crate::n1::n2 - X: t v - "###); -} - -#[test] -fn macro_rules_from_other_crates_are_visible() { - let map = def_map_with_crate_graph( - " - //- /main.rs - foo::structs!(Foo, Bar) - mod bar; - - //- /bar.rs - use crate::*; - - //- /lib.rs - #[macro_export] - macro_rules! structs { - ($($i:ident),*) => { - $(struct $i { field: u32 } )* - } - } - ", - crate_graph! { - "main": ("/main.rs", ["foo"]), - "foo": ("/lib.rs", []), - }, - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Bar: t v - ⋮Foo: t v - ⋮bar: t - ⋮ - ⋮crate::bar - ⋮Bar: t v - ⋮Foo: t v - ⋮bar: t - "###); -} - -#[test] -fn macro_rules_export_with_local_inner_macros_are_visible() { - let map = def_map_with_crate_graph( - " - //- /main.rs - foo::structs!(Foo, Bar) - mod bar; - - //- /bar.rs - use crate::*; - - //- /lib.rs - #[macro_export(local_inner_macros)] - macro_rules! structs { - ($($i:ident),*) => { - $(struct $i { field: u32 } )* - } - } - ", - crate_graph! { - "main": ("/main.rs", ["foo"]), - "foo": ("/lib.rs", []), - }, - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Bar: t v - ⋮Foo: t v - ⋮bar: t - ⋮ - ⋮crate::bar - ⋮Bar: t v - ⋮Foo: t v - ⋮bar: t - "###); -} - -#[test] -fn unexpanded_macro_should_expand_by_fixedpoint_loop() { - let map = def_map_with_crate_graph( - " - //- /main.rs - macro_rules! baz { - () => { - use foo::bar; - } - } - - foo!(); - bar!(); - baz!(); - - //- /lib.rs - #[macro_export] - macro_rules! foo { - () => { - struct Foo { field: u32 } - } - } - #[macro_export] - macro_rules! bar { - () => { - use foo::foo; - } - } - ", - crate_graph! { - "main": ("/main.rs", ["foo"]), - "foo": ("/lib.rs", []), - }, - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Foo: t v - ⋮bar: m - ⋮foo: m - "###); -} - -#[test] -fn 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 - structs!(Foo); - structs_priv!(Bar); - structs_not_exported!(MacroNotResolved1); - crate::structs!(MacroNotResolved2); - - mod bar; - - #[macro_use] - extern crate foo; - - //- /bar.rs - structs!(Baz); - crate::structs!(MacroNotResolved3); - - //- /lib.rs - #[macro_export] - macro_rules! structs { - ($i:ident) => { struct $i; } - } - - macro_rules! structs_not_exported { - ($i:ident) => { struct $i; } - } - - mod priv_mod { - #[macro_export] - macro_rules! structs_priv { - ($i:ident) => { struct $i; } - } - } - ", - crate_graph! { - "main": ("/main.rs", ["foo"]), - "foo": ("/lib.rs", []), - }, - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Bar: t v - ⋮Foo: t v - ⋮bar: t - ⋮foo: t - ⋮ - ⋮crate::bar - ⋮Baz: t v - "###); -} - -#[test] -fn prelude_is_macro_use() { - // covers!(prelude_is_macro_use); - let map = def_map_with_crate_graph( - " - //- /main.rs - structs!(Foo); - structs_priv!(Bar); - structs_outside!(Out); - crate::structs!(MacroNotResolved2); - - mod bar; - - //- /bar.rs - structs!(Baz); - crate::structs!(MacroNotResolved3); - - //- /lib.rs - #[prelude_import] - use self::prelude::*; - - mod prelude { - #[macro_export] - macro_rules! structs { - ($i:ident) => { struct $i; } - } - - mod priv_mod { - #[macro_export] - macro_rules! structs_priv { - ($i:ident) => { struct $i; } - } - } - } - - #[macro_export] - macro_rules! structs_outside { - ($i:ident) => { struct $i; } - } - ", - crate_graph! { - "main": ("/main.rs", ["foo"]), - "foo": ("/lib.rs", []), - }, - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Bar: t v - ⋮Foo: t v - ⋮Out: t v - ⋮bar: t - ⋮ - ⋮crate::bar - ⋮Baz: t v - "###); -} - -#[test] -fn prelude_cycle() { - let map = def_map( - " - //- /lib.rs - #[prelude_import] - use self::prelude::*; - - declare_mod!(); - - mod prelude { - macro_rules! declare_mod { - () => (mod foo {}) - } - } - ", - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮prelude: t - ⋮ - ⋮crate::prelude - "###); -} - -#[test] -fn plain_macros_are_legacy_textual_scoped() { - let map = def_map( - r#" - //- /main.rs - mod m1; - bar!(NotFoundNotMacroUse); - - mod m2 { - foo!(NotFoundBeforeInside2); - } - - macro_rules! foo { - ($x:ident) => { struct $x; } - } - foo!(Ok); - - mod m3; - foo!(OkShadowStop); - bar!(NotFoundMacroUseStop); - - #[macro_use] - mod m5 { - #[macro_use] - mod m6 { - macro_rules! foo { - ($x:ident) => { fn $x() {} } - } - } - } - foo!(ok_double_macro_use_shadow); - - baz!(NotFoundBefore); - #[macro_use] - mod m7 { - macro_rules! baz { - ($x:ident) => { struct $x; } - } - } - baz!(OkAfter); - - //- /m1.rs - foo!(NotFoundBeforeInside1); - macro_rules! bar { - ($x:ident) => { struct $x; } - } - - //- /m3/mod.rs - foo!(OkAfterInside); - macro_rules! foo { - ($x:ident) => { fn $x() {} } - } - foo!(ok_shadow); - - #[macro_use] - mod m4; - bar!(OkMacroUse); - - //- /m3/m4.rs - foo!(ok_shadow_deep); - macro_rules! bar { - ($x:ident) => { struct $x; } - } - "#, - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Ok: t v - ⋮OkAfter: t v - ⋮OkShadowStop: t v - ⋮m1: t - ⋮m2: t - ⋮m3: t - ⋮m5: t - ⋮m7: t - ⋮ok_double_macro_use_shadow: v - ⋮ - ⋮crate::m7 - ⋮ - ⋮crate::m1 - ⋮ - ⋮crate::m5 - ⋮m6: t - ⋮ - ⋮crate::m5::m6 - ⋮ - ⋮crate::m2 - ⋮ - ⋮crate::m3 - ⋮OkAfterInside: t v - ⋮OkMacroUse: t v - ⋮m4: t - ⋮ok_shadow: v - ⋮ - ⋮crate::m3::m4 - ⋮ok_shadow_deep: v - "###); -} - -#[test] -fn type_value_macro_live_in_different_scopes() { - let map = def_map( - " - //- /main.rs - #[macro_export] - macro_rules! foo { - ($x:ident) => { type $x = (); } - } - - foo!(foo); - use foo as bar; - - use self::foo as baz; - fn baz() {} - ", - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮bar: t m - ⋮baz: t v m - ⋮foo: t m - "###); -} - -#[test] -fn macro_use_can_be_aliased() { - let map = def_map_with_crate_graph( - " - //- /main.rs - #[macro_use] - extern crate foo; - - foo!(Direct); - bar!(Alias); - - //- /lib.rs - use crate::foo as bar; - - mod m { - #[macro_export] - macro_rules! foo { - ($x:ident) => { struct $x; } - } - } - ", - crate_graph! { - "main": ("/main.rs", ["foo"]), - "foo": ("/lib.rs", []), - }, - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Alias: t v - ⋮Direct: t v - ⋮foo: t - "###); -} - -#[test] -fn path_qualified_macros() { - let map = def_map( - " - //- /main.rs - macro_rules! foo { - ($x:ident) => { struct $x; } - } - - crate::foo!(NotResolved); - - crate::bar!(OkCrate); - bar!(OkPlain); - alias1!(NotHere); - m::alias1!(OkAliasPlain); - m::alias2!(OkAliasSuper); - m::alias3!(OkAliasCrate); - not_found!(NotFound); - - mod m { - #[macro_export] - macro_rules! bar { - ($x:ident) => { struct $x; } - } - - pub use bar as alias1; - pub use super::bar as alias2; - pub use crate::bar as alias3; - pub use self::bar as not_found; - } - ", - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮OkAliasCrate: t v - ⋮OkAliasPlain: t v - ⋮OkAliasSuper: t v - ⋮OkCrate: t v - ⋮OkPlain: t v - ⋮bar: m - ⋮m: t - ⋮ - ⋮crate::m - ⋮alias1: m - ⋮alias2: m - ⋮alias3: m - ⋮not_found: _ - "###); -} - -#[test] -fn macro_dollar_crate_is_correct_in_item() { - // covers!(macro_dollar_crate_self); - // covers!(macro_dollar_crate_other); - let map = def_map_with_crate_graph( - " - //- /main.rs - #[macro_use] - extern crate foo; - - #[macro_use] - mod m { - macro_rules! current { - () => { - use $crate::Foo as FooSelf; - } - } - } - - struct Foo; - - current!(); - not_current1!(); - foo::not_current2!(); - - //- /lib.rs - mod m { - #[macro_export] - macro_rules! not_current1 { - () => { - use $crate::Bar; - } - } - } - - #[macro_export] - macro_rules! not_current2 { - () => { - use $crate::Baz; - } - } - - struct Bar; - struct Baz; - ", - crate_graph! { - "main": ("/main.rs", ["foo"]), - "foo": ("/lib.rs", []), - }, - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Bar: t v - ⋮Baz: t v - ⋮Foo: t v - ⋮FooSelf: t v - ⋮foo: t - ⋮m: t - ⋮ - ⋮crate::m - "###); -} - -#[test] -fn macro_dollar_crate_is_correct_in_indirect_deps() { - // covers!(macro_dollar_crate_other); - // From std - let map = def_map_with_crate_graph( - r#" - //- /main.rs - foo!(); - - //- /std.rs - #[prelude_import] - use self::prelude::*; - - pub use core::foo; - - mod prelude {} - - #[macro_use] - mod std_macros; - - //- /core.rs - #[macro_export] - macro_rules! foo { - () => { - use $crate::bar; - } - } - - pub struct bar; - "#, - crate_graph! { - "main": ("/main.rs", ["std"]), - "std": ("/std.rs", ["core"]), - "core": ("/core.rs", []), - }, - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮bar: t v - "###); -} diff --git a/crates/ra_hir/src/nameres/tests/mod_resolution.rs b/crates/ra_hir/src/nameres/tests/mod_resolution.rs deleted file mode 100644 index abfe8b1c3..000000000 --- a/crates/ra_hir/src/nameres/tests/mod_resolution.rs +++ /dev/null @@ -1,759 +0,0 @@ -use super::*; - -#[test] -fn name_res_works_for_broken_modules() { - // covers!(name_res_works_for_broken_modules); - let map = def_map( - " - //- /lib.rs - mod foo // no `;`, no body - - use self::foo::Baz; - - //- /foo/mod.rs - pub mod bar; - - pub use self::bar::Baz; - - //- /foo/bar.rs - pub struct Baz; - ", - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮Baz: _ - "###); -} - -#[test] -fn nested_module_resolution() { - let map = def_map( - " - //- /lib.rs - mod n1; - - //- /n1.rs - mod n2; - - //- /n1/n2.rs - struct X; - ", - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮n1: t - ⋮ - ⋮crate::n1 - ⋮n2: t - ⋮ - ⋮crate::n1::n2 - ⋮X: t v - "###); -} - -#[test] -fn module_resolution_works_for_non_standard_filenames() { - let map = def_map_with_crate_graph( - " - //- /my_library.rs - mod foo; - use self::foo::Bar; - - //- /foo/mod.rs - pub struct Bar; - ", - crate_graph! { - "my_library": ("/my_library.rs", []), - }, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮Bar: t v - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮Bar: t v - "###); -} - -#[test] -fn module_resolution_works_for_raw_modules() { - let map = def_map( - " - //- /lib.rs - mod r#async; - use self::r#async::Bar; - - //- /async.rs - pub struct Bar; - ", - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮Bar: t v - ⋮async: t - ⋮ - ⋮crate::async - ⋮Bar: t v - "###); -} - -#[test] -fn module_resolution_decl_path() { - let map = def_map( - r###" - //- /lib.rs - #[path = "bar/baz/foo.rs"] - mod foo; - use self::foo::Bar; - - //- /bar/baz/foo.rs - pub struct Bar; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮Bar: t v - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮Bar: t v - "###); -} - -#[test] -fn module_resolution_module_with_path_in_mod_rs() { - let map = def_map( - r###" - //- /main.rs - mod foo; - - //- /foo/mod.rs - #[path = "baz.rs"] - pub mod bar; - - use self::bar::Baz; - - //- /foo/baz.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮Baz: t v - ⋮bar: t - ⋮ - ⋮crate::foo::bar - ⋮Baz: t v - "###); -} - -#[test] -fn module_resolution_module_with_path_non_crate_root() { - let map = def_map( - r###" - //- /main.rs - mod foo; - - //- /foo.rs - #[path = "baz.rs"] - pub mod bar; - - use self::bar::Baz; - - //- /baz.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮Baz: t v - ⋮bar: t - ⋮ - ⋮crate::foo::bar - ⋮Baz: t v - "###); -} - -#[test] -fn module_resolution_module_decl_path_super() { - let map = def_map( - r###" - //- /main.rs - #[path = "bar/baz/module.rs"] - mod foo; - pub struct Baz; - - //- /bar/baz/module.rs - use super::Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮Baz: t v - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮Baz: t v - "###); -} - -#[test] -fn module_resolution_explicit_path_mod_rs() { - let map = def_map( - r###" - //- /main.rs - #[path = "module/mod.rs"] - mod foo; - - //- /module/mod.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮Baz: t v - "###); -} - -#[test] -fn module_resolution_relative_path() { - let map = def_map( - r###" - //- /main.rs - mod foo; - - //- /foo.rs - #[path = "./sub.rs"] - pub mod foo_bar; - - //- /sub.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮foo_bar: t - ⋮ - ⋮crate::foo::foo_bar - ⋮Baz: t v - "###); -} - -#[test] -fn module_resolution_relative_path_2() { - let map = def_map( - r###" - //- /main.rs - mod foo; - - //- /foo/mod.rs - #[path="../sub.rs"] - pub mod foo_bar; - - //- /sub.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮foo_bar: t - ⋮ - ⋮crate::foo::foo_bar - ⋮Baz: t v - "###); -} - -#[test] -fn module_resolution_explicit_path_mod_rs_2() { - let map = def_map( - r###" - //- /main.rs - #[path = "module/bar/mod.rs"] - mod foo; - - //- /module/bar/mod.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮Baz: t v - "###); -} - -#[test] -fn module_resolution_explicit_path_mod_rs_with_win_separator() { - let map = def_map( - r###" - //- /main.rs - #[path = "module\bar\mod.rs"] - mod foo; - - //- /module/bar/mod.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮Baz: t v - "###); -} - -#[test] -fn module_resolution_decl_inside_inline_module_with_path_attribute() { - let map = def_map( - r###" - //- /main.rs - #[path = "models"] - mod foo { - mod bar; - } - - //- /models/bar.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮bar: t - ⋮ - ⋮crate::foo::bar - ⋮Baz: t v - "###); -} - -#[test] -fn module_resolution_decl_inside_inline_module() { - let map = def_map( - r###" - //- /main.rs - mod foo { - mod bar; - } - - //- /foo/bar.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮bar: t - ⋮ - ⋮crate::foo::bar - ⋮Baz: t v - "###); -} - -#[test] -fn module_resolution_decl_inside_inline_module_2_with_path_attribute() { - let map = def_map( - r###" - //- /main.rs - #[path = "models/db"] - mod foo { - mod bar; - } - - //- /models/db/bar.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮bar: t - ⋮ - ⋮crate::foo::bar - ⋮Baz: t v - "###); -} - -#[test] -fn module_resolution_decl_inside_inline_module_3() { - let map = def_map( - r###" - //- /main.rs - #[path = "models/db"] - mod foo { - #[path = "users.rs"] - mod bar; - } - - //- /models/db/users.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮bar: t - ⋮ - ⋮crate::foo::bar - ⋮Baz: t v - "###); -} - -#[test] -fn module_resolution_decl_inside_inline_module_empty_path() { - let map = def_map( - r###" - //- /main.rs - #[path = ""] - mod foo { - #[path = "users.rs"] - mod bar; - } - - //- /users.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮bar: t - ⋮ - ⋮crate::foo::bar - ⋮Baz: t v - "###); -} - -#[test] -fn module_resolution_decl_empty_path() { - let map = def_map( - r###" - //- /main.rs - #[path = ""] // Should try to read `/` (a directory) - mod foo; - - //- /foo.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - "###); -} - -#[test] -fn module_resolution_decl_inside_inline_module_relative_path() { - let map = def_map( - r###" - //- /main.rs - #[path = "./models"] - mod foo { - mod bar; - } - - //- /models/bar.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮bar: t - ⋮ - ⋮crate::foo::bar - ⋮Baz: t v - "###); -} - -#[test] -fn module_resolution_decl_inside_inline_module_in_crate_root() { - let map = def_map( - r###" - //- /main.rs - mod foo { - #[path = "baz.rs"] - mod bar; - } - use self::foo::bar::Baz; - - //- /foo/baz.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮Baz: t v - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮bar: t - ⋮ - ⋮crate::foo::bar - ⋮Baz: t v - "###); -} - -#[test] -fn module_resolution_decl_inside_inline_module_in_mod_rs() { - let map = def_map( - r###" - //- /main.rs - mod foo; - - //- /foo/mod.rs - mod bar { - #[path = "qwe.rs"] - pub mod baz; - } - use self::bar::baz::Baz; - - //- /foo/bar/qwe.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮Baz: t v - ⋮bar: t - ⋮ - ⋮crate::foo::bar - ⋮baz: t - ⋮ - ⋮crate::foo::bar::baz - ⋮Baz: t v - "###); -} - -#[test] -fn module_resolution_decl_inside_inline_module_in_non_crate_root() { - let map = def_map( - r###" - //- /main.rs - mod foo; - - //- /foo.rs - mod bar { - #[path = "qwe.rs"] - pub mod baz; - } - use self::bar::baz::Baz; - - //- /foo/bar/qwe.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮Baz: t v - ⋮bar: t - ⋮ - ⋮crate::foo::bar - ⋮baz: t - ⋮ - ⋮crate::foo::bar::baz - ⋮Baz: t v - "###); -} - -#[test] -fn module_resolution_decl_inside_inline_module_in_non_crate_root_2() { - let map = def_map( - r###" - //- /main.rs - mod foo; - - //- /foo.rs - #[path = "bar"] - mod bar { - pub mod baz; - } - use self::bar::baz::Baz; - - //- /bar/baz.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮foo: t - ⋮ - ⋮crate::foo - ⋮Baz: t v - ⋮bar: t - ⋮ - ⋮crate::foo::bar - ⋮baz: t - ⋮ - ⋮crate::foo::bar::baz - ⋮Baz: t v - "###); -} - -#[test] -fn unresolved_module_diagnostics() { - let diagnostics = MockDatabase::with_files( - r" - //- /lib.rs - mod foo; - mod bar; - mod baz {} - //- /foo.rs - ", - ) - .diagnostics(); - - assert_snapshot!(diagnostics, @r###" - "mod bar;": unresolved module - "### - ); -} - -#[test] -fn module_resolution_decl_inside_module_in_non_crate_root_2() { - let map = def_map( - r###" - //- /main.rs - #[path="module/m2.rs"] - mod module; - - //- /module/m2.rs - pub mod submod; - - //- /module/submod.rs - pub struct Baz; - "###, - ); - - assert_snapshot!(map, @r###" - ⋮crate - ⋮module: t - ⋮ - ⋮crate::module - ⋮submod: t - ⋮ - ⋮crate::module::submod - ⋮Baz: t v - "###); -} - -#[test] -fn nested_out_of_line_module() { - let map = def_map( - r###" - //- /lib.rs - mod a { - mod b { - mod c; - } - } - - //- /a/b/c.rs - struct X; - "###, - ); - - assert_snapshot!(map, @r###" - crate - a: t - - crate::a - b: t - - crate::a::b - c: t - - crate::a::b::c - X: t v - "###); -} - -#[test] -fn nested_out_of_line_module_with_path() { - let map = def_map( - r###" - //- /lib.rs - mod a { - #[path = "d/e"] - mod b { - mod c; - } - } - - //- /a/d/e/c.rs - struct X; - "###, - ); - - assert_snapshot!(map, @r###" - crate - a: t - - crate::a - b: t - - crate::a::b - c: t - - crate::a::b::c - X: t v - "###); -} diff --git a/crates/ra_hir/src/nameres/tests/primitives.rs b/crates/ra_hir/src/nameres/tests/primitives.rs deleted file mode 100644 index 0e2708658..000000000 --- a/crates/ra_hir/src/nameres/tests/primitives.rs +++ /dev/null @@ -1,24 +0,0 @@ -use super::*; - -#[test] -fn primitive_reexport() { - let map = def_map( - " - //- /lib.rs - mod foo; - use foo::int; - - //- /foo.rs - pub use i32 as int; - ", - ); - assert_snapshot!(map, @r###" - ⋮crate - ⋮foo: t - ⋮int: t - ⋮ - ⋮crate::foo - ⋮int: t - "### - ); -} diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml index 746c907e8..15055db64 100644 --- a/crates/ra_hir_def/Cargo.toml +++ b/crates/ra_hir_def/Cargo.toml @@ -19,3 +19,7 @@ test_utils = { path = "../test_utils" } mbe = { path = "../ra_mbe", package = "ra_mbe" } ra_cfg = { path = "../ra_cfg" } tt = { path = "../ra_tt", package = "ra_tt" } + +[dev-dependencies] +insta = "0.12.0" + diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs index db59344aa..b3640da3d 100644 --- a/crates/ra_hir_def/src/nameres.rs +++ b/crates/ra_hir_def/src/nameres.rs @@ -6,6 +6,9 @@ pub mod per_ns; pub mod collector; pub mod mod_resolution; +#[cfg(test)] +mod tests; + use std::sync::Arc; use hir_expand::{diagnostics::DiagnosticSink, name::Name, MacroDefId}; diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs new file mode 100644 index 000000000..f9a8edd43 --- /dev/null +++ b/crates/ra_hir_def/src/nameres/tests.rs @@ -0,0 +1,522 @@ +mod globs; +mod incremental; +mod macros; +mod mod_resolution; +mod primitives; + +use std::sync::Arc; + +use insta::assert_snapshot; +use ra_db::{fixture::WithFixture, SourceDatabase}; +// use test_utils::covers; + +use crate::{db::DefDatabase2, nameres::*, test_db::TestDB, CrateModuleId}; + +fn def_map(fixtute: &str) -> String { + let dm = compute_crate_def_map(fixtute); + render_crate_def_map(&dm) +} + +fn compute_crate_def_map(fixture: &str) -> Arc { + let db = TestDB::with_files(fixture); + 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()); + return buf.trim().to_string(); + + fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: CrateModuleId) { + *buf += path; + *buf += "\n"; + + let mut entries = map.modules[module] + .scope + .items + .iter() + .map(|(name, res)| (name, res.def)) + .collect::>(); + entries.sort_by_key(|(name, _)| *name); + + for (name, res) in entries { + *buf += &format!("{}:", name); + + if res.types.is_some() { + *buf += " t"; + } + if res.values.is_some() { + *buf += " v"; + } + if res.macros.is_some() { + *buf += " m"; + } + if res.is_none() { + *buf += " _"; + } + + *buf += "\n"; + } + + for (name, child) in map.modules[module].children.iter() { + let path = path.to_string() + &format!("::{}", name); + go(buf, map, &path, *child); + } + } +} + +#[test] +fn crate_def_map_smoke_test() { + let map = def_map( + " + //- /lib.rs + mod foo; + struct S; + use crate::foo::bar::E; + use self::E::V; + + //- /foo/mod.rs + pub mod bar; + fn f() {} + + //- /foo/bar.rs + pub struct Baz; + enum E { V } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮E: t + ⋮S: t v + ⋮V: t v + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮f: v + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + ⋮E: t + "###) +} + +#[test] +fn bogus_paths() { + // covers!(bogus_paths); + let map = def_map( + " + //- /lib.rs + mod foo; + struct S; + use self; + + //- /foo/mod.rs + use super; + use crate; + + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮S: t v + ⋮foo: t + ⋮ + ⋮crate::foo + "### + ) +} + +#[test] +fn use_as() { + let map = def_map( + " + //- /lib.rs + mod foo; + + use crate::foo::Baz as Foo; + + //- /foo/mod.rs + pub struct Baz; + ", + ); + assert_snapshot!(map, + @r###" + ⋮crate + ⋮Foo: t v + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + "### + ); +} + +#[test] +fn use_trees() { + let map = def_map( + " + //- /lib.rs + mod foo; + + use crate::foo::bar::{Baz, Quux}; + + //- /foo/mod.rs + pub mod bar; + + //- /foo/bar.rs + pub struct Baz; + pub enum Quux {}; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + ⋮Quux: t + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + ⋮Quux: t + "###); +} + +#[test] +fn re_exports() { + let map = def_map( + " + //- /lib.rs + mod foo; + + use self::foo::Baz; + + //- /foo/mod.rs + pub mod bar; + + pub use self::bar::Baz; + + //- /foo/bar.rs + pub struct Baz; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn std_prelude() { + // covers!(std_prelude); + let map = def_map( + " + //- /main.rs crate:main deps:test_crate + use Foo::*; + + //- /lib.rs crate:test_crate + mod prelude; + #[prelude_import] + use prelude::*; + + //- /prelude.rs + pub enum Foo { Bar, Baz }; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Baz: t v + "###); +} + +#[test] +fn can_import_enum_variant() { + // covers!(can_import_enum_variant); + let map = def_map( + " + //- /lib.rs + enum E { V } + use self::E::V; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮E: t + ⋮V: t v + "### + ); +} + +#[test] +fn edition_2015_imports() { + let map = def_map( + " + //- /main.rs crate:main deps:other_crate edition:2015 + mod foo; + mod bar; + + //- /bar.rs + struct Bar; + + //- /foo.rs + use bar::Bar; + use other_crate::FromLib; + + //- /lib.rs crate:other_crate edition:2018 + struct FromLib; + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮bar: t + ⋮foo: t + ⋮ + ⋮crate::bar + ⋮Bar: t v + ⋮ + ⋮crate::foo + ⋮Bar: t v + ⋮FromLib: t v + "###); +} + +#[test] +fn item_map_using_self() { + let map = def_map( + " + //- /lib.rs + mod foo; + use crate::foo::bar::Baz::{self}; + //- /foo/mod.rs + pub mod bar; + //- /foo/bar.rs + pub struct Baz; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn item_map_across_crates() { + let map = def_map( + " + //- /main.rs crate:main deps:test_crate + use test_crate::Baz; + + //- /lib.rs crate:test_crate + pub struct Baz; + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + "###); +} + +#[test] +fn extern_crate_rename() { + let map = def_map( + " + //- /main.rs crate:main deps:alloc + extern crate alloc as alloc_crate; + + mod alloc; + mod sync; + + //- /sync.rs + use alloc_crate::Arc; + + //- /lib.rs crate:alloc + struct Arc; + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮alloc_crate: t + ⋮sync: t + ⋮ + ⋮crate::sync + ⋮Arc: t v + "###); +} + +#[test] +fn extern_crate_rename_2015_edition() { + let map = def_map( + " + //- /main.rs crate:main deps:alloc edition:2015 + extern crate alloc as alloc_crate; + + mod alloc; + mod sync; + + //- /sync.rs + use alloc_crate::Arc; + + //- /lib.rs crate:alloc + struct Arc; + ", + ); + + assert_snapshot!(map, + @r###" + ⋮crate + ⋮alloc_crate: t + ⋮sync: t + ⋮ + ⋮crate::sync + ⋮Arc: t v + "### + ); +} + +#[test] +fn import_across_source_roots() { + let map = def_map( + " + //- /main.rs crate:main deps:test_crate + use test_crate::a::b::C; + + //- root /test_crate/ + + //- /test_crate/lib.rs crate:test_crate + pub mod a { + pub mod b { + pub struct C; + } + } + + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮C: t v + "###); +} + +#[test] +fn reexport_across_crates() { + let map = def_map( + " + //- /main.rs crate:main deps:test_crate + use test_crate::Baz; + + //- /lib.rs crate:test_crate + pub use foo::Baz; + + mod foo; + + //- /foo.rs + pub struct Baz; + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + "###); +} + +#[test] +fn values_dont_shadow_extern_crates() { + let map = def_map( + " + //- /main.rs crate:main deps:foo + fn foo() {} + use foo::Bar; + + //- /foo/lib.rs crate:foo + pub struct Bar; + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮foo: v + "###); +} + +#[test] +fn cfg_not_test() { + let map = def_map( + r#" + //- /main.rs crate:main deps:std + use {Foo, Bar, Baz}; + + //- /lib.rs crate:std + #[prelude_import] + pub use self::prelude::*; + mod prelude { + #[cfg(test)] + pub struct Foo; + #[cfg(not(test))] + pub struct Bar; + #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] + pub struct Baz; + } + "#, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Baz: _ + ⋮Foo: _ + "###); +} + +#[test] +fn cfg_test() { + let map = def_map( + r#" + //- /main.rs crate:main deps:std + use {Foo, Bar, Baz}; + + //- /lib.rs crate:std cfg:test,feature=foo,feature=bar,opt=42 + #[prelude_import] + pub use self::prelude::*; + mod prelude { + #[cfg(test)] + pub struct Foo; + #[cfg(not(test))] + pub struct Bar; + #[cfg(all(not(any()), feature = "foo", feature = "bar", opt = "42"))] + pub struct Baz; + } + "#, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: _ + ⋮Baz: t v + ⋮Foo: t v + "###); +} diff --git a/crates/ra_hir_def/src/nameres/tests/globs.rs b/crates/ra_hir_def/src/nameres/tests/globs.rs new file mode 100644 index 000000000..cf4a2a851 --- /dev/null +++ b/crates/ra_hir_def/src/nameres/tests/globs.rs @@ -0,0 +1,114 @@ +use super::*; + +#[test] +fn glob_1() { + let map = def_map( + " + //- /lib.rs + mod foo; + use foo::*; + + //- /foo/mod.rs + pub mod bar; + pub use self::bar::Baz; + pub struct Foo; + + //- /foo/bar.rs + pub struct Baz; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + ⋮Foo: t v + ⋮bar: t + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + ⋮Foo: t v + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "### + ); +} + +#[test] +fn glob_2() { + let map = def_map( + " + //- /lib.rs + mod foo; + use foo::*; + + //- /foo/mod.rs + pub mod bar; + pub use self::bar::*; + pub struct Foo; + + //- /foo/bar.rs + pub struct Baz; + pub use super::*; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + ⋮Foo: t v + ⋮bar: t + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + ⋮Foo: t v + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + ⋮Foo: t v + ⋮bar: t + "### + ); +} + +#[test] +fn glob_across_crates() { + // covers!(glob_across_crates); + let map = def_map( + " + //- /main.rs crate:main deps:test_crate + use test_crate::*; + + //- /lib.rs crate:test_crate + pub struct Baz; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + "### + ); +} + +#[test] +fn glob_enum() { + // covers!(glob_enum); + let map = def_map( + " + //- /lib.rs + enum Foo { + Bar, Baz + } + use self::Foo::*; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Baz: t v + ⋮Foo: t + "### + ); +} diff --git a/crates/ra_hir_def/src/nameres/tests/incremental.rs b/crates/ra_hir_def/src/nameres/tests/incremental.rs new file mode 100644 index 000000000..80dcec62f --- /dev/null +++ b/crates/ra_hir_def/src/nameres/tests/incremental.rs @@ -0,0 +1,133 @@ +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) = TestDB::with_position(initial); + let krate = db.crate_graph().iter().next().unwrap(); + { + let events = db.log_executed(|| { + db.crate_def_map(krate); + }); + assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) + } + db.set_file_text(pos.file_id, Arc::new(file_change.to_string())); + + { + let events = db.log_executed(|| { + db.crate_def_map(krate); + }); + assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) + } +} + +#[test] +fn typing_inside_a_function_should_not_invalidate_def_map() { + check_def_map_is_not_recomputed( + " + //- /lib.rs + mod foo;<|> + + use crate::foo::bar::Baz; + + fn foo() -> i32 { + 1 + 1 + } + //- /foo/mod.rs + pub mod bar; + + //- /foo/bar.rs + pub struct Baz; + ", + " + mod foo; + + use crate::foo::bar::Baz; + + fn foo() -> i32 { 92 } + ", + ); +} + +#[test] +fn adding_inner_items_should_not_invalidate_def_map() { + check_def_map_is_not_recomputed( + " + //- /lib.rs + struct S { a: i32} + enum E { A } + trait T { + fn a() {} + } + mod foo;<|> + impl S { + fn a() {} + } + use crate::foo::bar::Baz; + //- /foo/mod.rs + pub mod bar; + + //- /foo/bar.rs + pub struct Baz; + ", + " + struct S { a: i32, b: () } + enum E { A, B } + trait T { + fn a() {} + fn b() {} + } + mod foo;<|> + impl S { + fn a() {} + fn b() {} + } + use crate::foo::bar::Baz; + ", + ); +} + +#[test] +fn typing_inside_a_macro_should_not_invalidate_def_map() { + let (mut db, pos) = TestDB::with_position( + " + //- /lib.rs + macro_rules! m { + ($ident:ident) => { + fn f() { + $ident + $ident; + }; + } + } + mod foo; + + //- /foo/mod.rs + pub mod bar; + + //- /foo/bar.rs + <|> + m!(X); + ", + ); + let krate = db.crate_graph().iter().next().unwrap(); + { + let events = db.log_executed(|| { + let crate_def_map = db.crate_def_map(krate); + let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); + assert_eq!(module_data.scope.items.len(), 1); + }); + assert!(format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) + } + db.set_file_text(pos.file_id, Arc::new("m!(Y);".to_string())); + + { + let events = db.log_executed(|| { + let crate_def_map = db.crate_def_map(krate); + let (_, module_data) = crate_def_map.modules.iter().last().unwrap(); + assert_eq!(module_data.scope.items.len(), 1); + }); + assert!(!format!("{:?}", events).contains("crate_def_map"), "{:#?}", events) + } +} diff --git a/crates/ra_hir_def/src/nameres/tests/macros.rs b/crates/ra_hir_def/src/nameres/tests/macros.rs new file mode 100644 index 000000000..9bb3895ad --- /dev/null +++ b/crates/ra_hir_def/src/nameres/tests/macros.rs @@ -0,0 +1,602 @@ +use super::*; + +#[test] +fn macro_rules_are_globally_visible() { + let map = def_map( + " + //- /lib.rs + macro_rules! structs { + ($($i:ident),*) => { + $(struct $i { field: u32 } )* + } + } + structs!(Foo); + mod nested; + + //- /nested.rs + structs!(Bar, Baz); + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Foo: t v + ⋮nested: t + ⋮ + ⋮crate::nested + ⋮Bar: t v + ⋮Baz: t v + "###); +} + +#[test] +fn macro_rules_can_define_modules() { + let map = def_map( + " + //- /lib.rs + macro_rules! m { + ($name:ident) => { mod $name; } + } + m!(n1); + + mod m { + m!(n3) + } + + //- /n1.rs + m!(n2) + //- /n1/n2.rs + struct X; + //- /m/n3.rs + struct Y; + ", + ); + assert_snapshot!(map, @r###" + crate + m: t + n1: t + + crate::m + n3: t + + crate::m::n3 + Y: t v + + crate::n1 + n2: t + + crate::n1::n2 + X: t v + "###); +} + +#[test] +fn macro_rules_from_other_crates_are_visible() { + let map = def_map( + " + //- /main.rs crate:main deps:foo + foo::structs!(Foo, Bar) + mod bar; + + //- /bar.rs + use crate::*; + + //- /lib.rs crate:foo + #[macro_export] + macro_rules! structs { + ($($i:ident),*) => { + $(struct $i { field: u32 } )* + } + } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Foo: t v + ⋮bar: t + ⋮ + ⋮crate::bar + ⋮Bar: t v + ⋮Foo: t v + ⋮bar: t + "###); +} + +#[test] +fn macro_rules_export_with_local_inner_macros_are_visible() { + let map = def_map( + " + //- /main.rs crate:main deps:foo + foo::structs!(Foo, Bar) + mod bar; + + //- /bar.rs + use crate::*; + + //- /lib.rs crate:foo + #[macro_export(local_inner_macros)] + macro_rules! structs { + ($($i:ident),*) => { + $(struct $i { field: u32 } )* + } + } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Foo: t v + ⋮bar: t + ⋮ + ⋮crate::bar + ⋮Bar: t v + ⋮Foo: t v + ⋮bar: t + "###); +} + +#[test] +fn unexpanded_macro_should_expand_by_fixedpoint_loop() { + let map = def_map( + " + //- /main.rs crate:main deps:foo + macro_rules! baz { + () => { + use foo::bar; + } + } + + foo!(); + bar!(); + baz!(); + + //- /lib.rs crate:foo + #[macro_export] + macro_rules! foo { + () => { + struct Foo { field: u32 } + } + } + #[macro_export] + macro_rules! bar { + () => { + use foo::foo; + } + } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Foo: t v + ⋮bar: m + ⋮foo: m + "###); +} + +#[test] +fn 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( + " + //- /main.rs crate:main deps:foo + structs!(Foo); + structs_priv!(Bar); + structs_not_exported!(MacroNotResolved1); + crate::structs!(MacroNotResolved2); + + mod bar; + + #[macro_use] + extern crate foo; + + //- /bar.rs + structs!(Baz); + crate::structs!(MacroNotResolved3); + + //- /lib.rs crate:foo + #[macro_export] + macro_rules! structs { + ($i:ident) => { struct $i; } + } + + macro_rules! structs_not_exported { + ($i:ident) => { struct $i; } + } + + mod priv_mod { + #[macro_export] + macro_rules! structs_priv { + ($i:ident) => { struct $i; } + } + } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Foo: t v + ⋮bar: t + ⋮foo: t + ⋮ + ⋮crate::bar + ⋮Baz: t v + "###); +} + +#[test] +fn prelude_is_macro_use() { + // covers!(prelude_is_macro_use); + let map = def_map( + " + //- /main.rs crate:main deps:foo + structs!(Foo); + structs_priv!(Bar); + structs_outside!(Out); + crate::structs!(MacroNotResolved2); + + mod bar; + + //- /bar.rs + structs!(Baz); + crate::structs!(MacroNotResolved3); + + //- /lib.rs crate:foo + #[prelude_import] + use self::prelude::*; + + mod prelude { + #[macro_export] + macro_rules! structs { + ($i:ident) => { struct $i; } + } + + mod priv_mod { + #[macro_export] + macro_rules! structs_priv { + ($i:ident) => { struct $i; } + } + } + } + + #[macro_export] + macro_rules! structs_outside { + ($i:ident) => { struct $i; } + } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Foo: t v + ⋮Out: t v + ⋮bar: t + ⋮ + ⋮crate::bar + ⋮Baz: t v + "###); +} + +#[test] +fn prelude_cycle() { + let map = def_map( + " + //- /lib.rs + #[prelude_import] + use self::prelude::*; + + declare_mod!(); + + mod prelude { + macro_rules! declare_mod { + () => (mod foo {}) + } + } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮prelude: t + ⋮ + ⋮crate::prelude + "###); +} + +#[test] +fn plain_macros_are_legacy_textual_scoped() { + let map = def_map( + r#" + //- /main.rs + mod m1; + bar!(NotFoundNotMacroUse); + + mod m2 { + foo!(NotFoundBeforeInside2); + } + + macro_rules! foo { + ($x:ident) => { struct $x; } + } + foo!(Ok); + + mod m3; + foo!(OkShadowStop); + bar!(NotFoundMacroUseStop); + + #[macro_use] + mod m5 { + #[macro_use] + mod m6 { + macro_rules! foo { + ($x:ident) => { fn $x() {} } + } + } + } + foo!(ok_double_macro_use_shadow); + + baz!(NotFoundBefore); + #[macro_use] + mod m7 { + macro_rules! baz { + ($x:ident) => { struct $x; } + } + } + baz!(OkAfter); + + //- /m1.rs + foo!(NotFoundBeforeInside1); + macro_rules! bar { + ($x:ident) => { struct $x; } + } + + //- /m3/mod.rs + foo!(OkAfterInside); + macro_rules! foo { + ($x:ident) => { fn $x() {} } + } + foo!(ok_shadow); + + #[macro_use] + mod m4; + bar!(OkMacroUse); + + //- /m3/m4.rs + foo!(ok_shadow_deep); + macro_rules! bar { + ($x:ident) => { struct $x; } + } + "#, + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Ok: t v + ⋮OkAfter: t v + ⋮OkShadowStop: t v + ⋮m1: t + ⋮m2: t + ⋮m3: t + ⋮m5: t + ⋮m7: t + ⋮ok_double_macro_use_shadow: v + ⋮ + ⋮crate::m7 + ⋮ + ⋮crate::m1 + ⋮ + ⋮crate::m5 + ⋮m6: t + ⋮ + ⋮crate::m5::m6 + ⋮ + ⋮crate::m2 + ⋮ + ⋮crate::m3 + ⋮OkAfterInside: t v + ⋮OkMacroUse: t v + ⋮m4: t + ⋮ok_shadow: v + ⋮ + ⋮crate::m3::m4 + ⋮ok_shadow_deep: v + "###); +} + +#[test] +fn type_value_macro_live_in_different_scopes() { + let map = def_map( + " + //- /main.rs + #[macro_export] + macro_rules! foo { + ($x:ident) => { type $x = (); } + } + + foo!(foo); + use foo as bar; + + use self::foo as baz; + fn baz() {} + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮bar: t m + ⋮baz: t v m + ⋮foo: t m + "###); +} + +#[test] +fn macro_use_can_be_aliased() { + let map = def_map( + " + //- /main.rs crate:main deps:foo + #[macro_use] + extern crate foo; + + foo!(Direct); + bar!(Alias); + + //- /lib.rs crate:foo + use crate::foo as bar; + + mod m { + #[macro_export] + macro_rules! foo { + ($x:ident) => { struct $x; } + } + } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Alias: t v + ⋮Direct: t v + ⋮foo: t + "###); +} + +#[test] +fn path_qualified_macros() { + let map = def_map( + " + //- /main.rs + macro_rules! foo { + ($x:ident) => { struct $x; } + } + + crate::foo!(NotResolved); + + crate::bar!(OkCrate); + bar!(OkPlain); + alias1!(NotHere); + m::alias1!(OkAliasPlain); + m::alias2!(OkAliasSuper); + m::alias3!(OkAliasCrate); + not_found!(NotFound); + + mod m { + #[macro_export] + macro_rules! bar { + ($x:ident) => { struct $x; } + } + + pub use bar as alias1; + pub use super::bar as alias2; + pub use crate::bar as alias3; + pub use self::bar as not_found; + } + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮OkAliasCrate: t v + ⋮OkAliasPlain: t v + ⋮OkAliasSuper: t v + ⋮OkCrate: t v + ⋮OkPlain: t v + ⋮bar: m + ⋮m: t + ⋮ + ⋮crate::m + ⋮alias1: m + ⋮alias2: m + ⋮alias3: m + ⋮not_found: _ + "###); +} + +#[test] +fn macro_dollar_crate_is_correct_in_item() { + // covers!(macro_dollar_crate_self); + // covers!(macro_dollar_crate_other); + let map = def_map( + " + //- /main.rs crate:main deps:foo + #[macro_use] + extern crate foo; + + #[macro_use] + mod m { + macro_rules! current { + () => { + use $crate::Foo as FooSelf; + } + } + } + + struct Foo; + + current!(); + not_current1!(); + foo::not_current2!(); + + //- /lib.rs crate:foo + mod m { + #[macro_export] + macro_rules! not_current1 { + () => { + use $crate::Bar; + } + } + } + + #[macro_export] + macro_rules! not_current2 { + () => { + use $crate::Baz; + } + } + + struct Bar; + struct Baz; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮Baz: t v + ⋮Foo: t v + ⋮FooSelf: t v + ⋮foo: t + ⋮m: t + ⋮ + ⋮crate::m + "###); +} + +#[test] +fn macro_dollar_crate_is_correct_in_indirect_deps() { + // covers!(macro_dollar_crate_other); + // From std + let map = def_map( + r#" + //- /main.rs crate:main deps:std + foo!(); + + //- /std.rs crate:std deps:core + #[prelude_import] + use self::prelude::*; + + pub use core::foo; + + mod prelude {} + + #[macro_use] + mod std_macros; + + //- /core.rs crate:core + #[macro_export] + macro_rules! foo { + () => { + use $crate::bar; + } + } + + pub struct bar; + "#, + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮bar: t v + "###); +} diff --git a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs new file mode 100644 index 000000000..8d804a63e --- /dev/null +++ b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs @@ -0,0 +1,782 @@ +use super::*; + +#[test] +fn name_res_works_for_broken_modules() { + // covers!(name_res_works_for_broken_modules); + let map = def_map( + " + //- /lib.rs + mod foo // no `;`, no body + + use self::foo::Baz; + + //- /foo/mod.rs + pub mod bar; + + pub use self::bar::Baz; + + //- /foo/bar.rs + pub struct Baz; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: _ + "###); +} + +#[test] +fn nested_module_resolution() { + let map = def_map( + " + //- /lib.rs + mod n1; + + //- /n1.rs + mod n2; + + //- /n1/n2.rs + struct X; + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮n1: t + ⋮ + ⋮crate::n1 + ⋮n2: t + ⋮ + ⋮crate::n1::n2 + ⋮X: t v + "###); +} + +#[test] +fn module_resolution_works_for_non_standard_filenames() { + let map = def_map( + " + //- /my_library.rs crate:my_library + mod foo; + use self::foo::Bar; + + //- /foo/mod.rs + pub struct Bar; + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Bar: t v + "###); +} + +#[test] +fn module_resolution_works_for_raw_modules() { + let map = def_map( + " + //- /lib.rs + mod r#async; + use self::r#async::Bar; + + //- /async.rs + pub struct Bar; + ", + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮async: t + ⋮ + ⋮crate::async + ⋮Bar: t v + "###); +} + +#[test] +fn module_resolution_decl_path() { + let map = def_map( + r###" + //- /lib.rs + #[path = "bar/baz/foo.rs"] + mod foo; + use self::foo::Bar; + + //- /bar/baz/foo.rs + pub struct Bar; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Bar: t v + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Bar: t v + "###); +} + +#[test] +fn module_resolution_module_with_path_in_mod_rs() { + let map = def_map( + r###" + //- /main.rs + mod foo; + + //- /foo/mod.rs + #[path = "baz.rs"] + pub mod bar; + + use self::bar::Baz; + + //- /foo/baz.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_module_with_path_non_crate_root() { + let map = def_map( + r###" + //- /main.rs + mod foo; + + //- /foo.rs + #[path = "baz.rs"] + pub mod bar; + + use self::bar::Baz; + + //- /baz.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_module_decl_path_super() { + let map = def_map( + r###" + //- /main.rs + #[path = "bar/baz/module.rs"] + mod foo; + pub struct Baz; + + //- /bar/baz/module.rs + use super::Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_explicit_path_mod_rs() { + let map = def_map( + r###" + //- /main.rs + #[path = "module/mod.rs"] + mod foo; + + //- /module/mod.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_relative_path() { + let map = def_map( + r###" + //- /main.rs + mod foo; + + //- /foo.rs + #[path = "./sub.rs"] + pub mod foo_bar; + + //- /sub.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮foo_bar: t + ⋮ + ⋮crate::foo::foo_bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_relative_path_2() { + let map = def_map( + r###" + //- /main.rs + mod foo; + + //- /foo/mod.rs + #[path="../sub.rs"] + pub mod foo_bar; + + //- /sub.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮foo_bar: t + ⋮ + ⋮crate::foo::foo_bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_explicit_path_mod_rs_2() { + let map = def_map( + r###" + //- /main.rs + #[path = "module/bar/mod.rs"] + mod foo; + + //- /module/bar/mod.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_explicit_path_mod_rs_with_win_separator() { + let map = def_map( + r###" + //- /main.rs + #[path = "module\bar\mod.rs"] + mod foo; + + //- /module/bar/mod.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_with_path_attribute() { + let map = def_map( + r###" + //- /main.rs + #[path = "models"] + mod foo { + mod bar; + } + + //- /models/bar.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module() { + let map = def_map( + r###" + //- /main.rs + mod foo { + mod bar; + } + + //- /foo/bar.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_2_with_path_attribute() { + let map = def_map( + r###" + //- /main.rs + #[path = "models/db"] + mod foo { + mod bar; + } + + //- /models/db/bar.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_3() { + let map = def_map( + r###" + //- /main.rs + #[path = "models/db"] + mod foo { + #[path = "users.rs"] + mod bar; + } + + //- /models/db/users.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_empty_path() { + let map = def_map( + r###" + //- /main.rs + #[path = ""] + mod foo { + #[path = "users.rs"] + mod bar; + } + + //- /users.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_empty_path() { + let map = def_map( + r###" + //- /main.rs + #[path = ""] // Should try to read `/` (a directory) + mod foo; + + //- /foo.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_relative_path() { + let map = def_map( + r###" + //- /main.rs + #[path = "./models"] + mod foo { + mod bar; + } + + //- /models/bar.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_in_crate_root() { + let map = def_map( + r###" + //- /main.rs + mod foo { + #[path = "baz.rs"] + mod bar; + } + use self::foo::bar::Baz; + + //- /foo/baz.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮Baz: t v + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_in_mod_rs() { + let map = def_map( + r###" + //- /main.rs + mod foo; + + //- /foo/mod.rs + mod bar { + #[path = "qwe.rs"] + pub mod baz; + } + use self::bar::baz::Baz; + + //- /foo/bar/qwe.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮baz: t + ⋮ + ⋮crate::foo::bar::baz + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_in_non_crate_root() { + let map = def_map( + r###" + //- /main.rs + mod foo; + + //- /foo.rs + mod bar { + #[path = "qwe.rs"] + pub mod baz; + } + use self::bar::baz::Baz; + + //- /foo/bar/qwe.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮baz: t + ⋮ + ⋮crate::foo::bar::baz + ⋮Baz: t v + "###); +} + +#[test] +fn module_resolution_decl_inside_inline_module_in_non_crate_root_2() { + let map = def_map( + r###" + //- /main.rs + mod foo; + + //- /foo.rs + #[path = "bar"] + mod bar { + pub mod baz; + } + use self::bar::baz::Baz; + + //- /bar/baz.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮ + ⋮crate::foo + ⋮Baz: t v + ⋮bar: t + ⋮ + ⋮crate::foo::bar + ⋮baz: t + ⋮ + ⋮crate::foo::bar::baz + ⋮Baz: t v + "###); +} + +#[test] +fn unresolved_module_diagnostics() { + let db = TestDB::with_files( + r" + //- /lib.rs + mod foo; + mod bar; + mod baz {} + //- /foo.rs + ", + ); + let krate = db.crate_graph().iter().next().unwrap(); + + let crate_def_map = db.crate_def_map(krate); + + insta::assert_debug_snapshot!( + crate_def_map.diagnostics, + @r###" + [ + UnresolvedModule { + module: CrateModuleId( + 0, + ), + declaration: AstId { + file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + file_ast_id: FileAstId { + raw: ErasedFileAstId( + 1, + ), + _ty: PhantomData, + }, + }, + candidate: "bar.rs", + }, + ] + "### + ); +} + +#[test] +fn module_resolution_decl_inside_module_in_non_crate_root_2() { + let map = def_map( + r###" + //- /main.rs + #[path="module/m2.rs"] + mod module; + + //- /module/m2.rs + pub mod submod; + + //- /module/submod.rs + pub struct Baz; + "###, + ); + + assert_snapshot!(map, @r###" + ⋮crate + ⋮module: t + ⋮ + ⋮crate::module + ⋮submod: t + ⋮ + ⋮crate::module::submod + ⋮Baz: t v + "###); +} + +#[test] +fn nested_out_of_line_module() { + let map = def_map( + r###" + //- /lib.rs + mod a { + mod b { + mod c; + } + } + + //- /a/b/c.rs + struct X; + "###, + ); + + assert_snapshot!(map, @r###" + crate + a: t + + crate::a + b: t + + crate::a::b + c: t + + crate::a::b::c + X: t v + "###); +} + +#[test] +fn nested_out_of_line_module_with_path() { + let map = def_map( + r###" + //- /lib.rs + mod a { + #[path = "d/e"] + mod b { + mod c; + } + } + + //- /a/d/e/c.rs + struct X; + "###, + ); + + assert_snapshot!(map, @r###" + crate + a: t + + crate::a + b: t + + crate::a::b + c: t + + crate::a::b::c + X: t v + "###); +} diff --git a/crates/ra_hir_def/src/nameres/tests/primitives.rs b/crates/ra_hir_def/src/nameres/tests/primitives.rs new file mode 100644 index 000000000..0e2708658 --- /dev/null +++ b/crates/ra_hir_def/src/nameres/tests/primitives.rs @@ -0,0 +1,24 @@ +use super::*; + +#[test] +fn primitive_reexport() { + let map = def_map( + " + //- /lib.rs + mod foo; + use foo::int; + + //- /foo.rs + pub use i32 as int; + ", + ); + assert_snapshot!(map, @r###" + ⋮crate + ⋮foo: t + ⋮int: t + ⋮ + ⋮crate::foo + ⋮int: t + "### + ); +} diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs index 67714c68e..05018f8e4 100644 --- a/crates/ra_hir_def/src/test_db.rs +++ b/crates/ra_hir_def/src/test_db.rs @@ -1,4 +1,7 @@ -use std::{panic, sync::Arc}; +use std::{ + panic, + sync::{Arc, Mutex}, +}; use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate}; use relative_path::RelativePath; @@ -13,12 +16,20 @@ use relative_path::RelativePath; #[derive(Debug, Default)] pub struct TestDB { runtime: salsa::Runtime, + events: Mutex>>>, } impl salsa::Database for TestDB { fn salsa_runtime(&self) -> &salsa::Runtime { &self.runtime } + + fn salsa_event(&self, event: impl Fn() -> salsa::Event) { + let mut events = self.events.lock().unwrap(); + if let Some(events) = &mut *events { + events.push(event()); + } + } } impl panic::RefUnwindSafe for TestDB {} @@ -38,3 +49,26 @@ impl FileLoader for TestDB { FileLoaderDelegate(self).relevant_crates(file_id) } } + +impl TestDB { + pub fn log(&self, f: impl FnOnce()) -> Vec> { + *self.events.lock().unwrap() = Some(Vec::new()); + f(); + self.events.lock().unwrap().take().unwrap() + } + + pub fn log_executed(&self, f: impl FnOnce()) -> Vec { + let events = self.log(f); + events + .into_iter() + .filter_map(|e| match e.kind { + // This pretty horrible, but `Debug` is the only way to inspect + // QueryDescriptor at the moment. + salsa::EventKind::WillExecute { database_key } => { + Some(format!("{:?}", database_key)) + } + _ => None, + }) + .collect() + } +} -- cgit v1.2.3 From 73fcf9a2d6ae14afa4e822370c0a46bf1d336081 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 3 Nov 2019 23:44:23 +0300 Subject: Restore crate_def_map marks --- crates/ra_hir_def/src/lib.rs | 2 ++ crates/ra_hir_def/src/marks.rs | 14 ++++++++++++++ crates/ra_hir_def/src/nameres.rs | 8 ++++---- crates/ra_hir_def/src/nameres/collector.rs | 15 +++++++-------- crates/ra_hir_def/src/nameres/raw.rs | 4 ++-- crates/ra_hir_def/src/nameres/tests.rs | 8 ++++---- crates/ra_hir_def/src/nameres/tests/globs.rs | 4 ++-- crates/ra_hir_def/src/nameres/tests/macros.rs | 10 +++++----- crates/ra_hir_def/src/nameres/tests/mod_resolution.rs | 2 +- 9 files changed, 41 insertions(+), 26 deletions(-) create mode 100644 crates/ra_hir_def/src/marks.rs (limited to 'crates') diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs index 02bd808fc..63ed2a098 100644 --- a/crates/ra_hir_def/src/lib.rs +++ b/crates/ra_hir_def/src/lib.rs @@ -17,6 +17,8 @@ pub mod diagnostics; #[cfg(test)] mod test_db; +#[cfg(test)] +mod marks; // FIXME: this should be private pub mod nameres; diff --git a/crates/ra_hir_def/src/marks.rs b/crates/ra_hir_def/src/marks.rs new file mode 100644 index 000000000..0b99eac71 --- /dev/null +++ b/crates/ra_hir_def/src/marks.rs @@ -0,0 +1,14 @@ +//! See test_utils/src/marks.rs + +test_utils::marks!( + bogus_paths + name_res_works_for_broken_modules + can_import_enum_variant + glob_enum + glob_across_crates + std_prelude + macro_rules_from_other_crates_are_visible_with_macro_use + prelude_is_macro_use + macro_dollar_crate_self + macro_dollar_crate_other +); diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs index b3640da3d..b5c682219 100644 --- a/crates/ra_hir_def/src/nameres.rs +++ b/crates/ra_hir_def/src/nameres.rs @@ -18,7 +18,7 @@ use ra_db::{CrateId, Edition, FileId}; use ra_prof::profile; use ra_syntax::ast; use rustc_hash::{FxHashMap, FxHashSet}; -// use test_utils::tested_by; +use test_utils::tested_by; use crate::{ builtin_type::BuiltinType, @@ -263,12 +263,12 @@ impl CrateDefMap { let mut curr_per_ns: PerNs = match path.kind { PathKind::DollarCrate(krate) => { if krate == self.krate { - // tested_by!(macro_dollar_crate_self); + tested_by!(macro_dollar_crate_self); PerNs::types(ModuleId { krate: self.krate, module_id: self.root }.into()) } else { let def_map = db.crate_def_map(krate); let module = ModuleId { krate, module_id: def_map.root }; - // tested_by!(macro_dollar_crate_other); + tested_by!(macro_dollar_crate_other); PerNs::types(module.into()) } } @@ -369,7 +369,7 @@ impl CrateDefMap { } ModuleDefId::AdtId(AdtId::EnumId(e)) => { // enum variant - // tested_by!(can_import_enum_variant); + tested_by!(can_import_enum_variant); let enum_data = db.enum_data(e); match enum_data.variant(&segment.name) { Some(local_id) => { diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 0bc36910c..3b61d9895 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs @@ -8,7 +8,7 @@ use ra_cfg::CfgOptions; use ra_db::{CrateId, FileId}; use ra_syntax::{ast, SmolStr}; use rustc_hash::FxHashMap; -// use test_utils::tested_by; +use test_utils::tested_by; use crate::{ attr::Attr, @@ -218,7 +218,7 @@ where ); if let Some(ModuleDefId::ModuleId(m)) = res.take_types() { - // tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use); + tested_by!(macro_rules_from_other_crates_are_visible_with_macro_use); self.import_all_macros_exported(current_module_id, m.krate); } } @@ -294,10 +294,10 @@ where match def.take_types() { Some(ModuleDefId::ModuleId(m)) => { if import.is_prelude { - // tested_by!(std_prelude); + tested_by!(std_prelude); self.def_map.prelude = Some(m); } else if m.krate != self.def_map.krate { - // tested_by!(glob_across_crates); + 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.module_id].scope; @@ -332,7 +332,7 @@ where } } Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => { - // tested_by!(glob_enum); + tested_by!(glob_enum); // glob import from enum => just import all the variants let enum_data = self.db.enum_data(e); let resolutions = enum_data @@ -373,8 +373,7 @@ where let resolution = Resolution { def, import: Some(import_id) }; self.update(module_id, Some(import_id), &[(name, resolution)]); } - // tested_by!(bogus_paths), - None => (), + None => tested_by!(bogus_paths), } } } @@ -534,7 +533,7 @@ where // 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); + tested_by!(prelude_is_macro_use); self.def_collector.import_all_macros_exported(self.module_id, prelude_module.krate); } } diff --git a/crates/ra_hir_def/src/nameres/raw.rs b/crates/ra_hir_def/src/nameres/raw.rs index 86c05d602..cb47fa317 100644 --- a/crates/ra_hir_def/src/nameres/raw.rs +++ b/crates/ra_hir_def/src/nameres/raw.rs @@ -14,6 +14,7 @@ use ra_syntax::{ ast::{self, AttrsOwner, NameOwner}, AstNode, AstPtr, SourceFile, }; +use test_utils::tested_by; use crate::{attr::Attr, db::DefDatabase2, path::Path, FileAstId, HirFileId, ModuleSource, Source}; @@ -297,8 +298,7 @@ impl RawItemsCollector { self.push_item(current_module, attrs, RawItemKind::Module(item)); return; } - // FIXME: restore this mark once we complete hir splitting - // tested_by!(name_res_works_for_broken_modules); + tested_by!(name_res_works_for_broken_modules); } fn add_use_item(&mut self, current_module: Option, use_item: ast::UseItem) { diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs index f9a8edd43..52bd0aa91 100644 --- a/crates/ra_hir_def/src/nameres/tests.rs +++ b/crates/ra_hir_def/src/nameres/tests.rs @@ -8,7 +8,7 @@ use std::sync::Arc; use insta::assert_snapshot; use ra_db::{fixture::WithFixture, SourceDatabase}; -// use test_utils::covers; +use test_utils::covers; use crate::{db::DefDatabase2, nameres::*, test_db::TestDB, CrateModuleId}; @@ -104,7 +104,7 @@ fn crate_def_map_smoke_test() { #[test] fn bogus_paths() { - // covers!(bogus_paths); + covers!(bogus_paths); let map = def_map( " //- /lib.rs @@ -219,7 +219,7 @@ fn re_exports() { #[test] fn std_prelude() { - // covers!(std_prelude); + covers!(std_prelude); let map = def_map( " //- /main.rs crate:main deps:test_crate @@ -243,7 +243,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_def/src/nameres/tests/globs.rs b/crates/ra_hir_def/src/nameres/tests/globs.rs index cf4a2a851..5b03fe365 100644 --- a/crates/ra_hir_def/src/nameres/tests/globs.rs +++ b/crates/ra_hir_def/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( " //- /main.rs crate:main deps:test_crate @@ -94,7 +94,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_def/src/nameres/tests/macros.rs b/crates/ra_hir_def/src/nameres/tests/macros.rs index 9bb3895ad..704065633 100644 --- a/crates/ra_hir_def/src/nameres/tests/macros.rs +++ b/crates/ra_hir_def/src/nameres/tests/macros.rs @@ -175,7 +175,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( " //- /main.rs crate:main deps:foo @@ -225,7 +225,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( " //- /main.rs crate:main deps:foo @@ -507,8 +507,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( " //- /main.rs crate:main deps:foo @@ -566,7 +566,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( r#" diff --git a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs index 8d804a63e..dee364a14 100644 --- a/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs +++ b/crates/ra_hir_def/src/nameres/tests/mod_resolution.rs @@ -2,7 +2,7 @@ use super::*; #[test] fn name_res_works_for_broken_modules() { - // covers!(name_res_works_for_broken_modules); + covers!(name_res_works_for_broken_modules); let map = def_map( " //- /lib.rs -- cgit v1.2.3 From f0eb9cc6e66a65d2df42c662499ebd77ea980de5 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 3 Nov 2019 23:49:44 +0300 Subject: Remove last traces of nameres from hir --- crates/ra_hir/src/code_model.rs | 3 +-- crates/ra_hir/src/db.rs | 3 +-- crates/ra_hir/src/lib.rs | 6 +++-- crates/ra_hir/src/nameres.rs | 53 -------------------------------------- crates/ra_hir/src/resolve.rs | 4 +-- crates/ra_hir/src/ty/infer/expr.rs | 3 +-- crates/ra_hir/src/ty/lower.rs | 5 ++-- crates/ra_hir_def/src/nameres.rs | 49 ++++++++++++++++++++++++++++++++++- 8 files changed, 59 insertions(+), 67 deletions(-) delete mode 100644 crates/ra_hir/src/nameres.rs (limited to 'crates') diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index f03b59217..181c5d47a 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -28,11 +28,10 @@ use crate::{ TypeAliasId, }, impl_block::ImplBlock, - nameres::{ImportId, Namespace}, resolve::{Resolver, Scope, TypeNs}, traits::TraitData, ty::{InferenceResult, TraitRef}, - Either, HasSource, Name, ScopeDef, Ty, + Either, HasSource, Name, ScopeDef, Ty, {ImportId, Namespace}, }; /// hir::Crate describes a single crate. It's the main interface with which diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index f45804c7c..eb66325f7 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -11,14 +11,13 @@ use crate::{ ids, impl_block::{ImplBlock, ImplSourceMap, ModuleImplBlocks}, lang_item::{LangItemTarget, LangItems}, - nameres::Namespace, traits::TraitData, ty::{ method_resolution::CrateImplBlocks, traits::Impl, CallableDef, FnSig, GenericPredicate, InferenceResult, Substs, Ty, TypableDef, TypeCtor, }, type_alias::TypeAliasData, - Const, ConstData, Crate, DefWithBody, ExprScopes, FnData, Function, Module, Static, + Const, ConstData, Crate, DefWithBody, ExprScopes, FnData, Function, Module, Namespace, Static, StructField, Trait, TypeAlias, }; diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 52bad2228..3ba99d92d 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -34,7 +34,6 @@ pub mod mock; pub mod source_binder; mod ids; -mod nameres; mod adt; mod traits; mod type_alias; @@ -73,7 +72,6 @@ pub use crate::{ generics::{GenericDef, GenericParam, GenericParams, HasGenericParams}, ids::{HirFileId, MacroCallId, MacroCallLoc, MacroDefId, MacroFile}, impl_block::ImplBlock, - nameres::{ImportId, Namespace, PerNs}, resolve::ScopeDef, source_binder::{PathResolution, ScopeEntryWithSyntax, SourceAnalyzer}, ty::{ @@ -83,6 +81,10 @@ pub use crate::{ pub use hir_def::{ builtin_type::BuiltinType, + nameres::{ + per_ns::{Namespace, PerNs}, + raw::ImportId, + }, path::{Path, PathKind}, type_ref::Mutability, }; diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs deleted file mode 100644 index 875addc84..000000000 --- a/crates/ra_hir/src/nameres.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! This module implements import-resolution/macro expansion algorithm. -//! -//! The result of this module is `CrateDefMap`: a data structure 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 -//! strict ordering. -//! -//! ## Collecting RawItems -//! -//! This happens in the `raw` module, which parses a single source file into a -//! set of top-level items. Nested imports 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 unresolved imports and macros. -//! -//! While we walk tree of modules, we also record macro_rules definitions and -//! expand calls to macro_rules defined macros. -//! -//! ## Resolving Imports -//! -//! We maintain a list of currently unresolved imports. On every iteration, we -//! try to resolve some imports from this list. If the import is resolved, we -//! record it, by adding an item to current module scope and, if necessary, by -//! recursively populating glob imports. -//! -//! ## Resolving Macros -//! -//! macro_rules from the same crate use a global mutable namespace. We expand -//! them immediately, when we collect modules. -//! -//! Macros from other crates (including proc-macros) can be used with -//! `foo::bar!` syntax. We handle them similarly to imports. There's a list of -//! unexpanded macros. On every iteration, we try to resolve each macro call -//! path and, upon success, we run macro expansion and "collect module" phase -//! on the result - -pub use hir_def::nameres::{ - per_ns::{Namespace, PerNs}, - raw::ImportId, -}; diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index 3e3f8c252..b932b0c8c 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs @@ -19,8 +19,8 @@ use crate::{ }, generics::GenericParams, impl_block::ImplBlock, - nameres::PerNs, - Adt, Const, Enum, EnumVariant, Function, MacroDef, ModuleDef, Static, Struct, Trait, TypeAlias, + Adt, Const, Enum, EnumVariant, Function, MacroDef, ModuleDef, PerNs, Static, Struct, Trait, + TypeAlias, }; #[derive(Debug, Clone, Default)] diff --git a/crates/ra_hir/src/ty/infer/expr.rs b/crates/ra_hir/src/ty/infer/expr.rs index fed52df39..a09ef5c5d 100644 --- a/crates/ra_hir/src/ty/infer/expr.rs +++ b/crates/ra_hir/src/ty/infer/expr.rs @@ -11,12 +11,11 @@ use crate::{ db::HirDatabase, expr::{self, Array, BinaryOp, Expr, ExprId, Literal, Statement, UnaryOp}, generics::{GenericParams, HasGenericParams}, - nameres::Namespace, ty::{ autoderef, method_resolution, op, primitive, CallableDef, InferTy, Mutability, Obligation, ProjectionPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor, TypeWalk, }, - Adt, Name, + Adt, Name, Namespace, }; impl<'a, D: HirDatabase> InferenceContext<'a, D> { diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 8e2834307..e29ab8492 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -23,15 +23,14 @@ use crate::{ db::HirDatabase, generics::HasGenericParams, generics::{GenericDef, WherePredicate}, - nameres::Namespace, resolve::{Resolver, TypeNs}, ty::{ primitive::{FloatTy, IntTy}, Adt, }, util::make_mut_slice, - Const, Enum, EnumVariant, Function, ModuleDef, Path, Static, Struct, StructField, Trait, - TypeAlias, Union, + Const, Enum, EnumVariant, Function, ModuleDef, Namespace, Path, Static, Struct, StructField, + Trait, TypeAlias, Union, }; impl Ty { diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs index b5c682219..fbd4248e6 100644 --- a/crates/ra_hir_def/src/nameres.rs +++ b/crates/ra_hir_def/src/nameres.rs @@ -1,4 +1,51 @@ -//! FIXME: write short doc here +//! This module implements import-resolution/macro expansion algorithm. +//! +//! The result of this module is `CrateDefMap`: a data structure 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 +//! strict ordering. +//! +//! ## Collecting RawItems +//! +//! This happens in the `raw` module, which parses a single source file into a +//! set of top-level items. Nested imports 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 unresolved imports and macros. +//! +//! While we walk tree of modules, we also record macro_rules definitions and +//! expand calls to macro_rules defined macros. +//! +//! ## Resolving Imports +//! +//! We maintain a list of currently unresolved imports. On every iteration, we +//! try to resolve some imports from this list. If the import is resolved, we +//! record it, by adding an item to current module scope and, if necessary, by +//! recursively populating glob imports. +//! +//! ## Resolving Macros +//! +//! macro_rules from the same crate use a global mutable namespace. We expand +//! them immediately, when we collect modules. +//! +//! Macros from other crates (including proc-macros) can be used with +//! `foo::bar!` syntax. We handle them similarly to imports. There's a list of +//! unexpanded macros. On every iteration, we try to resolve each macro call +//! path and, upon success, we run macro expansion and "collect module" phase +//! on the result // FIXME: review privacy of submodules pub mod raw; -- cgit v1.2.3 From 8922a44395482896fec0c0a47a7fac4612112d45 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 4 Nov 2019 00:11:37 +0300 Subject: Add small module-level docs --- crates/ra_hir/src/from_id.rs | 5 +++++ crates/ra_hir_def/src/diagnostics.rs | 2 ++ crates/ra_hir_def/src/test_db.rs | 2 ++ 3 files changed, 9 insertions(+) (limited to 'crates') diff --git a/crates/ra_hir/src/from_id.rs b/crates/ra_hir/src/from_id.rs index c08203bca..089dbc908 100644 --- a/crates/ra_hir/src/from_id.rs +++ b/crates/ra_hir/src/from_id.rs @@ -1,3 +1,8 @@ +//! Utility module for converting between hir_def ids and code_model wrappers. +//! +//! It's unclear if we need this long-term, but it's definitelly useful while we +//! are splitting the hir. + use hir_def::{AdtId, EnumVariantId, ModuleDefId}; use crate::{Adt, EnumVariant, ModuleDef}; diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs index 637184c58..1c1ccdecb 100644 --- a/crates/ra_hir_def/src/diagnostics.rs +++ b/crates/ra_hir_def/src/diagnostics.rs @@ -1,3 +1,5 @@ +//! Diagnostics produced by `hir_def`. + use std::any::Any; use hir_expand::diagnostics::Diagnostic; diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs index 05018f8e4..f91a5b41d 100644 --- a/crates/ra_hir_def/src/test_db.rs +++ b/crates/ra_hir_def/src/test_db.rs @@ -1,3 +1,5 @@ +//! Database used for testing `hir_def`. + use std::{ panic, sync::{Arc, Mutex}, -- cgit v1.2.3