use crate::{
code_model::src::HasSource,
ids::{DefWithBodyId, FunctionId, ItemDefinitionId, Lookup, StructId, TypeAliasId},
item_scope::ItemScope,
DefDatabase, FileId, HirDatabase, InFile, ModuleId,
};
use mun_syntax::{ast, match_ast, AstNode, SyntaxNode};
use rustc_hash::FxHashMap;
pub(super) type SourceToDefCache = FxHashMap<SourceToDefContainer, SourceToDefMap>;
pub(super) struct SourceToDefContext<'a, 'db> {
pub(super) db: &'db dyn HirDatabase,
pub(super) cache: &'a mut SourceToDefCache,
}
impl SourceToDefContext<'_, '_> {
pub(super) fn find_container(
&mut self,
src: InFile<&SyntaxNode>,
) -> Option<SourceToDefContainer> {
for container in std::iter::successors(Some(src.cloned()), move |node| {
node.value.parent().map(|parent| node.with_value(parent))
})
.skip(1)
{
let res: SourceToDefContainer = match_ast! {
match (container.value) {
ast::FunctionDef(it) => {
let def = self.fn_to_def(container.with_value(it))?;
DefWithBodyId::from(def).into()
},
_ => continue,
}
};
return Some(res);
}
let def = self.file_to_def(src.file_id)?;
Some(def.into())
}
fn fn_to_def(&mut self, src: InFile<ast::FunctionDef>) -> Option<FunctionId> {
let container = self.find_container(src.as_ref().map(|it| it.syntax()))?;
let db = self.db;
let def_map = &*self
.cache
.entry(container)
.or_insert_with(|| container.source_to_def_map(db));
def_map.functions.get(&src).copied()
}
fn file_to_def(&self, file_id: FileId) -> Option<ModuleId> {
let source_root_id = self.db.file_source_root(file_id);
let packages = self.db.packages();
let result = packages
.iter()
.filter(|package_id| packages[*package_id].source_root == source_root_id)
.find_map(|package_id| {
let module_tree = self.db.module_tree(package_id);
let module_id = module_tree.module_for_file(file_id)?;
Some(ModuleId {
package: package_id,
local_id: module_id,
})
});
result
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub(crate) enum SourceToDefContainer {
DefWithBodyId(DefWithBodyId),
ModuleId(ModuleId),
}
impl From<DefWithBodyId> for SourceToDefContainer {
fn from(id: DefWithBodyId) -> Self {
SourceToDefContainer::DefWithBodyId(id)
}
}
impl From<ModuleId> for SourceToDefContainer {
fn from(id: ModuleId) -> Self {
SourceToDefContainer::ModuleId(id)
}
}
impl SourceToDefContainer {
fn source_to_def_map(self, db: &dyn HirDatabase) -> SourceToDefMap {
match self {
SourceToDefContainer::DefWithBodyId(id) => id.source_to_def_map(db),
SourceToDefContainer::ModuleId(id) => id.source_to_def_map(db),
}
}
}
trait SourceToDef {
fn source_to_def_map(&self, db: &dyn HirDatabase) -> SourceToDefMap;
}
impl SourceToDef for DefWithBodyId {
fn source_to_def_map(&self, _db: &dyn HirDatabase) -> SourceToDefMap {
SourceToDefMap::default()
}
}
impl SourceToDef for ModuleId {
fn source_to_def_map(&self, db: &dyn HirDatabase) -> SourceToDefMap {
let package_defs = db.package_defs(self.package);
let module_scope = &package_defs[self.local_id];
module_scope.source_to_def_map(db)
}
}
impl SourceToDef for ItemScope {
fn source_to_def_map(&self, db: &dyn HirDatabase) -> SourceToDefMap {
let mut result = SourceToDefMap::default();
self.declarations()
.for_each(|item| add_module_def(db.upcast(), &mut result, item));
return result;
fn add_module_def(db: &dyn DefDatabase, map: &mut SourceToDefMap, item: ItemDefinitionId) {
match item {
ItemDefinitionId::FunctionId(id) => {
let src = id.lookup(db).source(db);
map.functions.insert(src, id);
}
ItemDefinitionId::StructId(id) => {
let src = id.lookup(db).source(db);
map.structs.insert(src, id);
}
ItemDefinitionId::TypeAliasId(id) => {
let src = id.lookup(db).source(db);
map.type_aliases.insert(src, id);
}
_ => {}
}
}
}
}
#[derive(Default)]
pub(crate) struct SourceToDefMap {
functions: FxHashMap<InFile<ast::FunctionDef>, FunctionId>,
structs: FxHashMap<InFile<ast::StructDef>, StructId>,
type_aliases: FxHashMap<InFile<ast::TypeAliasDef>, TypeAliasId>,
}