2022-01-12 18:56:47 +00:00
|
|
|
//! File symbol extraction.
|
|
|
|
|
|
|
|
use hir_def::{
|
2023-05-02 09:56:48 +00:00
|
|
|
AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, MacroId, ModuleDefId, ModuleId, TraitId,
|
2022-01-12 18:56:47 +00:00
|
|
|
};
|
|
|
|
use hir_ty::db::HirDatabase;
|
2023-05-02 09:56:48 +00:00
|
|
|
use syntax::SmolStr;
|
2022-01-12 18:56:47 +00:00
|
|
|
|
2023-05-02 09:56:48 +00:00
|
|
|
use crate::{Module, ModuleDef};
|
2022-01-12 18:56:47 +00:00
|
|
|
|
|
|
|
/// The actual data that is stored in the index. It should be as compact as
|
|
|
|
/// possible.
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
pub struct FileSymbol {
|
2023-05-02 09:56:48 +00:00
|
|
|
// even though name can be derived from the def, we store it for efficiency
|
2022-01-12 18:56:47 +00:00
|
|
|
pub name: SmolStr,
|
2023-05-02 09:56:48 +00:00
|
|
|
pub def: ModuleDef,
|
2022-01-12 18:56:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Represents an outstanding module that the symbol collector must collect symbols from.
|
|
|
|
struct SymbolCollectorWork {
|
|
|
|
module_id: ModuleId,
|
|
|
|
parent: Option<DefWithBodyId>,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct SymbolCollector<'a> {
|
|
|
|
db: &'a dyn HirDatabase,
|
|
|
|
symbols: Vec<FileSymbol>,
|
|
|
|
work: Vec<SymbolCollectorWork>,
|
|
|
|
current_container_name: Option<SmolStr>,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Given a [`ModuleId`] and a [`HirDatabase`], use the DefMap for the module's crate to collect
|
|
|
|
/// all symbols that should be indexed for the given module.
|
|
|
|
impl<'a> SymbolCollector<'a> {
|
2023-04-18 13:57:49 +00:00
|
|
|
pub fn new(db: &'a dyn HirDatabase) -> Self {
|
|
|
|
SymbolCollector {
|
2022-01-12 18:56:47 +00:00
|
|
|
db,
|
|
|
|
symbols: Default::default(),
|
2023-04-18 13:57:49 +00:00
|
|
|
work: Default::default(),
|
2022-01-12 18:56:47 +00:00
|
|
|
current_container_name: None,
|
2023-04-18 13:57:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn collect(&mut self, module: Module) {
|
|
|
|
// The initial work is the root module we're collecting, additional work will
|
|
|
|
// be populated as we traverse the module's definitions.
|
|
|
|
self.work.push(SymbolCollectorWork { module_id: module.into(), parent: None });
|
2022-01-12 18:56:47 +00:00
|
|
|
|
2023-04-18 13:57:49 +00:00
|
|
|
while let Some(work) = self.work.pop() {
|
|
|
|
self.do_work(work);
|
2022-01-12 18:56:47 +00:00
|
|
|
}
|
2023-04-18 13:57:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn finish(self) -> Vec<FileSymbol> {
|
|
|
|
self.symbols
|
|
|
|
}
|
2022-01-12 18:56:47 +00:00
|
|
|
|
2023-04-18 13:57:49 +00:00
|
|
|
pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Vec<FileSymbol> {
|
|
|
|
let mut symbol_collector = SymbolCollector::new(db);
|
|
|
|
symbol_collector.collect(module);
|
|
|
|
symbol_collector.finish()
|
2022-01-12 18:56:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn do_work(&mut self, work: SymbolCollectorWork) {
|
|
|
|
self.db.unwind_if_cancelled();
|
|
|
|
|
|
|
|
let parent_name = work.parent.and_then(|id| self.def_with_body_id_name(id));
|
|
|
|
self.with_container_name(parent_name, |s| s.collect_from_module(work.module_id));
|
|
|
|
}
|
|
|
|
|
|
|
|
fn collect_from_module(&mut self, module_id: ModuleId) {
|
|
|
|
let def_map = module_id.def_map(self.db.upcast());
|
|
|
|
let scope = &def_map[module_id.local_id].scope;
|
|
|
|
|
|
|
|
for module_def_id in scope.declarations() {
|
|
|
|
match module_def_id {
|
|
|
|
ModuleDefId::ModuleId(id) => self.push_module(id),
|
|
|
|
ModuleDefId::FunctionId(id) => {
|
2023-05-02 09:56:48 +00:00
|
|
|
self.push_decl(id);
|
2022-01-12 18:56:47 +00:00
|
|
|
self.collect_from_body(id);
|
|
|
|
}
|
2023-05-02 09:56:48 +00:00
|
|
|
ModuleDefId::AdtId(AdtId::StructId(id)) => self.push_decl(id),
|
|
|
|
ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id),
|
|
|
|
ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id),
|
2022-01-12 18:56:47 +00:00
|
|
|
ModuleDefId::ConstId(id) => {
|
2023-05-02 09:56:48 +00:00
|
|
|
self.push_decl(id);
|
2022-01-12 18:56:47 +00:00
|
|
|
self.collect_from_body(id);
|
|
|
|
}
|
|
|
|
ModuleDefId::StaticId(id) => {
|
2023-05-02 09:56:48 +00:00
|
|
|
self.push_decl(id);
|
2022-01-12 18:56:47 +00:00
|
|
|
self.collect_from_body(id);
|
|
|
|
}
|
|
|
|
ModuleDefId::TraitId(id) => {
|
2023-05-02 09:56:48 +00:00
|
|
|
self.push_decl(id);
|
2022-01-12 18:56:47 +00:00
|
|
|
self.collect_from_trait(id);
|
|
|
|
}
|
2023-03-03 15:24:07 +00:00
|
|
|
ModuleDefId::TraitAliasId(id) => {
|
2023-05-02 09:56:48 +00:00
|
|
|
self.push_decl(id);
|
2023-03-03 15:24:07 +00:00
|
|
|
}
|
2022-01-12 18:56:47 +00:00
|
|
|
ModuleDefId::TypeAliasId(id) => {
|
2023-05-02 09:56:48 +00:00
|
|
|
self.push_decl(id);
|
2022-01-12 18:56:47 +00:00
|
|
|
}
|
2022-03-08 22:51:48 +00:00
|
|
|
ModuleDefId::MacroId(id) => match id {
|
2023-05-02 09:56:48 +00:00
|
|
|
MacroId::Macro2Id(id) => self.push_decl(id),
|
|
|
|
MacroId::MacroRulesId(id) => self.push_decl(id),
|
|
|
|
MacroId::ProcMacroId(id) => self.push_decl(id),
|
2022-03-08 22:51:48 +00:00
|
|
|
},
|
2022-01-12 18:56:47 +00:00
|
|
|
// Don't index these.
|
|
|
|
ModuleDefId::BuiltinType(_) => {}
|
|
|
|
ModuleDefId::EnumVariantId(_) => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for impl_id in scope.impls() {
|
|
|
|
self.collect_from_impl(impl_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
for const_id in scope.unnamed_consts() {
|
|
|
|
self.collect_from_body(const_id);
|
|
|
|
}
|
2022-03-09 00:44:20 +00:00
|
|
|
|
|
|
|
for (_, id) in scope.legacy_macros() {
|
2022-07-05 09:28:47 +00:00
|
|
|
for &id in id {
|
|
|
|
if id.module(self.db.upcast()) == module_id {
|
|
|
|
match id {
|
2023-05-02 09:56:48 +00:00
|
|
|
MacroId::Macro2Id(id) => self.push_decl(id),
|
|
|
|
MacroId::MacroRulesId(id) => self.push_decl(id),
|
|
|
|
MacroId::ProcMacroId(id) => self.push_decl(id),
|
2022-07-05 09:28:47 +00:00
|
|
|
}
|
2022-03-23 13:30:04 +00:00
|
|
|
}
|
2022-03-09 00:44:20 +00:00
|
|
|
}
|
|
|
|
}
|
2022-01-12 18:56:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn collect_from_body(&mut self, body_id: impl Into<DefWithBodyId>) {
|
|
|
|
let body_id = body_id.into();
|
|
|
|
let body = self.db.body(body_id);
|
|
|
|
|
|
|
|
// Descend into the blocks and enqueue collection of all modules within.
|
|
|
|
for (_, def_map) in body.blocks(self.db.upcast()) {
|
|
|
|
for (id, _) in def_map.modules() {
|
|
|
|
self.work.push(SymbolCollectorWork {
|
|
|
|
module_id: def_map.module_id(id),
|
|
|
|
parent: Some(body_id),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn collect_from_impl(&mut self, impl_id: ImplId) {
|
|
|
|
let impl_data = self.db.impl_data(impl_id);
|
|
|
|
for &assoc_item_id in &impl_data.items {
|
|
|
|
self.push_assoc_item(assoc_item_id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn collect_from_trait(&mut self, trait_id: TraitId) {
|
|
|
|
let trait_data = self.db.trait_data(trait_id);
|
|
|
|
self.with_container_name(trait_data.name.as_text(), |s| {
|
|
|
|
for &(_, assoc_item_id) in &trait_data.items {
|
|
|
|
s.push_assoc_item(assoc_item_id);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn with_container_name(&mut self, container_name: Option<SmolStr>, f: impl FnOnce(&mut Self)) {
|
|
|
|
if let Some(container_name) = container_name {
|
|
|
|
let prev = self.current_container_name.replace(container_name);
|
|
|
|
f(self);
|
|
|
|
self.current_container_name = prev;
|
|
|
|
} else {
|
|
|
|
f(self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn def_with_body_id_name(&self, body_id: DefWithBodyId) -> Option<SmolStr> {
|
|
|
|
match body_id {
|
2023-05-02 09:56:48 +00:00
|
|
|
DefWithBodyId::FunctionId(id) => Some(self.db.function_data(id).name.to_smol_str()),
|
|
|
|
DefWithBodyId::StaticId(id) => Some(self.db.static_data(id).name.to_smol_str()),
|
|
|
|
DefWithBodyId::ConstId(id) => Some(self.db.const_data(id).name.as_ref()?.to_smol_str()),
|
|
|
|
DefWithBodyId::VariantId(id) => {
|
|
|
|
Some(self.db.enum_data(id.parent).variants[id.local_id].name.to_smol_str())
|
|
|
|
}
|
2022-01-12 18:56:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn push_assoc_item(&mut self, assoc_item_id: AssocItemId) {
|
|
|
|
match assoc_item_id {
|
2023-05-02 09:56:48 +00:00
|
|
|
AssocItemId::FunctionId(id) => self.push_decl(id),
|
|
|
|
AssocItemId::ConstId(id) => self.push_decl(id),
|
|
|
|
AssocItemId::TypeAliasId(id) => self.push_decl(id),
|
2022-01-12 18:56:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-02 09:56:48 +00:00
|
|
|
fn push_decl(&mut self, id: impl Into<ModuleDefId>) {
|
|
|
|
let def = ModuleDef::from(id.into());
|
|
|
|
if let Some(name) = def.name(self.db) {
|
|
|
|
self.symbols.push(FileSymbol { name: name.to_smol_str(), def });
|
2022-01-12 18:56:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn push_module(&mut self, module_id: ModuleId) {
|
2023-05-02 09:56:48 +00:00
|
|
|
let def = Module::from(module_id);
|
|
|
|
if let Some(name) = def.name(self.db) {
|
|
|
|
self.symbols.push(FileSymbol { name: name.to_smol_str(), def: ModuleDef::Module(def) });
|
2022-01-12 18:56:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|