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_def/src/nameres/collector.rs | 845 +++++++++++++++++++++++++++++ crates/ra_hir_def/src/nameres/per_ns.rs | 82 +++ 2 files changed, 927 insertions(+) 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/ra_hir_def/src/nameres') 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), + } + } +} -- 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_hir_def/src/nameres/collector.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'crates/ra_hir_def/src/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 { -- 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_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 + 6 files changed, 2177 insertions(+) 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/ra_hir_def/src/nameres') 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 + "### + ); +} -- 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/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 +- 6 files changed, 21 insertions(+), 22 deletions(-) (limited to 'crates/ra_hir_def/src/nameres') 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 3603d0213480c7b3423345d21243397eb904a073 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 4 Nov 2019 01:14:17 +0300 Subject: Reexport relative_path from ra_db --- crates/ra_hir_def/src/nameres/mod_resolution.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'crates/ra_hir_def/src/nameres') diff --git a/crates/ra_hir_def/src/nameres/mod_resolution.rs b/crates/ra_hir_def/src/nameres/mod_resolution.rs index 7d7e2779a..f6b0b8fb1 100644 --- a/crates/ra_hir_def/src/nameres/mod_resolution.rs +++ b/crates/ra_hir_def/src/nameres/mod_resolution.rs @@ -1,8 +1,7 @@ //! This module resolves `mod foo;` declaration to file. use hir_expand::name::Name; -use ra_db::FileId; +use ra_db::{FileId, RelativePathBuf}; use ra_syntax::SmolStr; -use relative_path::RelativePathBuf; use crate::{db::DefDatabase2, HirFileId}; -- cgit v1.2.3