flatten module structure

This commit is contained in:
Aleksey Kladov 2019-01-06 17:33:27 +03:00
parent 5a505189a8
commit fd4456d0ec
13 changed files with 431 additions and 436 deletions

View file

@ -1,7 +1,8 @@
use relative_path::RelativePathBuf;
use ra_db::{CrateId, Cancelable, FileId}; use ra_db::{CrateId, Cancelable, FileId};
use ra_syntax::{ast, SyntaxNode}; use ra_syntax::{ast, SyntaxNode};
use crate::{Name, db::HirDatabase, DefId, Path, PerNs, module::{Problem, ModuleScope}}; use crate::{Name, db::HirDatabase, DefId, Path, PerNs, nameres::ModuleScope};
/// hir::Crate describes a single crate. It's the main inteface with which /// hir::Crate describes a single crate. It's the main inteface with which
/// crate's dependencies interact. Mostly, it should be just a proxy for the /// crate's dependencies interact. Mostly, it should be just a proxy for the
@ -39,6 +40,17 @@ pub enum ModuleSource {
Module(ast::ModuleNode), Module(ast::ModuleNode),
} }
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum Problem {
UnresolvedModule {
candidate: RelativePathBuf,
},
NotDirOwner {
move_to: RelativePathBuf,
candidate: RelativePathBuf,
},
}
impl Module { impl Module {
/// Name of this module. /// Name of this module.
pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<Name>> { pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<Name>> {

View file

@ -1,194 +1,2 @@
use ra_db::{CrateId, Cancelable, SourceRootId, FileId}; mod krate; // `crate` is invalid ident :(
use ra_syntax::{ast, SyntaxNode, AstNode}; pub(crate) mod module;
use crate::{
HirFileId, Crate, CrateDependency, AsName, DefId, DefLoc, DefKind, Name, Path, PathKind, PerNs, Def, ModuleId,
module::{ModuleScope, Problem},
db::HirDatabase,
};
use crate::code_model_api::{Module, ModuleSource};
impl Crate {
pub(crate) fn new(crate_id: CrateId) -> Crate {
Crate { crate_id }
}
pub(crate) fn dependencies_impl(&self, db: &impl HirDatabase) -> Vec<CrateDependency> {
let crate_graph = db.crate_graph();
crate_graph
.dependencies(self.crate_id)
.map(|dep| {
let krate = Crate::new(dep.crate_id());
let name = dep.as_name();
CrateDependency { krate, name }
})
.collect()
}
pub(crate) fn root_module_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> {
let crate_graph = db.crate_graph();
let file_id = crate_graph.crate_root(self.crate_id);
let source_root_id = db.file_source_root(file_id);
let file_id = HirFileId::from(file_id);
let module_tree = db.module_tree(source_root_id)?;
// FIXME: teach module tree about crate roots instead of guessing
let (module_id, _) = ctry!(module_tree
.modules_with_sources()
.find(|(_, src)| src.file_id() == file_id));
let def_loc = DefLoc {
kind: DefKind::Module,
source_root_id,
module_id,
source_item_id: module_id.source(&module_tree).0,
};
let def_id = def_loc.id(db);
let module = Module::new(def_id);
Ok(Some(module))
}
}
impl Module {
pub(crate) fn new(def_id: DefId) -> Self {
crate::code_model_api::Module { def_id }
}
pub(crate) fn from_module_id(
db: &impl HirDatabase,
source_root_id: SourceRootId,
module_id: ModuleId,
) -> Cancelable<Self> {
let module_tree = db.module_tree(source_root_id)?;
let def_loc = DefLoc {
kind: DefKind::Module,
source_root_id,
module_id,
source_item_id: module_id.source(&module_tree).0,
};
let def_id = def_loc.id(db);
let module = Module::new(def_id);
Ok(module)
}
pub(crate) fn name_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Name>> {
let loc = self.def_id.loc(db);
let module_tree = db.module_tree(loc.source_root_id)?;
let link = ctry!(loc.module_id.parent_link(&module_tree));
Ok(Some(link.name(&module_tree).clone()))
}
pub fn defenition_source_impl(
&self,
db: &impl HirDatabase,
) -> Cancelable<(FileId, ModuleSource)> {
let loc = self.def_id.loc(db);
let file_id = loc.source_item_id.file_id.as_original_file();
let syntax_node = db.file_item(loc.source_item_id);
let syntax_node = syntax_node.borrowed();
let module_source = if let Some(source_file) = ast::SourceFile::cast(syntax_node) {
ModuleSource::SourceFile(source_file.owned())
} else {
let module = ast::Module::cast(syntax_node).unwrap();
ModuleSource::Module(module.owned())
};
Ok((file_id, module_source))
}
pub fn declaration_source_impl(
&self,
db: &impl HirDatabase,
) -> Cancelable<Option<(FileId, ast::ModuleNode)>> {
let loc = self.def_id.loc(db);
let module_tree = db.module_tree(loc.source_root_id)?;
let link = ctry!(loc.module_id.parent_link(&module_tree));
let file_id = link
.owner(&module_tree)
.source(&module_tree)
.file_id()
.as_original_file();
let src = link.bind_source(&module_tree, db);
Ok(Some((file_id, src)))
}
pub(crate) fn krate_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Crate>> {
let root = self.crate_root(db)?;
let loc = root.def_id.loc(db);
let file_id = loc.source_item_id.file_id.as_original_file();
let crate_graph = db.crate_graph();
let crate_id = ctry!(crate_graph.crate_id_for_crate_root(file_id));
Ok(Some(Crate::new(crate_id)))
}
pub(crate) fn crate_root_impl(&self, db: &impl HirDatabase) -> Cancelable<Module> {
let loc = self.def_id.loc(db);
let module_tree = db.module_tree(loc.source_root_id)?;
let module_id = loc.module_id.crate_root(&module_tree);
Module::from_module_id(db, loc.source_root_id, module_id)
}
/// Finds a child module with the specified name.
pub fn child_impl(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
let loc = self.def_id.loc(db);
let module_tree = db.module_tree(loc.source_root_id)?;
let child_id = ctry!(loc.module_id.child(&module_tree, name));
Module::from_module_id(db, loc.source_root_id, child_id).map(Some)
}
pub fn parent_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> {
let loc = self.def_id.loc(db);
let module_tree = db.module_tree(loc.source_root_id)?;
let parent_id = ctry!(loc.module_id.parent(&module_tree));
Module::from_module_id(db, loc.source_root_id, parent_id).map(Some)
}
/// Returns a `ModuleScope`: a set of items, visible in this module.
pub fn scope_impl(&self, db: &impl HirDatabase) -> Cancelable<ModuleScope> {
let loc = self.def_id.loc(db);
let item_map = db.item_map(loc.source_root_id)?;
let res = item_map.per_module[&loc.module_id].clone();
Ok(res)
}
pub fn resolve_path_impl(
&self,
db: &impl HirDatabase,
path: &Path,
) -> Cancelable<PerNs<DefId>> {
let mut curr_per_ns = PerNs::types(
match path.kind {
PathKind::Crate => self.crate_root(db)?,
PathKind::Self_ | PathKind::Plain => self.clone(),
PathKind::Super => {
if let Some(p) = self.parent(db)? {
p
} else {
return Ok(PerNs::none());
}
}
}
.def_id,
);
let segments = &path.segments;
for name in segments.iter() {
let curr = if let Some(r) = curr_per_ns.as_ref().take_types() {
r
} else {
return Ok(PerNs::none());
};
let module = match curr.resolve(db)? {
Def::Module(it) => it,
// TODO here would be the place to handle enum variants...
_ => return Ok(PerNs::none()),
};
let scope = module.scope(db)?;
curr_per_ns = if let Some(r) = scope.get(&name) {
r.def_id
} else {
return Ok(PerNs::none());
};
}
Ok(curr_per_ns)
}
pub fn problems_impl(&self, db: &impl HirDatabase) -> Cancelable<Vec<(SyntaxNode, Problem)>> {
let loc = self.def_id.loc(db);
let module_tree = db.module_tree(loc.source_root_id)?;
Ok(loc.module_id.problems(&module_tree, db))
}
}

View file

@ -0,0 +1,45 @@
use ra_db::{CrateId, Cancelable};
use crate::{
HirFileId, Crate, CrateDependency, AsName, DefLoc, DefKind, Module,
db::HirDatabase,
};
impl Crate {
pub(crate) fn new(crate_id: CrateId) -> Crate {
Crate { crate_id }
}
pub(crate) fn dependencies_impl(&self, db: &impl HirDatabase) -> Vec<CrateDependency> {
let crate_graph = db.crate_graph();
crate_graph
.dependencies(self.crate_id)
.map(|dep| {
let krate = Crate::new(dep.crate_id());
let name = dep.as_name();
CrateDependency { krate, name }
})
.collect()
}
pub(crate) fn root_module_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> {
let crate_graph = db.crate_graph();
let file_id = crate_graph.crate_root(self.crate_id);
let source_root_id = db.file_source_root(file_id);
let file_id = HirFileId::from(file_id);
let module_tree = db.module_tree(source_root_id)?;
// FIXME: teach module tree about crate roots instead of guessing
let (module_id, _) = ctry!(module_tree
.modules_with_sources()
.find(|(_, src)| src.file_id() == file_id));
let def_loc = DefLoc {
kind: DefKind::Module,
source_root_id,
module_id,
source_item_id: module_id.source(&module_tree).0,
};
let def_id = def_loc.id(db);
let module = Module::new(def_id);
Ok(Some(module))
}
}

View file

@ -0,0 +1,154 @@
use ra_db::{Cancelable, SourceRootId, FileId};
use ra_syntax::{ast, SyntaxNode, AstNode};
use crate::{
Module, ModuleSource, Problem,
Crate, DefId, DefLoc, DefKind, Name, Path, PathKind, PerNs, Def, ModuleId,
nameres::ModuleScope,
db::HirDatabase,
};
impl Module {
pub(crate) fn new(def_id: DefId) -> Self {
crate::code_model_api::Module { def_id }
}
pub(crate) fn from_module_id(
db: &impl HirDatabase,
source_root_id: SourceRootId,
module_id: ModuleId,
) -> Cancelable<Self> {
let module_tree = db.module_tree(source_root_id)?;
let def_loc = DefLoc {
kind: DefKind::Module,
source_root_id,
module_id,
source_item_id: module_id.source(&module_tree).0,
};
let def_id = def_loc.id(db);
let module = Module::new(def_id);
Ok(module)
}
pub(crate) fn name_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Name>> {
let loc = self.def_id.loc(db);
let module_tree = db.module_tree(loc.source_root_id)?;
let link = ctry!(loc.module_id.parent_link(&module_tree));
Ok(Some(link.name(&module_tree).clone()))
}
pub fn defenition_source_impl(
&self,
db: &impl HirDatabase,
) -> Cancelable<(FileId, ModuleSource)> {
let loc = self.def_id.loc(db);
let file_id = loc.source_item_id.file_id.as_original_file();
let syntax_node = db.file_item(loc.source_item_id);
let syntax_node = syntax_node.borrowed();
let module_source = if let Some(source_file) = ast::SourceFile::cast(syntax_node) {
ModuleSource::SourceFile(source_file.owned())
} else {
let module = ast::Module::cast(syntax_node).unwrap();
ModuleSource::Module(module.owned())
};
Ok((file_id, module_source))
}
pub fn declaration_source_impl(
&self,
db: &impl HirDatabase,
) -> Cancelable<Option<(FileId, ast::ModuleNode)>> {
let loc = self.def_id.loc(db);
let module_tree = db.module_tree(loc.source_root_id)?;
let link = ctry!(loc.module_id.parent_link(&module_tree));
let file_id = link
.owner(&module_tree)
.source(&module_tree)
.file_id()
.as_original_file();
let src = link.bind_source(&module_tree, db);
Ok(Some((file_id, src)))
}
pub(crate) fn krate_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Crate>> {
let root = self.crate_root(db)?;
let loc = root.def_id.loc(db);
let file_id = loc.source_item_id.file_id.as_original_file();
let crate_graph = db.crate_graph();
let crate_id = ctry!(crate_graph.crate_id_for_crate_root(file_id));
Ok(Some(Crate::new(crate_id)))
}
pub(crate) fn crate_root_impl(&self, db: &impl HirDatabase) -> Cancelable<Module> {
let loc = self.def_id.loc(db);
let module_tree = db.module_tree(loc.source_root_id)?;
let module_id = loc.module_id.crate_root(&module_tree);
Module::from_module_id(db, loc.source_root_id, module_id)
}
/// Finds a child module with the specified name.
pub fn child_impl(&self, db: &impl HirDatabase, name: &Name) -> Cancelable<Option<Module>> {
let loc = self.def_id.loc(db);
let module_tree = db.module_tree(loc.source_root_id)?;
let child_id = ctry!(loc.module_id.child(&module_tree, name));
Module::from_module_id(db, loc.source_root_id, child_id).map(Some)
}
pub fn parent_impl(&self, db: &impl HirDatabase) -> Cancelable<Option<Module>> {
let loc = self.def_id.loc(db);
let module_tree = db.module_tree(loc.source_root_id)?;
let parent_id = ctry!(loc.module_id.parent(&module_tree));
Module::from_module_id(db, loc.source_root_id, parent_id).map(Some)
}
/// Returns a `ModuleScope`: a set of items, visible in this module.
pub fn scope_impl(&self, db: &impl HirDatabase) -> Cancelable<ModuleScope> {
let loc = self.def_id.loc(db);
let item_map = db.item_map(loc.source_root_id)?;
let res = item_map.per_module[&loc.module_id].clone();
Ok(res)
}
pub fn resolve_path_impl(
&self,
db: &impl HirDatabase,
path: &Path,
) -> Cancelable<PerNs<DefId>> {
let mut curr_per_ns = PerNs::types(
match path.kind {
PathKind::Crate => self.crate_root(db)?,
PathKind::Self_ | PathKind::Plain => self.clone(),
PathKind::Super => {
if let Some(p) = self.parent(db)? {
p
} else {
return Ok(PerNs::none());
}
}
}
.def_id,
);
let segments = &path.segments;
for name in segments.iter() {
let curr = if let Some(r) = curr_per_ns.as_ref().take_types() {
r
} else {
return Ok(PerNs::none());
};
let module = match curr.resolve(db)? {
Def::Module(it) => it,
// TODO here would be the place to handle enum variants...
_ => return Ok(PerNs::none()),
};
let scope = module.scope(db)?;
curr_per_ns = if let Some(r) = scope.get(&name) {
r.def_id
} else {
return Ok(PerNs::none());
};
}
Ok(curr_per_ns)
}
pub fn problems_impl(&self, db: &impl HirDatabase) -> Cancelable<Vec<(SyntaxNode, Problem)>> {
let loc = self.def_id.loc(db);
let module_tree = db.module_tree(loc.source_root_id)?;
Ok(loc.module_id.problems(&module_tree, db))
}
}

View file

@ -9,8 +9,8 @@ use crate::{
query_definitions, query_definitions,
FnSignature, FnScopes, FnSignature, FnScopes,
macros::MacroExpansion, macros::MacroExpansion,
module::{ModuleId, ModuleTree, ModuleSource, module_tree::{ModuleId, ModuleTree, ModuleSource},
nameres::{ItemMap, InputModuleItems}}, nameres::{ItemMap, InputModuleItems},
ty::{InferenceResult, Ty}, ty::{InferenceResult, Ty},
adt::{StructData, EnumData}, adt::{StructData, EnumData},
impl_block::ModuleImplBlocks, impl_block::ModuleImplBlocks,
@ -71,7 +71,7 @@ pub trait HirDatabase: SyntaxDatabase
use fn query_definitions::file_item; use fn query_definitions::file_item;
} }
fn submodules(source: ModuleSource) -> Cancelable<Arc<Vec<crate::module::imp::Submodule>>> { fn submodules(source: ModuleSource) -> Cancelable<Arc<Vec<crate::module_tree::Submodule>>> {
type SubmodulesQuery; type SubmodulesQuery;
use fn query_definitions::submodules; use fn query_definitions::submodules;
} }
@ -86,7 +86,7 @@ pub trait HirDatabase: SyntaxDatabase
} }
fn module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> { fn module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> {
type ModuleTreeQuery; type ModuleTreeQuery;
use fn crate::module::imp::module_tree; use fn crate::module_tree::ModuleTree::module_tree_query;
} }
fn impls_in_module(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<ModuleImplBlocks>> { fn impls_in_module(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<ModuleImplBlocks>> {

View file

@ -10,7 +10,7 @@ use crate::{
Function, Function,
db::HirDatabase, db::HirDatabase,
type_ref::TypeRef, type_ref::TypeRef,
module::ModuleId, module_tree::ModuleId,
}; };
use crate::code_model_api::{Module, ModuleSource}; use crate::code_model_api::{Module, ModuleSource};

View file

@ -24,7 +24,8 @@ pub mod source_binder;
mod ids; mod ids;
mod macros; mod macros;
mod name; mod name;
mod module; mod module_tree;
mod nameres;
mod function; mod function;
mod adt; mod adt;
mod type_ref; mod type_ref;
@ -32,14 +33,13 @@ mod ty;
mod impl_block; mod impl_block;
mod expr; mod expr;
pub mod code_model_api; mod code_model_api;
mod code_model_impl; mod code_model_impl;
use crate::{ use crate::{
db::HirDatabase, db::HirDatabase,
name::{AsName, KnownName}, name::{AsName, KnownName},
ids::{DefKind, SourceItemId, SourceFileItemId, SourceFileItems}, ids::{DefKind, SourceItemId, SourceFileItemId, SourceFileItems},
code_model_api::{Crate, CrateDependency}
}; };
pub use self::{ pub use self::{
@ -56,7 +56,10 @@ pub use self::{
pub use self::function::FnSignatureInfo; pub use self::function::FnSignatureInfo;
pub use self::code_model_api::Module; pub use self::code_model_api::{
Crate, CrateDependency,
Module, ModuleSource, Problem,
};
pub enum Def { pub enum Def {
Module(Module), Module(Module),

View file

@ -1,190 +0,0 @@
use std::sync::Arc;
use ra_syntax::ast::{self, NameOwner};
use relative_path::RelativePathBuf;
use rustc_hash::{FxHashMap, FxHashSet};
use arrayvec::ArrayVec;
use ra_db::{SourceRoot, SourceRootId, Cancelable, FileId};
use crate::{
HirDatabase, Name, AsName,
};
use super::{
LinkData, LinkId, ModuleData, ModuleId, ModuleSource,
ModuleTree, Problem,
};
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
pub enum Submodule {
Declaration(Name),
Definition(Name, ModuleSource),
}
impl Submodule {
fn name(&self) -> &Name {
match self {
Submodule::Declaration(name) => name,
Submodule::Definition(name, _) => name,
}
}
}
pub(crate) fn modules<'a>(
root: impl ast::ModuleItemOwner<'a>,
) -> impl Iterator<Item = (Name, ast::Module<'a>)> {
root.items()
.filter_map(|item| match item {
ast::ModuleItem::Module(m) => Some(m),
_ => None,
})
.filter_map(|module| {
let name = module.name()?.as_name();
Some((name, module))
})
}
pub(crate) fn module_tree(
db: &impl HirDatabase,
source_root: SourceRootId,
) -> Cancelable<Arc<ModuleTree>> {
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<ModuleTree> {
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.values() {
let source = ModuleSource::new_file(file_id.into());
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<ModuleSource>,
roots: &mut FxHashMap<FileId, ModuleId>,
parent: Option<LinkId>,
source: ModuleSource,
) -> Cancelable<ModuleId> {
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(db, source, &name);
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::new_file(file_id.into()),
),
})
.collect::<Cancelable<Vec<_>>>()?;
(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(
db: &impl HirDatabase,
source: ModuleSource,
name: &Name,
) -> (Vec<FileId>, Option<Problem>) {
// FIXME: handle submodules of inline modules properly
let file_id = source.file_id().original_file(db);
let source_root_id = db.file_source_root(file_id);
let path = db.file_relative_path(file_id);
let root = RelativePathBuf::default();
let dir_path = path.parent().unwrap_or(&root);
let mod_name = path.file_stem().unwrap_or("unknown");
let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main";
let file_mod = dir_path.join(format!("{}.rs", name));
let dir_mod = dir_path.join(format!("{}/mod.rs", name));
let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name));
let mut candidates = ArrayVec::<[_; 2]>::new();
if is_dir_owner {
candidates.push(file_mod.clone());
candidates.push(dir_mod);
} else {
candidates.push(file_dir_mod.clone());
};
let sr = db.source_root(source_root_id);
let points_to = candidates
.into_iter()
.filter_map(|path| sr.files.get(&path))
.map(|&it| it)
.collect::<Vec<_>>();
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)
}

View file

@ -1,17 +1,32 @@
pub(super) mod imp; use std::sync::Arc;
pub(super) mod nameres;
use rustc_hash::{FxHashMap, FxHashSet};
use arrayvec::ArrayVec;
use relative_path::RelativePathBuf;
use ra_db::{FileId, SourceRootId, Cancelable, SourceRoot};
use ra_syntax::{ use ra_syntax::{
algo::generate, algo::generate,
ast::{self, AstNode, NameOwner}, ast::{self, AstNode, NameOwner},
SyntaxNode, SyntaxNode,
}; };
use ra_arena::{Arena, RawId, impl_arena_id}; use ra_arena::{Arena, RawId, impl_arena_id};
use relative_path::RelativePathBuf;
use crate::{Name, HirDatabase, SourceItemId, SourceFileItemId, HirFileId}; use crate::{Name, AsName, HirDatabase, SourceItemId, SourceFileItemId, HirFileId, Problem};
pub use self::nameres::{ModuleScope, Resolution, Namespace, PerNs}; #[derive(Clone, Hash, PartialEq, Eq, Debug)]
pub enum Submodule {
Declaration(Name),
Definition(Name, ModuleSource),
}
impl Submodule {
fn name(&self) -> &Name {
match self {
Submodule::Declaration(name) => name,
Submodule::Definition(name, _) => name,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ModuleId(RawId); pub struct ModuleId(RawId);
@ -34,7 +49,31 @@ pub struct ModuleTree {
links: Arena<LinkId, LinkData>, links: Arena<LinkId, LinkData>,
} }
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ModuleData {
source: ModuleSource,
parent: Option<LinkId>,
children: Vec<LinkId>,
}
#[derive(Hash, Debug, PartialEq, Eq)]
struct LinkData {
owner: ModuleId,
name: Name,
points_to: Vec<ModuleId>,
problem: Option<Problem>,
}
impl ModuleTree { impl ModuleTree {
pub(crate) fn module_tree_query(
db: &impl HirDatabase,
source_root: SourceRootId,
) -> Cancelable<Arc<ModuleTree>> {
db.check_canceled()?;
let res = create_module_tree(db, source_root)?;
Ok(Arc::new(res))
}
pub(crate) fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a { pub(crate) fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a {
self.mods.iter().map(|(id, _)| id) self.mods.iter().map(|(id, _)| id)
} }
@ -58,17 +97,6 @@ pub(crate) enum ModuleSourceNode {
Module(ast::ModuleNode), Module(ast::ModuleNode),
} }
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum Problem {
UnresolvedModule {
candidate: RelativePathBuf,
},
NotDirOwner {
move_to: RelativePathBuf,
candidate: RelativePathBuf,
},
}
impl ModuleId { impl ModuleId {
pub(crate) fn source(self, tree: &ModuleTree) -> ModuleSource { pub(crate) fn source(self, tree: &ModuleTree) -> ModuleSource {
tree.mods[self].source tree.mods[self].source
@ -93,7 +121,10 @@ impl ModuleId {
.find(|it| it.name == *name)?; .find(|it| it.name == *name)?;
Some(*link.points_to.first()?) Some(*link.points_to.first()?)
} }
fn children<'a>(self, tree: &'a ModuleTree) -> impl Iterator<Item = (Name, ModuleId)> + 'a { pub(crate) fn children<'a>(
self,
tree: &'a ModuleTree,
) -> impl Iterator<Item = (Name, ModuleId)> + 'a {
tree.mods[self].children.iter().filter_map(move |&it| { tree.mods[self].children.iter().filter_map(move |&it| {
let link = &tree.links[it]; let link = &tree.links[it];
let module = *link.points_to.first()?; let module = *link.points_to.first()?;
@ -133,7 +164,7 @@ impl LinkId {
let owner = self.owner(tree); let owner = self.owner(tree);
match owner.source(tree).resolve(db) { match owner.source(tree).resolve(db) {
ModuleSourceNode::SourceFile(root) => { ModuleSourceNode::SourceFile(root) => {
let ast = imp::modules(root.borrowed()) let ast = modules(root.borrowed())
.find(|(name, _)| name == &tree.links[self].name) .find(|(name, _)| name == &tree.links[self].name)
.unwrap() .unwrap()
.1; .1;
@ -144,13 +175,6 @@ impl LinkId {
} }
} }
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ModuleData {
source: ModuleSource,
parent: Option<LinkId>,
children: Vec<LinkId>,
}
impl ModuleSource { impl ModuleSource {
// precondition: item_id **must** point to module // precondition: item_id **must** point to module
fn new(file_id: HirFileId, item_id: Option<SourceFileItemId>) -> ModuleSource { fn new(file_id: HirFileId, item_id: Option<SourceFileItemId>) -> ModuleSource {
@ -188,14 +212,6 @@ impl ModuleSource {
} }
} }
#[derive(Hash, Debug, PartialEq, Eq)]
struct LinkData {
owner: ModuleId,
name: Name,
points_to: Vec<ModuleId>,
problem: Option<Problem>,
}
impl ModuleTree { impl ModuleTree {
fn push_mod(&mut self, data: ModuleData) -> ModuleId { fn push_mod(&mut self, data: ModuleData) -> ModuleId {
self.mods.alloc(data) self.mods.alloc(data)
@ -207,3 +223,153 @@ impl ModuleTree {
id id
} }
} }
fn modules<'a>(
root: impl ast::ModuleItemOwner<'a>,
) -> impl Iterator<Item = (Name, ast::Module<'a>)> {
root.items()
.filter_map(|item| match item {
ast::ModuleItem::Module(m) => Some(m),
_ => None,
})
.filter_map(|module| {
let name = module.name()?.as_name();
Some((name, module))
})
}
fn create_module_tree<'a>(
db: &impl HirDatabase,
source_root: SourceRootId,
) -> Cancelable<ModuleTree> {
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.values() {
let source = ModuleSource::new_file(file_id.into());
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<ModuleSource>,
roots: &mut FxHashMap<FileId, ModuleId>,
parent: Option<LinkId>,
source: ModuleSource,
) -> Cancelable<ModuleId> {
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(db, source, &name);
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::new_file(file_id.into()),
),
})
.collect::<Cancelable<Vec<_>>>()?;
(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(
db: &impl HirDatabase,
source: ModuleSource,
name: &Name,
) -> (Vec<FileId>, Option<Problem>) {
// FIXME: handle submodules of inline modules properly
let file_id = source.file_id().original_file(db);
let source_root_id = db.file_source_root(file_id);
let path = db.file_relative_path(file_id);
let root = RelativePathBuf::default();
let dir_path = path.parent().unwrap_or(&root);
let mod_name = path.file_stem().unwrap_or("unknown");
let is_dir_owner = mod_name == "mod" || mod_name == "lib" || mod_name == "main";
let file_mod = dir_path.join(format!("{}.rs", name));
let dir_mod = dir_path.join(format!("{}/mod.rs", name));
let file_dir_mod = dir_path.join(format!("{}/{}.rs", mod_name, name));
let mut candidates = ArrayVec::<[_; 2]>::new();
if is_dir_owner {
candidates.push(file_mod.clone());
candidates.push(dir_mod);
} else {
candidates.push(file_dir_mod.clone());
};
let sr = db.source_root(source_root_id);
let points_to = candidates
.into_iter()
.filter_map(|path| sr.files.get(&path))
.map(|&it| it)
.collect::<Vec<_>>();
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)
}

View file

@ -31,7 +31,7 @@ use crate::{
Path, PathKind, Path, PathKind,
HirDatabase, Crate, HirDatabase, Crate,
Name, AsName, Name, AsName,
module::{ModuleId, ModuleTree}, module_tree::{ModuleId, ModuleTree},
}; };
/// Item map is the result of the name resolution. Item map contains, for each /// Item map is the result of the name resolution. Item map contains, for each

View file

@ -17,7 +17,7 @@ fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) {
let module = hir::source_binder::module_from_position(&db, pos) let module = hir::source_binder::module_from_position(&db, pos)
.unwrap() .unwrap()
.unwrap(); .unwrap();
let module_id = module.def_id.loc(&db).module_id; let module_id = module.module_id;
(db.item_map(source_root).unwrap(), module_id) (db.item_map(source_root).unwrap(), module_id)
} }
@ -155,7 +155,7 @@ fn item_map_across_crates() {
let module = hir::source_binder::module_from_file_id(&db, main_id) let module = hir::source_binder::module_from_file_id(&db, main_id)
.unwrap() .unwrap()
.unwrap(); .unwrap();
let module_id = module.def_id.loc(&db).module_id; let module_id = module.module_id;
let item_map = db.item_map(source_root).unwrap(); let item_map = db.item_map(source_root).unwrap();
check_module_item_map( check_module_item_map(

View file

@ -15,11 +15,8 @@ use crate::{
MacroCallLoc, MacroCallLoc,
db::HirDatabase, db::HirDatabase,
function::FnScopes, function::FnScopes,
module::{ module_tree::{ModuleId, Submodule, ModuleSource, ModuleSourceNode},
ModuleSource, ModuleSourceNode, ModuleId, nameres::{InputModuleItems, ItemMap, Resolver},
imp::Submodule,
nameres::{InputModuleItems, ItemMap, Resolver},
},
adt::{StructData, EnumData}, adt::{StructData, EnumData},
}; };

View file

@ -14,7 +14,7 @@ use ra_syntax::{
use crate::{ use crate::{
HirDatabase, Function, SourceItemId, HirDatabase, Function, SourceItemId,
module::ModuleSource, module_tree::ModuleSource,
DefKind, DefLoc, AsName, DefKind, DefLoc, AsName,
}; };