mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-26 20:09:19 +00:00
flatten module structure
This commit is contained in:
parent
5a505189a8
commit
fd4456d0ec
13 changed files with 431 additions and 436 deletions
|
@ -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>> {
|
||||
|
|
|
@ -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;
|
||||
|
|
45
crates/ra_hir/src/code_model_impl/krate.rs
Normal file
45
crates/ra_hir/src/code_model_impl/krate.rs
Normal 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))
|
||||
}
|
||||
}
|
154
crates/ra_hir/src/code_model_impl/module.rs
Normal file
154
crates/ra_hir/src/code_model_impl/module.rs
Normal 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))
|
||||
}
|
||||
}
|
|
@ -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>> {
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
|||
Function,
|
||||
db::HirDatabase,
|
||||
type_ref::TypeRef,
|
||||
module::ModuleId,
|
||||
module_tree::ModuleId,
|
||||
};
|
||||
|
||||
use crate::code_model_api::{Module, ModuleSource};
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
|
@ -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(
|
|
@ -15,11 +15,8 @@ use crate::{
|
|||
MacroCallLoc,
|
||||
db::HirDatabase,
|
||||
function::FnScopes,
|
||||
module::{
|
||||
ModuleSource, ModuleSourceNode, ModuleId,
|
||||
imp::Submodule,
|
||||
module_tree::{ModuleId, Submodule, ModuleSource, ModuleSourceNode},
|
||||
nameres::{InputModuleItems, ItemMap, Resolver},
|
||||
},
|
||||
adt::{StructData, EnumData},
|
||||
};
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ use ra_syntax::{
|
|||
|
||||
use crate::{
|
||||
HirDatabase, Function, SourceItemId,
|
||||
module::ModuleSource,
|
||||
module_tree::ModuleSource,
|
||||
DefKind, DefLoc, AsName,
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue