From 35cfb418baa8c7f37a27152bcb1894275c2abee2 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 10 Feb 2019 15:16:23 +0100 Subject: Add some tests --- crates/ra_hir/src/marks.rs | 1 + crates/ra_hir/src/nameres/tests.rs | 119 +++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) (limited to 'crates') diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs index aba0c9968..5732eeccf 100644 --- a/crates/ra_hir/src/marks.rs +++ b/crates/ra_hir/src/marks.rs @@ -4,4 +4,5 @@ test_utils::marks!( type_var_cycles_resolve_completely type_var_cycles_resolve_as_possible type_var_resolves_to_int_var + glob_enum ); diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index 3dfad6bf2..91566fc98 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs @@ -164,6 +164,125 @@ fn re_exports() { ); } +#[test] +fn glob_1() { + let (item_map, module_id) = item_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; + ", + ); + check_module_item_map( + &item_map, + module_id, + " + Baz: t v + Foo: t v + bar: t + foo: t + ", + ); +} + +#[test] +fn glob_2() { + let (item_map, module_id) = item_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::*; + ", + ); + check_module_item_map( + &item_map, + module_id, + " + Baz: t v + Foo: t v + bar: t + foo: t + ", + ); +} + +#[test] +fn glob_enum() { + covers!(glob_enum); + let (item_map, module_id) = item_map( + " + //- /lib.rs + enum Foo { + Bar, Baz + } + use self::Foo::*; + <|> + ", + ); + check_module_item_map( + &item_map, + module_id, + " + Bar: t v + Baz: t v + Foo: t + ", + ); +} + +#[test] +fn glob_across_crates() { + let (mut db, sr) = MockDatabase::with_files( + " + //- /main.rs + use test_crate::*; + + //- /lib.rs + pub struct Baz; + ", + ); + let main_id = sr.files[RelativePath::new("/main.rs")]; + let lib_id = sr.files[RelativePath::new("/lib.rs")]; + + let mut crate_graph = CrateGraph::default(); + let main_crate = crate_graph.add_crate_root(main_id); + let lib_crate = crate_graph.add_crate_root(lib_id); + crate_graph.add_dep(main_crate, "test_crate".into(), lib_crate).unwrap(); + + db.set_crate_graph(Arc::new(crate_graph)); + + let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap(); + let krate = module.krate(&db).unwrap(); + let item_map = db.item_map(krate); + + check_module_item_map( + &item_map, + module.module_id, + " + Baz: t v + ", + ); +} + #[test] fn module_resolution_works_for_non_standard_filenames() { let (item_map, module_id) = item_map_custom_crate_root( -- cgit v1.2.3 From c1e295682ec23b7f9923cda07fc28d99c987f327 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 10 Feb 2019 15:34:04 +0100 Subject: Implement glob imports from enums --- crates/ra_hir/src/code_model_api.rs | 4 ++-- crates/ra_hir/src/nameres.rs | 39 +++++++++++++++++++++++++++++++++---- 2 files changed, 37 insertions(+), 6 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index 19f103855..94a08aa63 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs @@ -327,7 +327,7 @@ impl Enum { db.enum_data(*self).name.clone() } - pub fn variants(&self, db: &impl HirDatabase) -> Vec { + pub fn variants(&self, db: &impl PersistentHirDatabase) -> Vec { db.enum_data(*self) .variants .iter() @@ -389,7 +389,7 @@ impl EnumVariant { self.parent } - pub fn name(&self, db: &impl HirDatabase) -> Option { + pub fn name(&self, db: &impl PersistentHirDatabase) -> Option { db.enum_data(self.parent).variants[self.id].name.clone() } diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index b7382d9c3..0b6ab1211 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -264,14 +264,45 @@ where import: &ImportData, ) -> ReachedFixedPoint { log::debug!("resolving import: {:?}", import); - if import.is_glob { - return ReachedFixedPoint::Yes; - }; let original_module = Module { krate: self.krate, module_id }; let (def, reached_fixedpoint) = self.result.resolve_path_fp(self.db, original_module, &import.path); - if reached_fixedpoint == ReachedFixedPoint::Yes { + if reached_fixedpoint != ReachedFixedPoint::Yes { + return reached_fixedpoint; + } + + if import.is_glob { + log::debug!("glob import: {:?}", import); + match def.take_types() { + Some(ModuleDef::Module(m)) => { + // TODO + } + Some(ModuleDef::Enum(e)) => { + tested_by!(glob_enum); + let variants = e.variants(self.db); + let resolutions = variants.into_iter() + .filter_map(|variant| { + let res = Resolution { + def: PerNs::both(variant.into(), e.into()), + import: Some(import_id), + }; + let name = variant.name(self.db)?; + Some((name, res)) + }) + .collect::>(); + self.update(module_id, |items| { + items.items.extend(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 { let last_segment = import.path.segments.last().unwrap(); let name = import.alias.clone().unwrap_or_else(|| last_segment.name.clone()); log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); -- cgit v1.2.3 From 2e1d739a8063307facf9ff098c26b02005092e05 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 10 Feb 2019 15:41:44 +0100 Subject: Import glob imports from other crates This is the easy part since we don't have to consider the fixpoint algorithm. --- crates/ra_hir/src/marks.rs | 1 + crates/ra_hir/src/nameres.rs | 17 +++++++++++++++-- crates/ra_hir/src/nameres/tests.rs | 1 + 3 files changed, 17 insertions(+), 2 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs index 5732eeccf..50d4e824c 100644 --- a/crates/ra_hir/src/marks.rs +++ b/crates/ra_hir/src/marks.rs @@ -5,4 +5,5 @@ test_utils::marks!( type_var_cycles_resolve_as_possible type_var_resolves_to_int_var glob_enum + glob_across_crates ); diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 0b6ab1211..2442e754e 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -276,12 +276,25 @@ where log::debug!("glob import: {:?}", import); match def.take_types() { Some(ModuleDef::Module(m)) => { - // TODO + if m.krate != self.krate { + tested_by!(glob_across_crates); + // glob import from other crate => we can just import everything once + let item_map = self.db.item_map(m.krate); + let scope = &item_map[m.module_id]; + self.update(module_id, |items| { + // TODO: handle shadowing and visibility + items.items.extend( + scope.items.iter().map(|(name, res)| (name.clone(), res.clone())), + ); + }); + } } Some(ModuleDef::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() + let resolutions = variants + .into_iter() .filter_map(|variant| { let res = Resolution { def: PerNs::both(variant.into(), e.into()), diff --git a/crates/ra_hir/src/nameres/tests.rs b/crates/ra_hir/src/nameres/tests.rs index 91566fc98..9c0e4ef29 100644 --- a/crates/ra_hir/src/nameres/tests.rs +++ b/crates/ra_hir/src/nameres/tests.rs @@ -251,6 +251,7 @@ fn glob_enum() { #[test] fn glob_across_crates() { + covers!(glob_across_crates); let (mut db, sr) = MockDatabase::with_files( " //- /main.rs -- cgit v1.2.3 From 2f24e740db3365afac56aad3e8a533340369ef7d Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 10 Feb 2019 16:19:50 +0100 Subject: Implement glob imports within the same crate Fixes #231. --- crates/ra_hir/src/nameres.rs | 93 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 16 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 2442e754e..94f7db024 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -61,7 +61,7 @@ impl ModuleScope { /// `Resolution` is basically `DefId` atm, but it should account for stuff like /// multiple namespaces, ambiguity and errors. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct Resolution { /// None for unresolved pub def: PerNs, @@ -154,6 +154,8 @@ struct Resolver<'a, DB> { krate: Crate, module_tree: Arc, processed_imports: FxHashSet<(ModuleId, ImportId)>, + /// If module `a` has `use b::*`, then this contains the mapping b -> a (and the import) + glob_imports: FxHashMap>, result: ItemMap, } @@ -173,6 +175,7 @@ where krate, module_tree, processed_imports: FxHashSet::default(), + glob_imports: FxHashMap::default(), result: ItemMap::default(), } } @@ -281,12 +284,28 @@ where // glob import from other crate => we can just import everything once let item_map = self.db.item_map(m.krate); let scope = &item_map[m.module_id]; - self.update(module_id, |items| { - // TODO: handle shadowing and visibility - items.items.extend( - scope.items.iter().map(|(name, res)| (name.clone(), res.clone())), - ); - }); + 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.result[m.module_id]; + 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(ModuleDef::Enum(e)) => { @@ -304,9 +323,7 @@ where Some((name, res)) }) .collect::>(); - self.update(module_id, |items| { - items.items.extend(resolutions); - }); + self.update(module_id, Some(import_id), &resolutions); } Some(d) => { log::debug!("glob import {:?} from non-module/enum {:?}", import, d); @@ -328,17 +345,61 @@ where } } } - self.update(module_id, |items| { - let res = Resolution { def, import: Some(import_id) }; - items.items.insert(name, res); - }); + let resolution = Resolution { def, import: Some(import_id) }; + self.update(module_id, None, &[(name, resolution)]); } reached_fixedpoint } - fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) { + fn update( + &mut self, + module_id: ModuleId, + import: Option, + resolutions: &[(Name, Resolution)], + ) { + self.update_recursive(module_id, import, resolutions, 0) + } + + fn update_recursive( + &mut self, + module_id: ModuleId, + 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 = self.result.per_module.get_mut(module_id).unwrap(); - f(module_items) + 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 !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); + } } } -- cgit v1.2.3