From 93c0b7d794e55e255b102478e2c482c3037d0acb Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 8 Dec 2018 19:28:01 +0300 Subject: resolve 2018 style modules --- crates/ra_hir/src/module/imp.rs | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/module/imp.rs b/crates/ra_hir/src/module/imp.rs index 0eec38797..9f144e139 100644 --- a/crates/ra_hir/src/module/imp.rs +++ b/crates/ra_hir/src/module/imp.rs @@ -164,26 +164,26 @@ fn resolve_submodule( 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 - } + let file_dir_mod = RelativePathBuf::from(format!("../{}/{}.rs", mod_name, name)); + let tmp1; + let tmp2; + let candidates = if is_dir_owner { + tmp1 = [&file_mod, &dir_mod]; + tmp1.iter() } else { - points_to = Vec::new(); - problem = Some(Problem::NotDirOwner { - move_to: RelativePathBuf::from(format!("../{}/mod.rs", mod_name)), - candidate: file_mod, - }); - } + tmp2 = [&file_dir_mod]; + tmp2.iter() + }; + + let points_to = candidates + .filter_map(|path| file_resolver.resolve(file_id, path)) + .collect::>(); + let problem = if points_to.is_empty() { + Some(Problem::UnresolvedModule { + candidate: if is_dir_owner { file_mod } else { file_dir_mod }, + }) + } else { + None + }; (points_to, problem) } -- cgit v1.2.3 From 4cbc902fcc9de79893779582dac01351d1137c7f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 8 Dec 2018 19:30:35 +0300 Subject: grand module rename --- crates/ra_hir/src/function.rs | 151 ++++++++++++++++++ crates/ra_hir/src/function/mod.rs | 151 ------------------ crates/ra_hir/src/module.rs | 320 ++++++++++++++++++++++++++++++++++++++ crates/ra_hir/src/module/mod.rs | 320 -------------------------------------- 4 files changed, 471 insertions(+), 471 deletions(-) create mode 100644 crates/ra_hir/src/function.rs delete mode 100644 crates/ra_hir/src/function/mod.rs create mode 100644 crates/ra_hir/src/module.rs delete mode 100644 crates/ra_hir/src/module/mod.rs (limited to 'crates/ra_hir/src') diff --git a/crates/ra_hir/src/function.rs b/crates/ra_hir/src/function.rs new file mode 100644 index 000000000..5187dc051 --- /dev/null +++ b/crates/ra_hir/src/function.rs @@ -0,0 +1,151 @@ +mod scope; + +use std::{ + cmp::{max, min}, + sync::Arc, +}; + +use ra_syntax::{ + TextRange, TextUnit, + ast::{self, AstNode, DocCommentsOwner, NameOwner}, +}; + +use crate::{ DefId, HirDatabase }; + +pub use self::scope::FnScopes; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct FnId(pub(crate) DefId); + +pub struct Function { + fn_id: FnId, +} + +impl Function { + pub(crate) fn new(def_id: DefId) -> Function { + let fn_id = FnId(def_id); + Function { fn_id } + } + + pub fn scope(&self, db: &impl HirDatabase) -> Arc { + db.fn_scopes(self.fn_id) + } + + pub fn signature_info(&self, db: &impl HirDatabase) -> Option { + let syntax = db.fn_syntax(self.fn_id); + FnSignatureInfo::new(syntax.borrowed()) + } +} + +#[derive(Debug, Clone)] +pub struct FnSignatureInfo { + pub name: String, + pub label: String, + pub ret_type: Option, + pub params: Vec, + pub doc: Option, +} + +impl FnSignatureInfo { + fn new(node: ast::FnDef) -> Option { + let name = node.name()?.text().to_string(); + + let mut doc = None; + + // Strip the body out for the label. + let mut label: String = if let Some(body) = node.body() { + let body_range = body.syntax().range(); + let label: String = node + .syntax() + .children() + .filter(|child| !child.range().is_subrange(&body_range)) + .map(|node| node.text().to_string()) + .collect(); + label + } else { + node.syntax().text().to_string() + }; + + if let Some((comment_range, docs)) = FnSignatureInfo::extract_doc_comments(node) { + let comment_range = comment_range + .checked_sub(node.syntax().range().start()) + .unwrap(); + let start = comment_range.start().to_usize(); + let end = comment_range.end().to_usize(); + + // Remove the comment from the label + label.replace_range(start..end, ""); + + // Massage markdown + let mut processed_lines = Vec::new(); + let mut in_code_block = false; + for line in docs.lines() { + if line.starts_with("```") { + in_code_block = !in_code_block; + } + + let line = if in_code_block && line.starts_with("```") && !line.contains("rust") { + "```rust".into() + } else { + line.to_string() + }; + + processed_lines.push(line); + } + + if !processed_lines.is_empty() { + doc = Some(processed_lines.join("\n")); + } + } + + let params = FnSignatureInfo::param_list(node); + let ret_type = node.ret_type().map(|r| r.syntax().text().to_string()); + + Some(FnSignatureInfo { + name, + ret_type, + params, + label: label.trim().to_owned(), + doc, + }) + } + + fn extract_doc_comments(node: ast::FnDef) -> Option<(TextRange, String)> { + if node.doc_comments().count() == 0 { + return None; + } + + let comment_text = node.doc_comment_text(); + + let (begin, end) = node + .doc_comments() + .map(|comment| comment.syntax().range()) + .map(|range| (range.start().to_usize(), range.end().to_usize())) + .fold((std::usize::MAX, std::usize::MIN), |acc, range| { + (min(acc.0, range.0), max(acc.1, range.1)) + }); + + let range = TextRange::from_to(TextUnit::from_usize(begin), TextUnit::from_usize(end)); + + Some((range, comment_text)) + } + + fn param_list(node: ast::FnDef) -> Vec { + let mut res = vec![]; + if let Some(param_list) = node.param_list() { + if let Some(self_param) = param_list.self_param() { + res.push(self_param.syntax().text().to_string()) + } + + // Maybe use param.pat here? See if we can just extract the name? + //res.extend(param_list.params().map(|p| p.syntax().text().to_string())); + res.extend( + param_list + .params() + .filter_map(|p| p.pat()) + .map(|pat| pat.syntax().text().to_string()), + ); + } + res + } +} diff --git a/crates/ra_hir/src/function/mod.rs b/crates/ra_hir/src/function/mod.rs deleted file mode 100644 index 5187dc051..000000000 --- a/crates/ra_hir/src/function/mod.rs +++ /dev/null @@ -1,151 +0,0 @@ -mod scope; - -use std::{ - cmp::{max, min}, - sync::Arc, -}; - -use ra_syntax::{ - TextRange, TextUnit, - ast::{self, AstNode, DocCommentsOwner, NameOwner}, -}; - -use crate::{ DefId, HirDatabase }; - -pub use self::scope::FnScopes; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct FnId(pub(crate) DefId); - -pub struct Function { - fn_id: FnId, -} - -impl Function { - pub(crate) fn new(def_id: DefId) -> Function { - let fn_id = FnId(def_id); - Function { fn_id } - } - - pub fn scope(&self, db: &impl HirDatabase) -> Arc { - db.fn_scopes(self.fn_id) - } - - pub fn signature_info(&self, db: &impl HirDatabase) -> Option { - let syntax = db.fn_syntax(self.fn_id); - FnSignatureInfo::new(syntax.borrowed()) - } -} - -#[derive(Debug, Clone)] -pub struct FnSignatureInfo { - pub name: String, - pub label: String, - pub ret_type: Option, - pub params: Vec, - pub doc: Option, -} - -impl FnSignatureInfo { - fn new(node: ast::FnDef) -> Option { - let name = node.name()?.text().to_string(); - - let mut doc = None; - - // Strip the body out for the label. - let mut label: String = if let Some(body) = node.body() { - let body_range = body.syntax().range(); - let label: String = node - .syntax() - .children() - .filter(|child| !child.range().is_subrange(&body_range)) - .map(|node| node.text().to_string()) - .collect(); - label - } else { - node.syntax().text().to_string() - }; - - if let Some((comment_range, docs)) = FnSignatureInfo::extract_doc_comments(node) { - let comment_range = comment_range - .checked_sub(node.syntax().range().start()) - .unwrap(); - let start = comment_range.start().to_usize(); - let end = comment_range.end().to_usize(); - - // Remove the comment from the label - label.replace_range(start..end, ""); - - // Massage markdown - let mut processed_lines = Vec::new(); - let mut in_code_block = false; - for line in docs.lines() { - if line.starts_with("```") { - in_code_block = !in_code_block; - } - - let line = if in_code_block && line.starts_with("```") && !line.contains("rust") { - "```rust".into() - } else { - line.to_string() - }; - - processed_lines.push(line); - } - - if !processed_lines.is_empty() { - doc = Some(processed_lines.join("\n")); - } - } - - let params = FnSignatureInfo::param_list(node); - let ret_type = node.ret_type().map(|r| r.syntax().text().to_string()); - - Some(FnSignatureInfo { - name, - ret_type, - params, - label: label.trim().to_owned(), - doc, - }) - } - - fn extract_doc_comments(node: ast::FnDef) -> Option<(TextRange, String)> { - if node.doc_comments().count() == 0 { - return None; - } - - let comment_text = node.doc_comment_text(); - - let (begin, end) = node - .doc_comments() - .map(|comment| comment.syntax().range()) - .map(|range| (range.start().to_usize(), range.end().to_usize())) - .fold((std::usize::MAX, std::usize::MIN), |acc, range| { - (min(acc.0, range.0), max(acc.1, range.1)) - }); - - let range = TextRange::from_to(TextUnit::from_usize(begin), TextUnit::from_usize(end)); - - Some((range, comment_text)) - } - - fn param_list(node: ast::FnDef) -> Vec { - let mut res = vec![]; - if let Some(param_list) = node.param_list() { - if let Some(self_param) = param_list.self_param() { - res.push(self_param.syntax().text().to_string()) - } - - // Maybe use param.pat here? See if we can just extract the name? - //res.extend(param_list.params().map(|p| p.syntax().text().to_string())); - res.extend( - param_list - .params() - .filter_map(|p| p.pat()) - .map(|pat| pat.syntax().text().to_string()), - ); - } - res - } -} diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs new file mode 100644 index 000000000..580c737c3 --- /dev/null +++ b/crates/ra_hir/src/module.rs @@ -0,0 +1,320 @@ +pub(super) mod imp; +pub(super) mod nameres; + +use std::sync::Arc; + +use ra_syntax::{ + algo::generate, + ast::{self, AstNode, NameOwner}, + SmolStr, SyntaxNode, +}; +use ra_db::{SourceRootId, FileId, Cancelable}; +use relative_path::RelativePathBuf; + +use crate::{ + DefKind, DefLoc, DefId, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, + arena::{Arena, Id}, +}; + +pub use self::nameres::ModuleScope; + +/// `Module` is API entry point to get all the information +/// about a particular module. +#[derive(Debug, Clone)] +pub struct Module { + tree: Arc, + pub(crate) source_root_id: SourceRootId, + pub(crate) module_id: ModuleId, +} + +impl Module { + pub(super) fn new( + db: &impl HirDatabase, + source_root_id: SourceRootId, + module_id: ModuleId, + ) -> Cancelable { + let module_tree = db.module_tree(source_root_id)?; + let res = Module { + tree: module_tree, + source_root_id, + module_id, + }; + Ok(res) + } + + /// Returns `mod foo;` or `mod foo {}` node whihc declared this module. + /// Returns `None` for the root module + pub fn parent_link_source(&self, db: &impl HirDatabase) -> Option<(FileId, ast::ModuleNode)> { + let link = self.module_id.parent_link(&self.tree)?; + let file_id = link.owner(&self.tree).source(&self.tree).file_id(); + let src = link.bind_source(&self.tree, db); + Some((file_id, src)) + } + + pub fn source(&self) -> ModuleSource { + self.module_id.source(&self.tree) + } + + /// Parent module. Returns `None` if this is a root module. + pub fn parent(&self) -> Option { + let parent_id = self.module_id.parent(&self.tree)?; + Some(Module { + module_id: parent_id, + ..self.clone() + }) + } + + /// The root of the tree this module is part of + pub fn crate_root(&self) -> Module { + let root_id = self.module_id.crate_root(&self.tree); + Module { + module_id: root_id, + ..self.clone() + } + } + + /// `name` is `None` for the crate's root module + pub fn name(&self) -> Option { + let link = self.module_id.parent_link(&self.tree)?; + Some(link.name(&self.tree)) + } + + pub fn def_id(&self, db: &impl HirDatabase) -> DefId { + let def_loc = DefLoc { + kind: DefKind::Module, + source_root_id: self.source_root_id, + module_id: self.module_id, + source_item_id: self.module_id.source(&self.tree).0, + }; + def_loc.id(db) + } + + /// Finds a child module with the specified name. + pub fn child(&self, name: &str) -> Option { + let child_id = self.module_id.child(&self.tree, name)?; + Some(Module { + module_id: child_id, + ..self.clone() + }) + } + + /// Returns a `ModuleScope`: a set of items, visible in this module. + pub fn scope(&self, db: &impl HirDatabase) -> Cancelable { + let item_map = db.item_map(self.source_root_id)?; + let res = item_map.per_module[&self.module_id].clone(); + Ok(res) + } + + pub fn resolve_path(&self, db: &impl HirDatabase, path: Path) -> Cancelable> { + let mut curr = match path.kind { + PathKind::Crate => self.crate_root(), + PathKind::Self_ | PathKind::Plain => self.clone(), + PathKind::Super => ctry!(self.parent()), + } + .def_id(db); + + let segments = path.segments; + for name in segments.iter() { + let module = match curr.loc(db) { + DefLoc { + kind: DefKind::Module, + source_root_id, + module_id, + .. + } => Module::new(db, source_root_id, module_id)?, + _ => return Ok(None), + }; + let scope = module.scope(db)?; + curr = ctry!(ctry!(scope.get(&name)).def_id); + } + Ok(Some(curr)) + } + + pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { + self.module_id.problems(&self.tree, db) + } +} + +/// Phisically, rust source is organized as a set of files, but logically it is +/// organized as a tree of modules. Usually, a single file corresponds to a +/// single module, but it is not nessary the case. +/// +/// Module encapsulate the logic of transitioning from the fuzzy world of files +/// (which can have multiple parents) to the precise world of modules (which +/// always have one parent). +#[derive(Default, Debug, PartialEq, Eq)] +pub struct ModuleTree { + mods: Arena, + links: Arena, +} + +impl ModuleTree { + pub(crate) fn modules<'a>(&'a self) -> impl Iterator + 'a { + self.mods.iter().map(|(id, _)| id) + } + + pub(crate) fn modules_with_sources<'a>( + &'a self, + ) -> impl Iterator + 'a { + self.mods.iter().map(|(id, m)| (id, m.source)) + } +} + +/// `ModuleSource` is the syntax tree element that produced this module: +/// either a file, or an inlinde module. +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct ModuleSource(SourceItemId); + +/// An owned syntax node for a module. Unlike `ModuleSource`, +/// this holds onto the AST for the whole file. +pub(crate) enum ModuleSourceNode { + SourceFile(ast::SourceFileNode), + Module(ast::ModuleNode), +} + +pub type ModuleId = Id; +type LinkId = Id; + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub enum Problem { + UnresolvedModule { + candidate: RelativePathBuf, + }, + NotDirOwner { + move_to: RelativePathBuf, + candidate: RelativePathBuf, + }, +} + +impl ModuleId { + pub(crate) fn source(self, tree: &ModuleTree) -> ModuleSource { + tree.mods[self].source + } + fn parent_link(self, tree: &ModuleTree) -> Option { + tree.mods[self].parent + } + fn parent(self, tree: &ModuleTree) -> Option { + let link = self.parent_link(tree)?; + Some(tree.links[link].owner) + } + fn crate_root(self, tree: &ModuleTree) -> ModuleId { + generate(Some(self), move |it| it.parent(tree)) + .last() + .unwrap() + } + fn child(self, tree: &ModuleTree, name: &str) -> Option { + let link = tree.mods[self] + .children + .iter() + .map(|&it| &tree.links[it]) + .find(|it| it.name == name)?; + Some(*link.points_to.first()?) + } + fn children<'a>(self, tree: &'a ModuleTree) -> impl Iterator + 'a { + tree.mods[self].children.iter().filter_map(move |&it| { + let link = &tree.links[it]; + let module = *link.points_to.first()?; + Some((link.name.clone(), module)) + }) + } + fn problems(self, tree: &ModuleTree, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { + tree.mods[self] + .children + .iter() + .filter_map(|&it| { + let p = tree.links[it].problem.clone()?; + let s = it.bind_source(tree, db); + let s = s.borrowed().name().unwrap().syntax().owned(); + Some((s, p)) + }) + .collect() + } +} + +impl LinkId { + fn owner(self, tree: &ModuleTree) -> ModuleId { + tree.links[self].owner + } + fn name(self, tree: &ModuleTree) -> SmolStr { + tree.links[self].name.clone() + } + fn bind_source<'a>(self, tree: &ModuleTree, db: &impl HirDatabase) -> ast::ModuleNode { + let owner = self.owner(tree); + match owner.source(tree).resolve(db) { + ModuleSourceNode::SourceFile(root) => { + let ast = imp::modules(root.borrowed()) + .find(|(name, _)| name == &tree.links[self].name) + .unwrap() + .1; + ast.owned() + } + ModuleSourceNode::Module(it) => it, + } + } +} + +#[derive(Debug, PartialEq, Eq, Hash)] +pub struct ModuleData { + source: ModuleSource, + parent: Option, + children: Vec, +} + +impl ModuleSource { + // precondition: item_id **must** point to module + fn new(file_id: FileId, item_id: SourceFileItemId) -> ModuleSource { + let source_item_id = SourceItemId { file_id, item_id }; + ModuleSource(source_item_id) + } + + pub(crate) fn new_file(db: &impl HirDatabase, file_id: FileId) -> ModuleSource { + let file_items = db.file_items(file_id); + let item_id = file_items.id_of_source_file(); + ModuleSource::new(file_id, item_id) + } + + pub(crate) fn new_inline( + db: &impl HirDatabase, + file_id: FileId, + m: ast::Module, + ) -> ModuleSource { + assert!(!m.has_semi()); + let file_items = db.file_items(file_id); + let item_id = file_items.id_of(m.syntax()); + ModuleSource::new(file_id, item_id) + } + + pub fn file_id(self) -> FileId { + self.0.file_id + } + + pub(crate) fn resolve(self, db: &impl HirDatabase) -> ModuleSourceNode { + let syntax_node = db.file_item(self.0); + let syntax_node = syntax_node.borrowed(); + if let Some(file) = ast::SourceFile::cast(syntax_node) { + return ModuleSourceNode::SourceFile(file.owned()); + } + let module = ast::Module::cast(syntax_node).unwrap(); + ModuleSourceNode::Module(module.owned()) + } +} + +#[derive(Hash, Debug, PartialEq, Eq)] +struct LinkData { + owner: ModuleId, + name: SmolStr, + points_to: Vec, + problem: Option, +} + +impl ModuleTree { + fn push_mod(&mut self, data: ModuleData) -> ModuleId { + self.mods.alloc(data) + } + fn push_link(&mut self, data: LinkData) -> LinkId { + let owner = data.owner; + let id = self.links.alloc(data); + self.mods[owner].children.push(id); + id + } +} diff --git a/crates/ra_hir/src/module/mod.rs b/crates/ra_hir/src/module/mod.rs deleted file mode 100644 index 580c737c3..000000000 --- a/crates/ra_hir/src/module/mod.rs +++ /dev/null @@ -1,320 +0,0 @@ -pub(super) mod imp; -pub(super) mod nameres; - -use std::sync::Arc; - -use ra_syntax::{ - algo::generate, - ast::{self, AstNode, NameOwner}, - SmolStr, SyntaxNode, -}; -use ra_db::{SourceRootId, FileId, Cancelable}; -use relative_path::RelativePathBuf; - -use crate::{ - DefKind, DefLoc, DefId, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, - arena::{Arena, Id}, -}; - -pub use self::nameres::ModuleScope; - -/// `Module` is API entry point to get all the information -/// about a particular module. -#[derive(Debug, Clone)] -pub struct Module { - tree: Arc, - pub(crate) source_root_id: SourceRootId, - pub(crate) module_id: ModuleId, -} - -impl Module { - pub(super) fn new( - db: &impl HirDatabase, - source_root_id: SourceRootId, - module_id: ModuleId, - ) -> Cancelable { - let module_tree = db.module_tree(source_root_id)?; - let res = Module { - tree: module_tree, - source_root_id, - module_id, - }; - Ok(res) - } - - /// Returns `mod foo;` or `mod foo {}` node whihc declared this module. - /// Returns `None` for the root module - pub fn parent_link_source(&self, db: &impl HirDatabase) -> Option<(FileId, ast::ModuleNode)> { - let link = self.module_id.parent_link(&self.tree)?; - let file_id = link.owner(&self.tree).source(&self.tree).file_id(); - let src = link.bind_source(&self.tree, db); - Some((file_id, src)) - } - - pub fn source(&self) -> ModuleSource { - self.module_id.source(&self.tree) - } - - /// Parent module. Returns `None` if this is a root module. - pub fn parent(&self) -> Option { - let parent_id = self.module_id.parent(&self.tree)?; - Some(Module { - module_id: parent_id, - ..self.clone() - }) - } - - /// The root of the tree this module is part of - pub fn crate_root(&self) -> Module { - let root_id = self.module_id.crate_root(&self.tree); - Module { - module_id: root_id, - ..self.clone() - } - } - - /// `name` is `None` for the crate's root module - pub fn name(&self) -> Option { - let link = self.module_id.parent_link(&self.tree)?; - Some(link.name(&self.tree)) - } - - pub fn def_id(&self, db: &impl HirDatabase) -> DefId { - let def_loc = DefLoc { - kind: DefKind::Module, - source_root_id: self.source_root_id, - module_id: self.module_id, - source_item_id: self.module_id.source(&self.tree).0, - }; - def_loc.id(db) - } - - /// Finds a child module with the specified name. - pub fn child(&self, name: &str) -> Option { - let child_id = self.module_id.child(&self.tree, name)?; - Some(Module { - module_id: child_id, - ..self.clone() - }) - } - - /// Returns a `ModuleScope`: a set of items, visible in this module. - pub fn scope(&self, db: &impl HirDatabase) -> Cancelable { - let item_map = db.item_map(self.source_root_id)?; - let res = item_map.per_module[&self.module_id].clone(); - Ok(res) - } - - pub fn resolve_path(&self, db: &impl HirDatabase, path: Path) -> Cancelable> { - let mut curr = match path.kind { - PathKind::Crate => self.crate_root(), - PathKind::Self_ | PathKind::Plain => self.clone(), - PathKind::Super => ctry!(self.parent()), - } - .def_id(db); - - let segments = path.segments; - for name in segments.iter() { - let module = match curr.loc(db) { - DefLoc { - kind: DefKind::Module, - source_root_id, - module_id, - .. - } => Module::new(db, source_root_id, module_id)?, - _ => return Ok(None), - }; - let scope = module.scope(db)?; - curr = ctry!(ctry!(scope.get(&name)).def_id); - } - Ok(Some(curr)) - } - - pub fn problems(&self, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { - self.module_id.problems(&self.tree, db) - } -} - -/// Phisically, rust source is organized as a set of files, but logically it is -/// organized as a tree of modules. Usually, a single file corresponds to a -/// single module, but it is not nessary the case. -/// -/// Module encapsulate the logic of transitioning from the fuzzy world of files -/// (which can have multiple parents) to the precise world of modules (which -/// always have one parent). -#[derive(Default, Debug, PartialEq, Eq)] -pub struct ModuleTree { - mods: Arena, - links: Arena, -} - -impl ModuleTree { - pub(crate) fn modules<'a>(&'a self) -> impl Iterator + 'a { - self.mods.iter().map(|(id, _)| id) - } - - pub(crate) fn modules_with_sources<'a>( - &'a self, - ) -> impl Iterator + 'a { - self.mods.iter().map(|(id, m)| (id, m.source)) - } -} - -/// `ModuleSource` is the syntax tree element that produced this module: -/// either a file, or an inlinde module. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct ModuleSource(SourceItemId); - -/// An owned syntax node for a module. Unlike `ModuleSource`, -/// this holds onto the AST for the whole file. -pub(crate) enum ModuleSourceNode { - SourceFile(ast::SourceFileNode), - Module(ast::ModuleNode), -} - -pub type ModuleId = Id; -type LinkId = Id; - -#[derive(Clone, Debug, Hash, PartialEq, Eq)] -pub enum Problem { - UnresolvedModule { - candidate: RelativePathBuf, - }, - NotDirOwner { - move_to: RelativePathBuf, - candidate: RelativePathBuf, - }, -} - -impl ModuleId { - pub(crate) fn source(self, tree: &ModuleTree) -> ModuleSource { - tree.mods[self].source - } - fn parent_link(self, tree: &ModuleTree) -> Option { - tree.mods[self].parent - } - fn parent(self, tree: &ModuleTree) -> Option { - let link = self.parent_link(tree)?; - Some(tree.links[link].owner) - } - fn crate_root(self, tree: &ModuleTree) -> ModuleId { - generate(Some(self), move |it| it.parent(tree)) - .last() - .unwrap() - } - fn child(self, tree: &ModuleTree, name: &str) -> Option { - let link = tree.mods[self] - .children - .iter() - .map(|&it| &tree.links[it]) - .find(|it| it.name == name)?; - Some(*link.points_to.first()?) - } - fn children<'a>(self, tree: &'a ModuleTree) -> impl Iterator + 'a { - tree.mods[self].children.iter().filter_map(move |&it| { - let link = &tree.links[it]; - let module = *link.points_to.first()?; - Some((link.name.clone(), module)) - }) - } - fn problems(self, tree: &ModuleTree, db: &impl HirDatabase) -> Vec<(SyntaxNode, Problem)> { - tree.mods[self] - .children - .iter() - .filter_map(|&it| { - let p = tree.links[it].problem.clone()?; - let s = it.bind_source(tree, db); - let s = s.borrowed().name().unwrap().syntax().owned(); - Some((s, p)) - }) - .collect() - } -} - -impl LinkId { - fn owner(self, tree: &ModuleTree) -> ModuleId { - tree.links[self].owner - } - fn name(self, tree: &ModuleTree) -> SmolStr { - tree.links[self].name.clone() - } - fn bind_source<'a>(self, tree: &ModuleTree, db: &impl HirDatabase) -> ast::ModuleNode { - let owner = self.owner(tree); - match owner.source(tree).resolve(db) { - ModuleSourceNode::SourceFile(root) => { - let ast = imp::modules(root.borrowed()) - .find(|(name, _)| name == &tree.links[self].name) - .unwrap() - .1; - ast.owned() - } - ModuleSourceNode::Module(it) => it, - } - } -} - -#[derive(Debug, PartialEq, Eq, Hash)] -pub struct ModuleData { - source: ModuleSource, - parent: Option, - children: Vec, -} - -impl ModuleSource { - // precondition: item_id **must** point to module - fn new(file_id: FileId, item_id: SourceFileItemId) -> ModuleSource { - let source_item_id = SourceItemId { file_id, item_id }; - ModuleSource(source_item_id) - } - - pub(crate) fn new_file(db: &impl HirDatabase, file_id: FileId) -> ModuleSource { - let file_items = db.file_items(file_id); - let item_id = file_items.id_of_source_file(); - ModuleSource::new(file_id, item_id) - } - - pub(crate) fn new_inline( - db: &impl HirDatabase, - file_id: FileId, - m: ast::Module, - ) -> ModuleSource { - assert!(!m.has_semi()); - let file_items = db.file_items(file_id); - let item_id = file_items.id_of(m.syntax()); - ModuleSource::new(file_id, item_id) - } - - pub fn file_id(self) -> FileId { - self.0.file_id - } - - pub(crate) fn resolve(self, db: &impl HirDatabase) -> ModuleSourceNode { - let syntax_node = db.file_item(self.0); - let syntax_node = syntax_node.borrowed(); - if let Some(file) = ast::SourceFile::cast(syntax_node) { - return ModuleSourceNode::SourceFile(file.owned()); - } - let module = ast::Module::cast(syntax_node).unwrap(); - ModuleSourceNode::Module(module.owned()) - } -} - -#[derive(Hash, Debug, PartialEq, Eq)] -struct LinkData { - owner: ModuleId, - name: SmolStr, - points_to: Vec, - problem: Option, -} - -impl ModuleTree { - fn push_mod(&mut self, data: ModuleData) -> ModuleId { - self.mods.alloc(data) - } - fn push_link(&mut self, data: LinkData) -> LinkId { - let owner = data.owner; - let id = self.links.alloc(data); - self.mods[owner].children.push(id); - id - } -} -- cgit v1.2.3