Replace module_tree with CrateDefMap

This commit is contained in:
Aleksey Kladov 2019-03-13 16:38:02 +03:00
parent 182c05a96c
commit 2195d1db6d
16 changed files with 333 additions and 511 deletions

View file

@ -9,12 +9,12 @@ use crate::{
HirDatabase, PersistentHirDatabase,
type_ref::TypeRef,
nameres::{ModuleScope, Namespace, lower::ImportId},
nameres::crate_def_map::ModuleId,
expr::{Body, BodySourceMap},
ty::InferenceResult,
adt::{EnumVariantId, StructFieldId, VariantDef},
generics::GenericParams,
docs::{Documentation, Docs, docs_from_ast},
module_tree::ModuleId,
ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId},
impl_block::ImplBlock,
resolve::Resolver,

View file

@ -18,9 +18,7 @@ impl Crate {
.collect()
}
pub(crate) fn root_module_impl(&self, db: &impl PersistentHirDatabase) -> Option<Module> {
let module_tree = db.module_tree(*self);
let module_id = module_tree.modules().next()?;
let module_id = db.crate_def_map(*self).root();
let module = Module { krate: *self, module_id };
Some(module)
}

View file

@ -1,33 +1,62 @@
use ra_syntax::{ast, SyntaxNode, TreeArc};
use ra_db::FileId;
use ra_syntax::{ast, SyntaxNode, TreeArc, AstNode};
use crate::{
Module, ModuleSource, Problem,
Name,
module_tree::ModuleId,
Module, ModuleSource, Problem, Name,
nameres::crate_def_map::ModuleId,
nameres::lower::ImportId,
HirDatabase, PersistentHirDatabase,
HirFileId
HirFileId, SourceItemId,
};
impl ModuleSource {
pub(crate) fn new(
db: &impl PersistentHirDatabase,
file_id: Option<FileId>,
decl_id: Option<SourceItemId>,
) -> ModuleSource {
match (file_id, decl_id) {
(Some(file_id), _) => {
let source_file = db.parse(file_id);
ModuleSource::SourceFile(source_file)
}
(None, Some(item_id)) => {
let module = db.file_item(item_id);
let module = ast::Module::cast(&*module).unwrap();
assert!(module.item_list().is_some(), "expected inline module");
ModuleSource::Module(module.to_owned())
}
(None, None) => panic!(),
}
}
}
impl Module {
fn with_module_id(&self, module_id: ModuleId) -> Module {
Module { module_id, krate: self.krate }
}
pub(crate) fn name_impl(&self, db: &impl HirDatabase) -> Option<Name> {
let module_tree = db.module_tree(self.krate);
let link = self.module_id.parent_link(&module_tree)?;
Some(link.name(&module_tree).clone())
let def_map = db.crate_def_map(self.krate);
let parent = def_map[self.module_id].parent?;
def_map[parent].children.iter().find_map(|(name, module_id)| {
if *module_id == self.module_id {
Some(name.clone())
} else {
None
}
})
}
pub(crate) fn definition_source_impl(
&self,
db: &impl PersistentHirDatabase,
) -> (HirFileId, ModuleSource) {
let module_tree = db.module_tree(self.krate);
let file_id = self.module_id.file_id(&module_tree);
let decl_id = self.module_id.decl_id(&module_tree);
let def_map = db.crate_def_map(self.krate);
let decl_id = def_map[self.module_id].declaration;
let file_id = def_map[self.module_id].definition;
let module_source = ModuleSource::new(db, file_id, decl_id);
let file_id = file_id.map(HirFileId::from).unwrap_or_else(|| decl_id.unwrap().file_id);
(file_id, module_source)
}
@ -35,11 +64,11 @@ impl Module {
&self,
db: &impl HirDatabase,
) -> Option<(HirFileId, TreeArc<ast::Module>)> {
let module_tree = db.module_tree(self.krate);
let link = self.module_id.parent_link(&module_tree)?;
let file_id = link.owner(&module_tree).file_id(&module_tree);
let src = link.source(&module_tree, db);
Some((file_id, src))
let def_map = db.crate_def_map(self.krate);
let decl = def_map[self.module_id].declaration?;
let syntax_node = db.file_item(decl);
let ast = ast::Module::cast(&syntax_node).unwrap().to_owned();
Some((decl.file_id, ast))
}
pub(crate) fn import_source_impl(
@ -53,16 +82,15 @@ impl Module {
}
pub(crate) fn crate_root_impl(&self, db: &impl PersistentHirDatabase) -> Module {
let module_tree = db.module_tree(self.krate);
let module_id = self.module_id.crate_root(&module_tree);
self.with_module_id(module_id)
let def_map = db.crate_def_map(self.krate);
self.with_module_id(def_map.root())
}
/// Finds a child module with the specified name.
pub(crate) fn child_impl(&self, db: &impl HirDatabase, name: &Name) -> Option<Module> {
let module_tree = db.module_tree(self.krate);
let child_id = self.module_id.child(&module_tree, name)?;
Some(self.with_module_id(child_id))
let def_map = db.crate_def_map(self.krate);
let child_id = def_map[self.module_id].children.get(name)?;
Some(self.with_module_id(*child_id))
}
/// Iterates over all child modules.
@ -70,18 +98,18 @@ impl Module {
&self,
db: &impl PersistentHirDatabase,
) -> impl Iterator<Item = Module> {
let module_tree = db.module_tree(self.krate);
let children = self
.module_id
.children(&module_tree)
.map(|(_, module_id)| self.with_module_id(module_id))
let def_map = db.crate_def_map(self.krate);
let children = def_map[self.module_id]
.children
.iter()
.map(|(_, module_id)| self.with_module_id(*module_id))
.collect::<Vec<_>>();
children.into_iter()
}
pub(crate) fn parent_impl(&self, db: &impl PersistentHirDatabase) -> Option<Module> {
let module_tree = db.module_tree(self.krate);
let parent_id = self.module_id.parent(&module_tree)?;
let def_map = db.crate_def_map(self.krate);
let parent_id = def_map[self.module_id].parent?;
Some(self.with_module_id(parent_id))
}
@ -89,7 +117,14 @@ impl Module {
&self,
db: &impl HirDatabase,
) -> Vec<(TreeArc<SyntaxNode>, Problem)> {
let module_tree = db.module_tree(self.krate);
self.module_id.problems(&module_tree, db)
let def_map = db.crate_def_map(self.krate);
let (my_file_id, _) = self.definition_source(db);
// FIXME: not entirely corret filterint by module
def_map
.problems()
.iter()
.filter(|(source_item_id, _problem)| my_file_id == source_item_id.file_id)
.map(|(source_item_id, problem)| (db.file_item(*source_item_id), problem.clone()))
.collect()
}
}

View file

@ -1,7 +1,7 @@
use std::sync::Arc;
use ra_syntax::{SyntaxNode, TreeArc, SourceFile};
use ra_db::{SourceDatabase, salsa};
use ra_db::{SourceDatabase, salsa, FileId};
use crate::{
MacroCallId, HirFileId,
@ -10,14 +10,11 @@ use crate::{
Struct, Enum, StructField,
Const, ConstSignature, Static,
macros::MacroExpansion,
module_tree::ModuleTree,
nameres::{ItemMap, lower::{LoweredModule, ImportSourceMap}},
nameres::{Namespace, ItemMap, lower::{LoweredModule, ImportSourceMap}, crate_def_map::{RawItems, CrateDefMap}},
ty::{InferenceResult, Ty, method_resolution::CrateImplBlocks, TypableDef, CallableDef, FnSig},
adt::{StructData, EnumData},
impl_block::{ModuleImplBlocks, ImplSourceMap},
generics::{GenericParams, GenericDef},
ids::SourceFileItemId,
nameres::Namespace,
type_ref::TypeRef,
};
@ -41,13 +38,6 @@ pub trait PersistentHirDatabase: SourceDatabase + AsRef<HirInterner> {
#[salsa::invoke(crate::ids::SourceFileItems::file_item_query)]
fn file_item(&self, source_item_id: SourceItemId) -> TreeArc<SyntaxNode>;
#[salsa::invoke(crate::module_tree::Submodule::submodules_query)]
fn submodules(
&self,
file_id: HirFileId,
delc_id: Option<SourceFileItemId>,
) -> Arc<Vec<crate::module_tree::Submodule>>;
#[salsa::invoke(crate::nameres::lower::LoweredModule::lower_module_with_source_map_query)]
fn lower_module_with_source_map(
&self,
@ -57,11 +47,14 @@ pub trait PersistentHirDatabase: SourceDatabase + AsRef<HirInterner> {
#[salsa::invoke(crate::nameres::lower::LoweredModule::lower_module_query)]
fn lower_module(&self, module: Module) -> Arc<LoweredModule>;
#[salsa::invoke(RawItems::raw_items_query)]
fn raw_items(&self, file_id: FileId) -> Arc<RawItems>;
#[salsa::invoke(crate::nameres::ItemMap::item_map_query)]
fn item_map(&self, krate: Crate) -> Arc<ItemMap>;
#[salsa::invoke(crate::module_tree::ModuleTree::module_tree_query)]
fn module_tree(&self, krate: Crate) -> Arc<ModuleTree>;
#[salsa::invoke(CrateDefMap::crate_def_map_query)]
fn crate_def_map(&self, krate: Crate) -> Arc<CrateDefMap>;
#[salsa::invoke(crate::impl_block::impls_in_module)]
fn impls_in_module(&self, module: Module) -> Arc<ModuleImplBlocks>;

View file

@ -296,6 +296,12 @@ impl AstItemDef<ast::TypeAliasDef> for TypeId {
pub struct SourceFileItemId(RawId);
impl_arena_id!(SourceFileItemId);
impl SourceFileItemId {
pub(crate) fn with_file_id(self, file_id: HirFileId) -> SourceItemId {
SourceItemId { file_id, item_id: self }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SourceItemId {
pub(crate) file_id: HirFileId,

View file

@ -26,7 +26,6 @@ pub mod source_binder;
mod ids;
mod macros;
mod name;
mod module_tree;
mod nameres;
mod adt;
mod type_alias;

View file

@ -1,340 +0,0 @@
use std::sync::Arc;
use arrayvec::ArrayVec;
use relative_path::RelativePathBuf;
use ra_db::{FileId, SourceRoot};
use ra_syntax::{
SyntaxNode, TreeArc,
algo::generate,
ast::{self, AstNode, NameOwner},
};
use ra_arena::{Arena, RawId, impl_arena_id};
use test_utils::tested_by;
use crate::{
Name, AsName, HirDatabase, SourceItemId, HirFileId, Problem, SourceFileItems, ModuleSource,
PersistentHirDatabase,
Crate,
ids::SourceFileItemId,
};
impl ModuleSource {
pub(crate) fn new(
db: &impl PersistentHirDatabase,
file_id: HirFileId,
decl_id: Option<SourceFileItemId>,
) -> ModuleSource {
match decl_id {
Some(item_id) => {
let module = db.file_item(SourceItemId { file_id, item_id });
let module = ast::Module::cast(&*module).unwrap();
assert!(module.item_list().is_some(), "expected inline module");
ModuleSource::Module(module.to_owned())
}
None => {
let source_file = db.hir_parse(file_id);
ModuleSource::SourceFile(source_file)
}
}
}
}
#[derive(Clone, Hash, PartialEq, Eq, Debug)]
pub struct Submodule {
name: Name,
is_declaration: bool,
decl_id: SourceFileItemId,
}
impl Submodule {
pub(crate) fn submodules_query(
db: &impl PersistentHirDatabase,
file_id: HirFileId,
decl_id: Option<SourceFileItemId>,
) -> Arc<Vec<Submodule>> {
db.check_canceled();
let file_items = db.file_items(file_id);
let module_source = ModuleSource::new(db, file_id, decl_id);
let submodules = match module_source {
ModuleSource::SourceFile(source_file) => {
collect_submodules(file_id, &file_items, &*source_file)
}
ModuleSource::Module(module) => {
collect_submodules(file_id, &file_items, module.item_list().unwrap())
}
};
return Arc::new(submodules);
fn collect_submodules(
file_id: HirFileId,
file_items: &SourceFileItems,
root: &impl ast::ModuleItemOwner,
) -> Vec<Submodule> {
root.items()
.filter_map(|item| match item.kind() {
ast::ModuleItemKind::Module(m) => Some(m),
_ => None,
})
.filter_map(|module| {
let name = module.name()?.as_name();
if !module.has_semi() && module.item_list().is_none() {
tested_by!(name_res_works_for_broken_modules);
return None;
}
let sub = Submodule {
name,
is_declaration: module.has_semi(),
decl_id: file_items.id_of(file_id, module.syntax()),
};
Some(sub)
})
.collect()
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ModuleId(RawId);
impl_arena_id!(ModuleId);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LinkId(RawId);
impl_arena_id!(LinkId);
/// Physically, 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 neccessarily always the case.
///
/// `ModuleTree` encapsulates 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<ModuleId, ModuleData>,
links: Arena<LinkId, LinkData>,
}
#[derive(Debug, PartialEq, Eq)]
pub struct ModuleData {
file_id: HirFileId,
/// Points to `ast::Module`, `None` for the whole file.
decl_id: Option<SourceFileItemId>,
parent: Option<LinkId>,
children: Vec<LinkId>,
}
#[derive(Hash, Debug, PartialEq, Eq)]
struct LinkData {
source: SourceItemId,
owner: ModuleId,
name: Name,
points_to: Vec<ModuleId>,
problem: Option<Problem>,
}
impl ModuleTree {
pub(crate) fn module_tree_query(
db: &impl PersistentHirDatabase,
krate: Crate,
) -> Arc<ModuleTree> {
db.check_canceled();
let mut res = ModuleTree::default();
res.init_crate(db, krate);
Arc::new(res)
}
pub(crate) fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a {
self.mods.iter().map(|(id, _)| id)
}
pub(crate) fn find_module_by_source(
&self,
file_id: HirFileId,
decl_id: Option<SourceFileItemId>,
) -> Option<ModuleId> {
let (res, _) =
self.mods.iter().find(|(_, m)| (m.file_id, m.decl_id) == (file_id, decl_id))?;
Some(res)
}
fn init_crate(&mut self, db: &impl PersistentHirDatabase, krate: Crate) {
let crate_graph = db.crate_graph();
let file_id = crate_graph.crate_root(krate.crate_id);
let source_root_id = db.file_source_root(file_id);
let source_root = db.source_root(source_root_id);
self.init_subtree(db, &source_root, None, file_id.into(), None);
}
fn init_subtree(
&mut self,
db: &impl PersistentHirDatabase,
source_root: &SourceRoot,
parent: Option<LinkId>,
file_id: HirFileId,
decl_id: Option<SourceFileItemId>,
) -> ModuleId {
let is_root = parent.is_none();
let id = self.alloc_mod(ModuleData { file_id, decl_id, parent, children: Vec::new() });
for sub in db.submodules(file_id, decl_id).iter() {
let link = self.alloc_link(LinkData {
source: SourceItemId { file_id, item_id: sub.decl_id },
name: sub.name.clone(),
owner: id,
points_to: Vec::new(),
problem: None,
});
let (points_to, problem) = if sub.is_declaration {
let (points_to, problem) = resolve_submodule(db, file_id, &sub.name, is_root);
let points_to = points_to
.into_iter()
.map(|file_id| {
self.init_subtree(db, source_root, Some(link), file_id.into(), None)
})
.collect::<Vec<_>>();
(points_to, problem)
} else {
let points_to =
self.init_subtree(db, source_root, Some(link), file_id, Some(sub.decl_id));
(vec![points_to], None)
};
self.links[link].points_to = points_to;
self.links[link].problem = problem;
}
id
}
fn alloc_mod(&mut self, data: ModuleData) -> ModuleId {
self.mods.alloc(data)
}
fn alloc_link(&mut self, data: LinkData) -> LinkId {
let owner = data.owner;
let id = self.links.alloc(data);
self.mods[owner].children.push(id);
id
}
}
impl ModuleId {
pub(crate) fn file_id(self, tree: &ModuleTree) -> HirFileId {
tree.mods[self].file_id
}
pub(crate) fn decl_id(self, tree: &ModuleTree) -> Option<SourceFileItemId> {
tree.mods[self].decl_id
}
pub(crate) fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
tree.mods[self].parent
}
pub(crate) fn parent(self, tree: &ModuleTree) -> Option<ModuleId> {
let link = self.parent_link(tree)?;
Some(tree.links[link].owner)
}
pub(crate) fn crate_root(self, tree: &ModuleTree) -> ModuleId {
generate(Some(self), move |it| it.parent(tree)).last().unwrap()
}
pub(crate) fn child(self, tree: &ModuleTree, name: &Name) -> Option<ModuleId> {
let link = tree.mods[self]
.children
.iter()
.map(|&it| &tree.links[it])
.find(|it| it.name == *name)?;
Some(*link.points_to.first()?)
}
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()?;
Some((link.name.clone(), module))
})
}
pub(crate) fn problems(
self,
tree: &ModuleTree,
db: &impl HirDatabase,
) -> Vec<(TreeArc<SyntaxNode>, Problem)> {
tree.mods[self]
.children
.iter()
.filter_map(|&link| {
let p = tree.links[link].problem.clone()?;
let s = link.source(tree, db);
let s = s.name().unwrap().syntax().to_owned();
Some((s, p))
})
.collect()
}
}
impl LinkId {
pub(crate) fn owner(self, tree: &ModuleTree) -> ModuleId {
tree.links[self].owner
}
pub(crate) fn name(self, tree: &ModuleTree) -> &Name {
&tree.links[self].name
}
pub(crate) fn source(
self,
tree: &ModuleTree,
db: &impl PersistentHirDatabase,
) -> TreeArc<ast::Module> {
let syntax_node = db.file_item(tree.links[self].source);
ast::Module::cast(&syntax_node).unwrap().to_owned()
}
}
pub(crate) fn resolve_module_declaration(
db: &impl PersistentHirDatabase,
file_id: HirFileId,
name: &Name,
is_root: bool,
) -> Option<FileId> {
resolve_submodule(db, file_id, name, is_root).0.first().map(|it| *it)
}
fn resolve_submodule(
db: &impl PersistentHirDatabase,
file_id: HirFileId,
name: &Name,
is_root: bool,
) -> (Vec<FileId>, Option<Problem>) {
// FIXME: handle submodules of inline modules properly
let file_id = 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 = is_root || mod_name == "mod";
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

@ -15,7 +15,7 @@
//! so that the results of name resolution can be preserved unless the module
//! structure itself is modified.
pub(crate) mod lower;
mod crate_def_map;
pub(crate) mod crate_def_map;
use std::{time, sync::Arc};
@ -29,8 +29,10 @@ use crate::{
Module, ModuleDef,
Path, PathKind, PersistentHirDatabase,
Crate, Name,
module_tree::{ModuleId, ModuleTree},
nameres::lower::{ImportId, LoweredModule, ImportData},
nameres::{
crate_def_map::{CrateDefMap, ModuleId},
lower::{ImportId, LoweredModule, ImportData}
},
};
/// `ItemMap` is the result of module name resolution. It contains, for each
@ -160,7 +162,7 @@ struct Resolver<'a, DB> {
db: &'a DB,
input: &'a FxHashMap<ModuleId, Arc<LoweredModule>>,
krate: Crate,
module_tree: Arc<ModuleTree>,
def_map: Arc<CrateDefMap>,
processed_imports: FxHashSet<(ModuleId, ImportId)>,
/// If module `a` has `use b::*`, then this contains the mapping b -> a (and the import)
glob_imports: FxHashMap<ModuleId, Vec<(ModuleId, ImportId)>>,
@ -176,12 +178,11 @@ where
input: &'a FxHashMap<ModuleId, Arc<LoweredModule>>,
krate: Crate,
) -> Resolver<'a, DB> {
let module_tree = db.module_tree(krate);
Resolver {
db,
input,
krate,
module_tree,
def_map: db.crate_def_map(krate),
processed_imports: FxHashSet::default(),
glob_imports: FxHashMap::default(),
result: ItemMap {
@ -254,9 +255,9 @@ where
}
// Populate modules
for (name, module_id) in module_id.children(&self.module_tree) {
let module = Module { module_id, krate: self.krate };
self.add_module_item(&mut module_items, name, PerNs::types(module.into()));
for (name, module_id) in self.def_map[module_id].children.iter() {
let module = Module { module_id: *module_id, krate: self.krate };
self.add_module_item(&mut module_items, name.clone(), PerNs::types(module.into()));
}
self.result.per_module.insert(module_id, module_items);
@ -479,8 +480,8 @@ enum ReachedFixedPoint {
impl ItemMap {
pub(crate) fn item_map_query(db: &impl PersistentHirDatabase, krate: Crate) -> Arc<ItemMap> {
let start = time::Instant::now();
let module_tree = db.module_tree(krate);
let input = module_tree
let def_map = db.crate_def_map(krate);
let input = def_map
.modules()
.map(|module_id| (module_id, db.lower_module(Module { krate, module_id })))
.collect::<FxHashMap<_, _>>();

View file

@ -48,25 +48,40 @@ mod tests;
use rustc_hash::FxHashMap;
use test_utils::tested_by;
use ra_arena::Arena;
use ra_arena::{Arena, RawId, impl_arena_id};
use ra_db::FileId;
use std::sync::Arc;
use crate::{
Name, Module, Path, PathKind, ModuleDef, Crate,
Name, Module, Path, PathKind, ModuleDef, Crate, Problem, HirFileId,
PersistentHirDatabase,
module_tree::ModuleId,
nameres::{ModuleScope, ResolveMode, ResolvePathResult, PerNs, Edition, ReachedFixedPoint},
ids::{SourceItemId, SourceFileItemId},
};
#[derive(Default, Debug)]
struct ModuleData {
parent: Option<ModuleId>,
children: FxHashMap<Name, ModuleId>,
scope: ModuleScope,
pub(crate) use self::raw::RawItems;
#[derive(Default, Debug, PartialEq, Eq)]
pub(crate) struct ModuleData {
pub(crate) parent: Option<ModuleId>,
pub(crate) children: FxHashMap<Name, ModuleId>,
pub(crate) scope: ModuleScope,
/// None for root
pub(crate) declaration: Option<SourceItemId>,
/// None for inline modules.
///
/// Note that non-inline modules, by definition, live inside non-macro file.
pub(crate) definition: Option<FileId>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct ModuleId(RawId);
impl_arena_id!(ModuleId);
/// Contans all top-level defs from a macro-expanded crate
#[derive(Debug)]
pub(crate) struct CrateDefMap {
#[derive(Debug, PartialEq, Eq)]
pub struct CrateDefMap {
krate: Crate,
edition: Edition,
/// The prelude module for this crate. This either comes from an import
@ -77,19 +92,85 @@ pub(crate) struct CrateDefMap {
root: ModuleId,
modules: Arena<ModuleId, ModuleData>,
public_macros: FxHashMap<Name, mbe::MacroRules>,
problems: CrateDefMapProblems,
}
#[derive(Default, Debug, PartialEq, Eq)]
pub(crate) struct CrateDefMapProblems {
problems: Vec<(SourceItemId, Problem)>,
}
impl CrateDefMapProblems {
fn add(&mut self, source_item_id: SourceItemId, problem: Problem) {
self.problems.push((source_item_id, problem))
}
pub(crate) fn iter<'a>(&'a self) -> impl Iterator<Item = (&'a SourceItemId, &'a Problem)> + 'a {
self.problems.iter().map(|(s, p)| (s, p))
}
}
impl std::ops::Index<ModuleId> for CrateDefMap {
type Output = ModuleScope;
fn index(&self, id: ModuleId) -> &ModuleScope {
&self.modules[id].scope
type Output = ModuleData;
fn index(&self, id: ModuleId) -> &ModuleData {
&self.modules[id]
}
}
impl CrateDefMap {
pub(crate) fn crate_def_map_query(
db: &impl PersistentHirDatabase,
krate: Crate,
) -> Arc<CrateDefMap> {
let def_map = {
let edition = krate.edition(db);
let mut modules: Arena<ModuleId, ModuleData> = Arena::default();
let root = modules.alloc(ModuleData::default());
CrateDefMap {
krate,
edition,
extern_prelude: FxHashMap::default(),
prelude: None,
root,
modules,
public_macros: FxHashMap::default(),
problems: CrateDefMapProblems::default(),
}
};
let def_map = collector::collect_defs(db, def_map);
Arc::new(def_map)
}
pub(crate) fn root(&self) -> ModuleId {
self.root
}
pub(crate) fn problems(&self) -> &CrateDefMapProblems {
&self.problems
}
pub(crate) fn modules<'a>(&'a self) -> impl Iterator<Item = ModuleId> + 'a {
self.modules.iter().map(|(id, _data)| id)
}
pub(crate) fn find_module_by_source(
&self,
file_id: HirFileId,
decl_id: Option<SourceFileItemId>,
) -> Option<ModuleId> {
let decl_id = decl_id.map(|it| it.with_file_id(file_id));
let (module_id, _module_data) = self.modules.iter().find(|(_module_id, module_data)| {
if decl_id.is_some() {
module_data.declaration == decl_id
} else {
module_data.definition.map(|it| it.into()) == Some(file_id)
}
})?;
Some(module_id)
}
// Returns Yes if we are sure that additions to `ItemMap` wouldn't change
// the result.
#[allow(unused)]
fn resolve_path_fp(
&self,
db: &impl PersistentHirDatabase,
@ -182,7 +263,7 @@ impl CrateDefMap {
);
}
match self[module.module_id].items.get(&segment.name) {
match self[module.module_id].scope.items.get(&segment.name) {
Some(res) if !res.def.is_none() => res.def,
_ => {
log::debug!("path segment {:?} not found", segment.name);
@ -225,7 +306,8 @@ impl CrateDefMap {
}
fn resolve_name_in_crate_root_or_extern_prelude(&self, name: &Name) -> PerNs<ModuleDef> {
let from_crate_root = self[self.root].items.get(name).map_or(PerNs::none(), |it| it.def);
let from_crate_root =
self[self.root].scope.items.get(name).map_or(PerNs::none(), |it| it.def);
let from_extern_prelude = self.resolve_name_in_extern_prelude(name);
from_crate_root.or(from_extern_prelude)
@ -241,7 +323,7 @@ impl CrateDefMap {
// - current module / scope
// - extern prelude
// - std prelude
let from_scope = self[module].items.get(name).map_or(PerNs::none(), |it| it.def);
let from_scope = self[module].scope.items.get(name).map_or(PerNs::none(), |it| it.def);
let from_extern_prelude =
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
let from_prelude = self.resolve_in_prelude(db, name);
@ -256,7 +338,7 @@ impl CrateDefMap {
fn resolve_in_prelude(&self, db: &impl PersistentHirDatabase, name: &Name) -> PerNs<ModuleDef> {
if let Some(prelude) = self.prelude {
let resolution = if prelude.krate == self.krate {
self[prelude.module_id].items.get(name).cloned()
self[prelude.module_id].scope.items.get(name).cloned()
} else {
db.item_map(prelude.krate)[prelude.module_id].items.get(name).cloned()
};

View file

@ -1,42 +1,25 @@
use std::sync::Arc;
use arrayvec::ArrayVec;
use rustc_hash::FxHashMap;
use ra_arena::Arena;
use relative_path::RelativePathBuf;
use test_utils::tested_by;
use ra_db::FileId;
use crate::{
Function, Module, Struct, Enum, Const, Static, Trait, TypeAlias,
Crate, PersistentHirDatabase, HirFileId, Name, Path,
PersistentHirDatabase, HirFileId, Name, Path, Problem,
KnownName,
nameres::{Resolution, PerNs, ModuleDef, ReachedFixedPoint, ResolveMode},
ids::{AstItemDef, LocationCtx, MacroCallLoc, SourceItemId, MacroCallId},
module_tree::resolve_module_declaration,
};
use super::{CrateDefMap, ModuleId, ModuleData, raw};
#[allow(unused)]
pub(crate) fn crate_def_map_query(
pub(super) fn collect_defs(
db: &impl PersistentHirDatabase,
krate: Crate,
) -> Arc<CrateDefMap> {
let mut def_map = {
let edition = krate.edition(db);
let mut modules: Arena<ModuleId, ModuleData> = Arena::default();
let root = modules.alloc(ModuleData::default());
CrateDefMap {
krate,
edition,
extern_prelude: FxHashMap::default(),
prelude: None,
root,
modules,
public_macros: FxHashMap::default(),
}
};
mut def_map: CrateDefMap,
) -> CrateDefMap {
// populate external prelude
for dep in krate.dependencies(db) {
for dep in def_map.krate.dependencies(db) {
log::debug!("crate dep {:?} -> {:?}", dep.name, dep.krate);
if let Some(module) = dep.krate.root_module(db) {
def_map.extern_prelude.insert(dep.name.clone(), module.into());
@ -52,7 +35,6 @@ pub(crate) fn crate_def_map_query(
let mut collector = DefCollector {
db,
krate,
def_map,
glob_imports: FxHashMap::default(),
unresolved_imports: Vec::new(),
@ -60,14 +42,12 @@ pub(crate) fn crate_def_map_query(
global_macro_scope: FxHashMap::default(),
};
collector.collect();
let def_map = collector.finish();
Arc::new(def_map)
collector.finish()
}
/// Walks the tree of module recursively
struct DefCollector<DB> {
db: DB,
krate: Crate,
def_map: CrateDefMap,
glob_imports: FxHashMap<ModuleId, Vec<(ModuleId, raw::ImportId)>>,
unresolved_imports: Vec<(ModuleId, raw::ImportId, raw::ImportData)>,
@ -75,23 +55,16 @@ struct DefCollector<DB> {
global_macro_scope: FxHashMap<Name, mbe::MacroRules>,
}
/// Walks a single module, populating defs, imports and macros
struct ModCollector<'a, D> {
def_collector: D,
module_id: ModuleId,
file_id: HirFileId,
raw_items: &'a raw::RawItems,
}
impl<'a, DB> DefCollector<&'a DB>
where
DB: PersistentHirDatabase,
{
fn collect(&mut self) {
let crate_graph = self.db.crate_graph();
let file_id = crate_graph.crate_root(self.krate.crate_id());
let raw_items = raw::RawItems::raw_items_query(self.db, file_id);
let file_id = crate_graph.crate_root(self.def_map.krate.crate_id());
let raw_items = self.db.raw_items(file_id);
let module_id = self.def_map.root;
self.def_map.modules[module_id].definition = Some(file_id);
ModCollector {
def_collector: &mut *self,
module_id,
@ -123,10 +96,6 @@ where
}
}
fn alloc_module(&mut self) -> ModuleId {
self.def_map.modules.alloc(ModuleData::default())
}
fn resolve_imports(&mut self) -> ReachedFixedPoint {
let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
let mut resolved = Vec::new();
@ -184,7 +153,7 @@ where
if import.is_prelude {
tested_by!(std_prelude);
self.def_map.prelude = Some(m);
} else if m.krate != self.krate {
} else if m.krate != self.def_map.krate {
tested_by!(glob_across_crates);
// glob import from other crate => we can just import everything once
let item_map = self.db.item_map(m.krate);
@ -199,7 +168,7 @@ where
// glob import from same crate => we do an initial
// import, and then need to propagate any further
// additions
let scope = &self.def_map[m.module_id];
let scope = &self.def_map[m.module_id].scope;
let items = scope
.items
.iter()
@ -243,11 +212,9 @@ where
log::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
// extern crates in the crate root are special-cased to insert entries into the extern prelude: rust-lang/rust#54658
if let Some(root_module) = self.krate.root_module(self.db) {
if import.is_extern_crate && module_id == root_module.module_id {
if let Some(def) = def.take_types() {
self.def_map.extern_prelude.insert(name.clone(), def);
}
if import.is_extern_crate && module_id == self.def_map.root {
if let Some(def) = def.take_types() {
self.def_map.extern_prelude.insert(name.clone(), def);
}
}
let resolution = Resolution { def, import: Some(import_id) };
@ -324,8 +291,7 @@ where
Some(it) => it,
_ => return true,
};
// FIXME: this should be a proper query
let def_map = crate_def_map_query(self.db, krate);
let def_map = self.db.crate_def_map(krate);
let rules = def_map.public_macros.get(&path.segments[1].name).cloned();
resolved.push((*module_id, *call_id, rules, tt.clone()));
false
@ -367,6 +333,14 @@ where
}
}
/// Walks a single module, populating defs, imports and macros
struct ModCollector<'a, D> {
def_collector: D,
module_id: ModuleId,
file_id: HirFileId,
raw_items: &'a raw::RawItems,
}
impl<DB> ModCollector<'_, &'_ mut DefCollector<&'_ DB>>
where
DB: PersistentHirDatabase,
@ -389,8 +363,12 @@ where
fn collect_module(&mut self, module: &raw::ModuleData) {
match module {
// inline module, just recurse
raw::ModuleData::Definition { name, items } => {
let module_id = self.push_child_module(name.clone());
raw::ModuleData::Definition { name, items, source_item_id } => {
let module_id = self.push_child_module(
name.clone(),
source_item_id.with_file_id(self.file_id),
None,
);
ModCollector {
def_collector: &mut *self.def_collector,
module_id,
@ -400,13 +378,20 @@ where
.collect(&*items);
}
// out of line module, resovle, parse and recurse
raw::ModuleData::Declaration { name } => {
let module_id = self.push_child_module(name.clone());
raw::ModuleData::Declaration { name, source_item_id } => {
let source_item_id = source_item_id.with_file_id(self.file_id);
let is_root = self.def_collector.def_map.modules[self.module_id].parent.is_none();
if let Some(file_id) =
resolve_module_declaration(self.def_collector.db, self.file_id, name, is_root)
{
let raw_items = raw::RawItems::raw_items_query(self.def_collector.db, file_id);
let (file_ids, problem) =
resolve_submodule(self.def_collector.db, self.file_id, name, is_root);
if let Some(problem) = problem {
self.def_collector.def_map.problems.add(source_item_id, problem)
}
if let Some(&file_id) = file_ids.first() {
let module_id =
self.push_child_module(name.clone(), source_item_id, Some(file_id));
let raw_items = self.def_collector.db.raw_items(file_id);
ModCollector {
def_collector: &mut *self.def_collector,
module_id,
@ -419,15 +404,23 @@ where
}
}
fn push_child_module(&mut self, name: Name) -> ModuleId {
let res = self.def_collector.alloc_module();
self.def_collector.def_map.modules[res].parent = Some(self.module_id);
self.def_collector.def_map.modules[self.module_id].children.insert(name, res);
fn push_child_module(
&mut self,
name: Name,
declaration: SourceItemId,
definition: Option<FileId>,
) -> ModuleId {
let modules = &mut self.def_collector.def_map.modules;
let res = modules.alloc(ModuleData::default());
modules[res].parent = Some(self.module_id);
modules[res].declaration = Some(declaration);
modules[res].definition = definition;
modules[self.module_id].children.insert(name, res);
res
}
fn define_def(&mut self, def: &raw::DefData) {
let module = Module { krate: self.def_collector.krate, module_id: self.module_id };
let module = Module { krate: self.def_collector.def_map.krate, module_id: self.module_id };
let ctx = LocationCtx::new(self.def_collector.db, module, self.file_id.into());
macro_rules! id {
() => {
@ -462,7 +455,7 @@ where
let source_item_id = SourceItemId { file_id: self.file_id, item_id: mac.source_item_id };
let macro_call_id = MacroCallLoc {
module: Module { krate: self.def_collector.krate, module_id: self.module_id },
module: Module { krate: self.def_collector.def_map.krate, module_id: self.module_id },
source_item_id,
}
.id(self.def_collector.db);
@ -491,3 +484,44 @@ where
fn is_macro_rules(path: &Path) -> bool {
path.as_ident().and_then(Name::as_known_name) == Some(KnownName::MacroRules)
}
fn resolve_submodule(
db: &impl PersistentHirDatabase,
file_id: HirFileId,
name: &Name,
is_root: bool,
) -> (Vec<FileId>, Option<Problem>) {
// FIXME: handle submodules of inline modules properly
let file_id = 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 = is_root || mod_name == "mod";
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

@ -3,6 +3,7 @@ use std::{
ops::Index,
};
use test_utils::tested_by;
use ra_db::FileId;
use ra_arena::{Arena, impl_arena_id, RawId};
use ra_syntax::{
@ -15,8 +16,8 @@ use crate::{
ids::{SourceFileItemId, SourceFileItems},
};
#[derive(Default, PartialEq, Eq)]
pub(crate) struct RawItems {
#[derive(Debug, Default, PartialEq, Eq)]
pub struct RawItems {
modules: Arena<Module, ModuleData>,
imports: Arena<ImportId, ImportData>,
defs: Arena<Def, DefData>,
@ -26,18 +27,21 @@ pub(crate) struct RawItems {
}
impl RawItems {
pub(crate) fn items(&self) -> &[RawItem] {
&self.items
}
pub(crate) fn raw_items_query(db: &impl PersistentHirDatabase, file_id: FileId) -> RawItems {
pub(crate) fn raw_items_query(
db: &impl PersistentHirDatabase,
file_id: FileId,
) -> Arc<RawItems> {
let mut collector = RawItemsCollector {
raw_items: RawItems::default(),
source_file_items: db.file_items(file_id.into()),
};
let source_file = db.parse(file_id);
collector.process_module(None, &*source_file);
collector.raw_items
Arc::new(collector.raw_items)
}
pub(crate) fn items(&self) -> &[RawItem] {
&self.items
}
// We can't use queries during name resolution for fear of cycles, so this
@ -81,7 +85,7 @@ impl Index<Macro> for RawItems {
}
}
#[derive(PartialEq, Eq, Clone, Copy)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub(crate) enum RawItem {
Module(Module),
Import(ImportId),
@ -89,24 +93,24 @@ pub(crate) enum RawItem {
Macro(Macro),
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct Module(RawId);
impl_arena_id!(Module);
#[derive(PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
pub(crate) enum ModuleData {
Declaration { name: Name },
Definition { name: Name, items: Vec<RawItem> },
Declaration { name: Name, source_item_id: SourceFileItemId },
Definition { name: Name, source_item_id: SourceFileItemId, items: Vec<RawItem> },
}
pub(crate) use crate::nameres::lower::ImportId;
pub(super) use crate::nameres::lower::ImportData;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct Def(RawId);
impl_arena_id!(Def);
#[derive(PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct DefData {
pub(crate) source_item_id: SourceFileItemId,
pub(crate) name: Name,
@ -124,11 +128,11 @@ pub(crate) enum DefKind {
TypeAlias,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct Macro(RawId);
impl_arena_id!(Macro);
#[derive(PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct MacroData {
pub(crate) source_item_id: SourceFileItemId,
pub(crate) path: Path,
@ -191,18 +195,25 @@ impl RawItemsCollector {
Some(it) => it.as_name(),
None => return,
};
let source_item_id = self.source_file_items.id_of_unchecked(module.syntax());
if module.has_semi() {
let item = self.raw_items.modules.alloc(ModuleData::Declaration { name });
let item =
self.raw_items.modules.alloc(ModuleData::Declaration { name, source_item_id });
self.push_item(current_module, RawItem::Module(item));
return;
}
if let Some(item_list) = module.item_list() {
let item =
self.raw_items.modules.alloc(ModuleData::Definition { name, items: Vec::new() });
let item = self.raw_items.modules.alloc(ModuleData::Definition {
name,
source_item_id,
items: Vec::new(),
});
self.process_module(Some(item), item_list);
self.push_item(current_module, RawItem::Module(item));
return;
}
tested_by!(name_res_works_for_broken_modules);
}
fn add_use_item(&mut self, current_module: Option<Module>, use_item: &ast::UseItem) {

View file

@ -15,7 +15,7 @@ fn compute_crate_def_map(fixture: &str, graph: Option<CrateGraphFixture>) -> Arc
}
let crate_id = db.crate_graph().iter().next().unwrap();
let krate = Crate { crate_id };
collector::crate_def_map_query(&db, krate)
db.crate_def_map(krate)
}
fn render_crate_def_map(map: &CrateDefMap) -> String {

View file

@ -7,7 +7,7 @@ use crate::{
ItemMap,
PersistentHirDatabase,
mock::MockDatabase,
module_tree::ModuleId,
nameres::crate_def_map::ModuleId,
};
use super::Resolution;
@ -359,6 +359,7 @@ fn std_prelude() {
let main_id = db.file_id_of("/main.rs");
let module = crate::source_binder::module_from_file_id(&db, main_id).unwrap();
eprintln!("module = {:?}", module);
let krate = module.krate(&db).unwrap();
let item_map = db.item_map(krate);

View file

@ -80,8 +80,8 @@ fn module_from_source(
let source_root_id = db.file_source_root(file_id.as_original_file());
db.source_root_crates(source_root_id).iter().map(|&crate_id| Crate { crate_id }).find_map(
|krate| {
let module_tree = db.module_tree(krate);
let module_id = module_tree.find_module_by_source(file_id, decl_id)?;
let def_map = db.crate_def_map(krate);
let module_id = def_map.find_module_by_source(file_id, decl_id)?;
Some(Module { krate, module_id })
},
)

View file

@ -7,10 +7,12 @@ use std::sync::Arc;
use rustc_hash::FxHashMap;
use crate::{
HirDatabase, module_tree::ModuleId, Module, Crate, Name, Function, Trait,
HirDatabase, Module, Crate, Name, Function, Trait,
ids::TraitId,
impl_block::{ImplId, ImplBlock, ImplItem},
ty::{AdtDef, Ty},
nameres::crate_def_map::ModuleId,
};
/// This is used as a key for indexing impls.

View file

@ -1,7 +1,7 @@
use itertools::Itertools;
use ra_syntax::{
TextRange, SyntaxNode,
ast::{self, AstNode, NameOwner, ModuleItemOwner},
ast::{self, AstNode, NameOwner, ModuleItemOwner, AttrsOwner},
};
use ra_db::SourceDatabase;