From 0e4b710af83844f4a7c471c5335c99aaaa25a90c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 28 Nov 2018 03:42:26 +0300 Subject: introduce hir crate --- crates/ra_hir/src/module/imp.rs | 194 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 crates/ra_hir/src/module/imp.rs (limited to 'crates/ra_hir/src/module/imp.rs') diff --git a/crates/ra_hir/src/module/imp.rs b/crates/ra_hir/src/module/imp.rs new file mode 100644 index 000000000..d55fa3e6b --- /dev/null +++ b/crates/ra_hir/src/module/imp.rs @@ -0,0 +1,194 @@ +use std::sync::Arc; + +use ra_syntax::{ + ast::{self, NameOwner}, + SmolStr, +}; +use relative_path::RelativePathBuf; +use rustc_hash::{FxHashMap, FxHashSet}; +use ra_db::{SourceRoot, SourceRootId, FileResolverImp, Cancelable, FileId,}; + +use crate::{ + HirDatabase, +}; + +use super::{ + LinkData, LinkId, ModuleData, ModuleId, ModuleSource, + ModuleTree, Problem, +}; + +#[derive(Clone, Hash, PartialEq, Eq, Debug)] +pub(crate) enum Submodule { + Declaration(SmolStr), + Definition(SmolStr, ModuleSource), +} + +impl Submodule { + fn name(&self) -> &SmolStr { + match self { + Submodule::Declaration(name) => name, + Submodule::Definition(name, _) => name, + } + } +} + +pub(crate) fn modules<'a>( + root: impl ast::ModuleItemOwner<'a>, +) -> impl Iterator)> { + root.items() + .filter_map(|item| match item { + ast::ModuleItem::Module(m) => Some(m), + _ => None, + }) + .filter_map(|module| { + let name = module.name()?.text(); + Some((name, module)) + }) +} + +pub(crate) fn module_tree( + db: &impl HirDatabase, + source_root: SourceRootId, +) -> Cancelable> { + db.check_canceled()?; + let res = create_module_tree(db, source_root)?; + Ok(Arc::new(res)) +} + +fn create_module_tree<'a>( + db: &impl HirDatabase, + source_root: SourceRootId, +) -> Cancelable { + let mut tree = ModuleTree::default(); + + let mut roots = FxHashMap::default(); + let mut visited = FxHashSet::default(); + + let source_root = db.source_root(source_root); + for &file_id in source_root.files.iter() { + let source = ModuleSource::SourceFile(file_id); + if visited.contains(&source) { + continue; // TODO: use explicit crate_roots here + } + assert!(!roots.contains_key(&file_id)); + let module_id = build_subtree( + db, + &source_root, + &mut tree, + &mut visited, + &mut roots, + None, + source, + )?; + roots.insert(file_id, module_id); + } + Ok(tree) +} + +fn build_subtree( + db: &impl HirDatabase, + source_root: &SourceRoot, + tree: &mut ModuleTree, + visited: &mut FxHashSet, + roots: &mut FxHashMap, + parent: Option, + source: ModuleSource, +) -> Cancelable { + visited.insert(source); + let id = tree.push_mod(ModuleData { + source, + parent, + children: Vec::new(), + }); + for sub in db.submodules(source)?.iter() { + let link = tree.push_link(LinkData { + name: sub.name().clone(), + owner: id, + points_to: Vec::new(), + problem: None, + }); + + let (points_to, problem) = match sub { + Submodule::Declaration(name) => { + let (points_to, problem) = + resolve_submodule(source, &name, &source_root.file_resolver); + let points_to = points_to + .into_iter() + .map(|file_id| match roots.remove(&file_id) { + Some(module_id) => { + tree.mods[module_id].parent = Some(link); + Ok(module_id) + } + None => build_subtree( + db, + source_root, + tree, + visited, + roots, + Some(link), + ModuleSource::SourceFile(file_id), + ), + }) + .collect::>>()?; + (points_to, problem) + } + Submodule::Definition(_name, submodule_source) => { + let points_to = build_subtree( + db, + source_root, + tree, + visited, + roots, + Some(link), + *submodule_source, + )?; + (vec![points_to], None) + } + }; + + tree.links[link].points_to = points_to; + tree.links[link].problem = problem; + } + Ok(id) +} + +fn resolve_submodule( + source: ModuleSource, + name: &SmolStr, + file_resolver: &FileResolverImp, +) -> (Vec, Option) { + let file_id = match source { + ModuleSource::SourceFile(it) => it, + ModuleSource::Module(..) => { + // TODO + return (Vec::new(), None); + } + }; + let mod_name = file_resolver.file_stem(file_id); + let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main"; + + let file_mod = RelativePathBuf::from(format!("../{}.rs", name)); + let dir_mod = RelativePathBuf::from(format!("../{}/mod.rs", name)); + let points_to: Vec; + let problem: Option; + if is_dir_owner { + points_to = [&file_mod, &dir_mod] + .iter() + .filter_map(|path| file_resolver.resolve(file_id, path)) + .collect(); + problem = if points_to.is_empty() { + Some(Problem::UnresolvedModule { + candidate: file_mod, + }) + } else { + None + } + } else { + points_to = Vec::new(); + problem = Some(Problem::NotDirOwner { + move_to: RelativePathBuf::from(format!("../{}/mod.rs", mod_name)), + candidate: file_mod, + }); + } + (points_to, problem) +} -- cgit v1.2.3