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_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
/// crate's dependencies interact. Mostly, it should be just a proxy for the
@ -39,6 +40,17 @@ pub enum ModuleSource {
Module(ast::ModuleNode),
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum Problem {
UnresolvedModule {
candidate: RelativePathBuf,
},
NotDirOwner {
move_to: RelativePathBuf,
candidate: RelativePathBuf,
},
}
impl Module {
/// Name of this module.
pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<Name>> {

View file

@ -1,194 +1,2 @@
use ra_db::{CrateId, Cancelable, SourceRootId, FileId};
use ra_syntax::{ast, SyntaxNode, AstNode};
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))
}
}
mod krate; // `crate` is invalid ident :(
pub(crate) mod module;

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,
FnSignature, FnScopes,
macros::MacroExpansion,
module::{ModuleId, ModuleTree, ModuleSource,
nameres::{ItemMap, InputModuleItems}},
module_tree::{ModuleId, ModuleTree, ModuleSource},
nameres::{ItemMap, InputModuleItems},
ty::{InferenceResult, Ty},
adt::{StructData, EnumData},
impl_block::ModuleImplBlocks,
@ -71,7 +71,7 @@ pub trait HirDatabase: SyntaxDatabase
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;
use fn query_definitions::submodules;
}
@ -86,7 +86,7 @@ pub trait HirDatabase: SyntaxDatabase
}
fn module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> {
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>> {

View file

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

View file

@ -24,7 +24,8 @@ pub mod source_binder;
mod ids;
mod macros;
mod name;
mod module;
mod module_tree;
mod nameres;
mod function;
mod adt;
mod type_ref;
@ -32,14 +33,13 @@ mod ty;
mod impl_block;
mod expr;
pub mod code_model_api;
mod code_model_api;
mod code_model_impl;
use crate::{
db::HirDatabase,
name::{AsName, KnownName},
ids::{DefKind, SourceItemId, SourceFileItemId, SourceFileItems},
code_model_api::{Crate, CrateDependency}
};
pub use self::{
@ -56,7 +56,10 @@ pub use self::{
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 {
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;
pub(super) mod nameres;
use std::sync::Arc;
use rustc_hash::{FxHashMap, FxHashSet};
use arrayvec::ArrayVec;
use relative_path::RelativePathBuf;
use ra_db::{FileId, SourceRootId, Cancelable, SourceRoot};
use ra_syntax::{
algo::generate,
ast::{self, AstNode, NameOwner},
SyntaxNode,
};
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)]
pub struct ModuleId(RawId);
@ -34,7 +49,31 @@ pub struct ModuleTree {
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 {
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 {
self.mods.iter().map(|(id, _)| id)
}
@ -58,17 +97,6 @@ pub(crate) enum ModuleSourceNode {
Module(ast::ModuleNode),
}
#[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
@ -93,7 +121,10 @@ impl ModuleId {
.find(|it| it.name == *name)?;
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| {
let link = &tree.links[it];
let module = *link.points_to.first()?;
@ -133,7 +164,7 @@ impl LinkId {
let owner = self.owner(tree);
match owner.source(tree).resolve(db) {
ModuleSourceNode::SourceFile(root) => {
let ast = imp::modules(root.borrowed())
let ast = modules(root.borrowed())
.find(|(name, _)| name == &tree.links[self].name)
.unwrap()
.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 {
// precondition: item_id **must** point to module
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 {
fn push_mod(&mut self, data: ModuleData) -> ModuleId {
self.mods.alloc(data)
@ -207,3 +223,153 @@ impl ModuleTree {
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,
HirDatabase, Crate,
Name, AsName,
module::{ModuleId, ModuleTree},
module_tree::{ModuleId, ModuleTree},
};
/// 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)
.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)
}
@ -155,7 +155,7 @@ fn item_map_across_crates() {
let module = hir::source_binder::module_from_file_id(&db, main_id)
.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();
check_module_item_map(

View file

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

View file

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