diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 95696936b8..22789e0897 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -28,6 +28,7 @@ mod has_source; pub mod diagnostics; pub mod db; +pub mod symbols; mod display; @@ -40,12 +41,16 @@ use hir_def::{ adt::{ReprKind, VariantData}, body::{BodyDiagnostic, SyntheticSyntax}, expr::{BindingAnnotation, LabelId, Pat, PatId}, + item_tree::ItemTreeNode, lang_item::LangItemTarget, nameres::{self, diagnostics::DefDiagnostic}, per_ns::PerNs, resolver::{HasResolver, Resolver}, - AttrDefId, ConstId, ConstParamId, EnumId, FunctionId, GenericDefId, HasModule, LifetimeParamId, - LocalEnumVariantId, LocalFieldId, StaticId, StructId, TypeAliasId, TypeParamId, UnionId, + src::HasSource as _, + AdtId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, + FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LifetimeParamId, + LocalEnumVariantId, LocalFieldId, Lookup, StaticId, StructId, TraitId, TypeAliasId, + TypeParamId, UnionId, }; use hir_expand::{name::name, MacroCallKind, MacroDefKind}; use hir_ty::{ @@ -106,24 +111,11 @@ pub use { builtin_attr::AttributeTemplate, find_path::PrefixKind, import_map, - item_scope::ItemScope, - item_tree::ItemTreeNode, - nameres::{DefMap, ModuleData, ModuleOrigin, ModuleSource}, + nameres::ModuleSource, path::{ModPath, PathKind}, - src::HasSource as DefHasSource, // xx: I don't like this shadowing of HasSource... :( type_ref::{Mutability, TypeRef}, visibility::Visibility, - AdtId, - AssocItemId, - AssocItemLoc, - DefWithBodyId, - ImplId, - ItemContainerId, - ItemLoc, - Lookup, - ModuleDefId, ModuleId, - TraitId, }, hir_expand::{ name::{known, Name}, diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs new file mode 100644 index 0000000000..b9d2f0cdca --- /dev/null +++ b/crates/hir/src/symbols.rs @@ -0,0 +1,359 @@ +//! File symbol extraction. + +use base_db::FileRange; +use either::Either; +use hir_def::{ + item_tree::ItemTreeNode, src::HasSource, AdtId, AssocItemId, AssocItemLoc, DefWithBodyId, + ImplId, ItemContainerId, ItemLoc, Lookup, ModuleDefId, ModuleId, TraitId, +}; +use hir_expand::{HirFileId, InFile}; +use hir_ty::db::HirDatabase; +use syntax::{ast::HasName, AstNode, SmolStr, SyntaxNode, SyntaxNodePtr}; + +use crate::{HasSource as _, MacroDef, Module, Semantics}; + +/// 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 { + pub name: SmolStr, + pub loc: DeclarationLocation, + pub kind: FileSymbolKind, + pub container_name: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct DeclarationLocation { + /// The file id for both the `ptr` and `name_ptr`. + pub hir_file_id: HirFileId, + /// This points to the whole syntax node of the declaration. + pub ptr: SyntaxNodePtr, + /// This points to the [`syntax::ast::Name`] identifier of the declaration. + pub name_ptr: SyntaxNodePtr, +} + +impl DeclarationLocation { + pub fn syntax(&self, sema: &Semantics) -> Option { + let root = sema.parse_or_expand(self.hir_file_id)?; + Some(self.ptr.to_node(&root)) + } + + pub fn original_range(&self, db: &dyn HirDatabase) -> Option { + find_original_file_range(db, self.hir_file_id, &self.ptr) + } + + pub fn original_name_range(&self, db: &dyn HirDatabase) -> Option { + find_original_file_range(db, self.hir_file_id, &self.name_ptr) + } +} + +fn find_original_file_range( + db: &dyn HirDatabase, + file_id: HirFileId, + ptr: &SyntaxNodePtr, +) -> Option { + let root = db.parse_or_expand(file_id)?; + let node = ptr.to_node(&root); + let node = InFile::new(file_id, &node); + + Some(node.original_file_range(db.upcast())) +} + +#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] +pub enum FileSymbolKind { + Const, + Enum, + Function, + Macro, + Module, + Static, + Struct, + Trait, + TypeAlias, + Union, +} + +impl FileSymbolKind { + pub fn is_type(self: FileSymbolKind) -> bool { + matches!( + self, + FileSymbolKind::Struct + | FileSymbolKind::Enum + | FileSymbolKind::Trait + | FileSymbolKind::TypeAlias + | FileSymbolKind::Union + ) + } +} + +/// Represents an outstanding module that the symbol collector must collect symbols from. +struct SymbolCollectorWork { + module_id: ModuleId, + parent: Option, +} + +pub struct SymbolCollector<'a> { + db: &'a dyn HirDatabase, + symbols: Vec, + work: Vec, + current_container_name: Option, +} + +/// 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> { + pub fn collect(db: &dyn HirDatabase, module_id: ModuleId) -> Vec { + let mut symbol_collector = SymbolCollector { + db, + symbols: Default::default(), + current_container_name: None, + // The initial work is the root module we're collecting, additional work will + // be populated as we traverse the module's definitions. + work: vec![SymbolCollectorWork { module_id, parent: None }], + }; + + while let Some(work) = symbol_collector.work.pop() { + symbol_collector.do_work(work); + } + + symbol_collector.symbols + } + + 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) => { + self.push_decl_assoc(id, FileSymbolKind::Function); + self.collect_from_body(id); + } + ModuleDefId::AdtId(AdtId::StructId(id)) => { + self.push_decl(id, FileSymbolKind::Struct) + } + ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id, FileSymbolKind::Enum), + ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id, FileSymbolKind::Union), + ModuleDefId::ConstId(id) => { + self.push_decl_assoc(id, FileSymbolKind::Const); + self.collect_from_body(id); + } + ModuleDefId::StaticId(id) => { + self.push_decl_assoc(id, FileSymbolKind::Static); + self.collect_from_body(id); + } + ModuleDefId::TraitId(id) => { + self.push_decl(id, FileSymbolKind::Trait); + self.collect_from_trait(id); + } + ModuleDefId::TypeAliasId(id) => { + self.push_decl_assoc(id, FileSymbolKind::TypeAlias); + } + // 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); + } + + for macro_def_id in scope.macro_declarations() { + self.push_decl_macro(macro_def_id.into()); + } + } + + fn collect_from_body(&mut self, body_id: impl Into) { + 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, 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 current_container_name(&self) -> Option { + self.current_container_name.clone() + } + + fn def_with_body_id_name(&self, body_id: DefWithBodyId) -> Option { + match body_id { + DefWithBodyId::FunctionId(id) => Some( + id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(), + ), + DefWithBodyId::StaticId(id) => Some( + id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(), + ), + DefWithBodyId::ConstId(id) => Some( + id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(), + ), + } + } + + fn push_assoc_item(&mut self, assoc_item_id: AssocItemId) { + match assoc_item_id { + AssocItemId::FunctionId(id) => self.push_decl_assoc(id, FileSymbolKind::Function), + AssocItemId::ConstId(id) => self.push_decl_assoc(id, FileSymbolKind::Const), + AssocItemId::TypeAliasId(id) => self.push_decl_assoc(id, FileSymbolKind::TypeAlias), + } + } + + fn push_decl_assoc(&mut self, id: L, kind: FileSymbolKind) + where + L: Lookup>, + T: ItemTreeNode, + ::Source: HasName, + { + fn container_name(db: &dyn HirDatabase, container: ItemContainerId) -> Option { + match container { + ItemContainerId::ModuleId(module_id) => { + let module = Module::from(module_id); + module.name(db).and_then(|name| name.as_text()) + } + ItemContainerId::TraitId(trait_id) => { + let trait_data = db.trait_data(trait_id); + trait_data.name.as_text() + } + ItemContainerId::ImplId(_) | ItemContainerId::ExternBlockId(_) => None, + } + } + + self.push_file_symbol(|s| { + let loc = id.lookup(s.db.upcast()); + let source = loc.source(s.db.upcast()); + let name_node = source.value.name()?; + let container_name = + container_name(s.db, loc.container).or_else(|| s.current_container_name()); + + Some(FileSymbol { + name: name_node.text().into(), + kind, + container_name, + loc: DeclarationLocation { + hir_file_id: source.file_id, + ptr: SyntaxNodePtr::new(source.value.syntax()), + name_ptr: SyntaxNodePtr::new(name_node.syntax()), + }, + }) + }) + } + + fn push_decl(&mut self, id: L, kind: FileSymbolKind) + where + L: Lookup>, + T: ItemTreeNode, + ::Source: HasName, + { + self.push_file_symbol(|s| { + let loc = id.lookup(s.db.upcast()); + let source = loc.source(s.db.upcast()); + let name_node = source.value.name()?; + + Some(FileSymbol { + name: name_node.text().into(), + kind, + container_name: s.current_container_name(), + loc: DeclarationLocation { + hir_file_id: source.file_id, + ptr: SyntaxNodePtr::new(source.value.syntax()), + name_ptr: SyntaxNodePtr::new(name_node.syntax()), + }, + }) + }) + } + + fn push_module(&mut self, module_id: ModuleId) { + self.push_file_symbol(|s| { + let def_map = module_id.def_map(s.db.upcast()); + let module_data = &def_map[module_id.local_id]; + let declaration = module_data.origin.declaration()?; + let module = declaration.to_node(s.db.upcast()); + let name_node = module.name()?; + + Some(FileSymbol { + name: name_node.text().into(), + kind: FileSymbolKind::Module, + container_name: s.current_container_name(), + loc: DeclarationLocation { + hir_file_id: declaration.file_id, + ptr: SyntaxNodePtr::new(module.syntax()), + name_ptr: SyntaxNodePtr::new(name_node.syntax()), + }, + }) + }) + } + + fn push_decl_macro(&mut self, macro_def: MacroDef) { + self.push_file_symbol(|s| { + let name = macro_def.name(s.db)?.as_text()?; + let source = macro_def.source(s.db)?; + + let (ptr, name_ptr) = match source.value { + Either::Left(m) => { + (SyntaxNodePtr::new(m.syntax()), SyntaxNodePtr::new(m.name()?.syntax())) + } + Either::Right(f) => { + (SyntaxNodePtr::new(f.syntax()), SyntaxNodePtr::new(f.name()?.syntax())) + } + }; + + Some(FileSymbol { + name, + kind: FileSymbolKind::Macro, + container_name: s.current_container_name(), + loc: DeclarationLocation { hir_file_id: source.file_id, name_ptr, ptr }, + }) + }) + } + + fn push_file_symbol(&mut self, f: impl FnOnce(&Self) -> Option) { + if let Some(file_symbol) = f(self) { + self.symbols.push(file_symbol); + } + } +} diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index b8482d0222..db7b80f71b 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -66,8 +66,7 @@ use ide_db::{ salsa::{self, ParallelDatabase}, Env, FileLoader, FileSet, SourceDatabase, VfsPath, }, - symbol_index::{self, FileSymbol}, - LineIndexDatabase, + symbol_index, LineIndexDatabase, }; use syntax::SourceFile; diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 52e099513a..1c3ff06247 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -4,8 +4,8 @@ use std::fmt; use either::Either; use hir::{ - AssocItem, Documentation, FieldSource, HasAttrs, HasSource, HirDisplay, InFile, ModuleSource, - Semantics, + symbols::FileSymbol, AssocItem, Documentation, FieldSource, HasAttrs, HasSource, HirDisplay, + InFile, ModuleSource, Semantics, }; use ide_db::{ base_db::{FileId, FileRange}, @@ -17,8 +17,6 @@ use syntax::{ match_ast, AstNode, SmolStr, TextRange, }; -use crate::FileSymbol; - /// `NavigationTarget` represents an element in the editor's UI which you can /// click on to navigate to a particular piece of code. /// diff --git a/crates/ide_db/src/items_locator.rs b/crates/ide_db/src/items_locator.rs index ca8266b6d2..db38a48783 100644 --- a/crates/ide_db/src/items_locator.rs +++ b/crates/ide_db/src/items_locator.rs @@ -5,6 +5,7 @@ use either::Either; use hir::{ import_map::{self, ImportKind}, + symbols::FileSymbol, AsAssocItem, Crate, ItemInNs, Semantics, }; use limit::Limit; @@ -13,8 +14,7 @@ use syntax::{ast, AstNode, SyntaxKind::NAME}; use crate::{ defs::{Definition, NameClass}, helpers::import_assets::NameToImport, - symbol_index::{self, FileSymbol}, - RootDatabase, + symbol_index, RootDatabase, }; /// A value to use, when uncertain which limit to pick. diff --git a/crates/ide_db/src/lib.rs b/crates/ide_db/src/lib.rs index bcbb09e31b..602eaf0de5 100644 --- a/crates/ide_db/src/lib.rs +++ b/crates/ide_db/src/lib.rs @@ -26,7 +26,10 @@ use base_db::{ salsa::{self, Durability}, AnchoredPath, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast, }; -use hir::db::{AstDatabase, DefDatabase, HirDatabase}; +use hir::{ + db::{AstDatabase, DefDatabase, HirDatabase}, + symbols::FileSymbolKind, +}; use rustc_hash::FxHashSet; use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase}; @@ -183,6 +186,23 @@ impl From for SymbolKind { } } +impl From for SymbolKind { + fn from(it: FileSymbolKind) -> Self { + match it { + FileSymbolKind::Const => SymbolKind::Const, + FileSymbolKind::Enum => SymbolKind::Enum, + FileSymbolKind::Function => SymbolKind::Function, + FileSymbolKind::Macro => SymbolKind::Macro, + FileSymbolKind::Module => SymbolKind::Module, + FileSymbolKind::Static => SymbolKind::Static, + FileSymbolKind::Struct => SymbolKind::Struct, + FileSymbolKind::Trait => SymbolKind::Trait, + FileSymbolKind::TypeAlias => SymbolKind::TypeAlias, + FileSymbolKind::Union => SymbolKind::Union, + } + } +} + #[cfg(test)] mod tests { mod sourcegen_lints; diff --git a/crates/ide_db/src/symbol_index.rs b/crates/ide_db/src/symbol_index.rs index 62f4a81910..e82230b2f3 100644 --- a/crates/ide_db/src/symbol_index.rs +++ b/crates/ide_db/src/symbol_index.rs @@ -30,21 +30,18 @@ use std::{ use base_db::{ salsa::{self, ParallelDatabase}, - CrateId, FileRange, SourceDatabaseExt, SourceRootId, Upcast, + CrateId, SourceDatabaseExt, SourceRootId, Upcast, }; -use either::Either; use fst::{self, Streamer}; use hir::{ db::{DefDatabase, HirDatabase}, - AdtId, AssocItemId, AssocItemLoc, DefHasSource, DefWithBodyId, HasSource, HirFileId, ImplId, - InFile, ItemContainerId, ItemLoc, ItemTreeNode, Lookup, MacroDef, Module, ModuleDefId, - ModuleId, Semantics, TraitId, + symbols::{FileSymbol, SymbolCollector}, + ModuleId, }; use rayon::prelude::*; use rustc_hash::FxHashSet; -use syntax::{ast::HasName, AstNode, SmolStr, SyntaxNode, SyntaxNodePtr}; -use crate::{RootDatabase, SymbolKind}; +use crate::RootDatabase; #[derive(Debug)] pub struct Query { @@ -123,7 +120,7 @@ fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Ar // we specifically avoid calling SymbolsDatabase::module_symbols here, even they do the same thing, // as the index for a library is not going to really ever change, and we do not want to store each // module's index in salsa. - .map(|module_id| SymbolCollector::collect(db, module_id)) + .map(|module_id| SymbolCollector::collect(db.upcast(), module_id)) .flatten() .collect(); @@ -132,7 +129,7 @@ fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Ar fn module_symbols(db: &dyn SymbolsDatabase, module_id: ModuleId) -> Arc { let _p = profile::span("module_symbols"); - let symbols = SymbolCollector::collect(db, module_id); + let symbols = SymbolCollector::collect(db.upcast(), module_id); Arc::new(SymbolIndex::new(symbols)) } @@ -356,374 +353,12 @@ impl Query { } } -/// 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 { - pub name: SmolStr, - pub loc: DeclarationLocation, - pub kind: FileSymbolKind, - pub container_name: Option, -} - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct DeclarationLocation { - /// The file id for both the `ptr` and `name_ptr`. - pub hir_file_id: HirFileId, - /// This points to the whole syntax node of the declaration. - pub ptr: SyntaxNodePtr, - /// This points to the [`syntax::ast::Name`] identifier of the declaration. - pub name_ptr: SyntaxNodePtr, -} - -impl DeclarationLocation { - pub fn syntax(&self, semantics: &Semantics<'_, RootDatabase>) -> Option { - let root = semantics.parse_or_expand(self.hir_file_id)?; - Some(self.ptr.to_node(&root)) - } - - pub fn original_range(&self, db: &dyn HirDatabase) -> Option { - find_original_file_range(db, self.hir_file_id, &self.ptr) - } - - pub fn original_name_range(&self, db: &dyn HirDatabase) -> Option { - find_original_file_range(db, self.hir_file_id, &self.name_ptr) - } -} - -fn find_original_file_range( - db: &dyn HirDatabase, - file_id: HirFileId, - ptr: &SyntaxNodePtr, -) -> Option { - let root = db.parse_or_expand(file_id)?; - let node = ptr.to_node(&root); - let node = InFile::new(file_id, &node); - - Some(node.original_file_range(db.upcast())) -} - -#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)] -pub enum FileSymbolKind { - Const, - Enum, - Function, - Macro, - Module, - Static, - Struct, - Trait, - TypeAlias, - Union, -} - -impl FileSymbolKind { - fn is_type(self: FileSymbolKind) -> bool { - matches!( - self, - FileSymbolKind::Struct - | FileSymbolKind::Enum - | FileSymbolKind::Trait - | FileSymbolKind::TypeAlias - | FileSymbolKind::Union - ) - } -} - -impl From for SymbolKind { - fn from(it: FileSymbolKind) -> Self { - match it { - FileSymbolKind::Const => SymbolKind::Const, - FileSymbolKind::Enum => SymbolKind::Enum, - FileSymbolKind::Function => SymbolKind::Function, - FileSymbolKind::Macro => SymbolKind::Macro, - FileSymbolKind::Module => SymbolKind::Module, - FileSymbolKind::Static => SymbolKind::Static, - FileSymbolKind::Struct => SymbolKind::Struct, - FileSymbolKind::Trait => SymbolKind::Trait, - FileSymbolKind::TypeAlias => SymbolKind::TypeAlias, - FileSymbolKind::Union => SymbolKind::Union, - } - } -} - -/// Represents an outstanding module that the symbol collector must collect symbols from. -struct SymbolCollectorWork { - module_id: ModuleId, - parent: Option, -} - -struct SymbolCollector<'a> { - db: &'a dyn SymbolsDatabase, - symbols: Vec, - work: Vec, - current_container_name: Option, -} - -/// Given a [`ModuleId`] and a [`SymbolsDatabase`], use the DefMap for the module's crate to collect all symbols that should be -/// indexed for the given module. -impl<'a> SymbolCollector<'a> { - fn collect(db: &dyn SymbolsDatabase, module_id: ModuleId) -> Vec { - let mut symbol_collector = SymbolCollector { - db, - symbols: Default::default(), - current_container_name: None, - // The initial work is the root module we're collecting, additional work will - // be populated as we traverse the module's definitions. - work: vec![SymbolCollectorWork { module_id, parent: None }], - }; - - while let Some(work) = symbol_collector.work.pop() { - symbol_collector.do_work(work); - } - - symbol_collector.symbols - } - - 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) => { - self.push_decl_assoc(id, FileSymbolKind::Function); - self.collect_from_body(id); - } - ModuleDefId::AdtId(AdtId::StructId(id)) => { - self.push_decl(id, FileSymbolKind::Struct) - } - ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id, FileSymbolKind::Enum), - ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id, FileSymbolKind::Union), - ModuleDefId::ConstId(id) => { - self.push_decl_assoc(id, FileSymbolKind::Const); - self.collect_from_body(id); - } - ModuleDefId::StaticId(id) => { - self.push_decl_assoc(id, FileSymbolKind::Static); - self.collect_from_body(id); - } - ModuleDefId::TraitId(id) => { - self.push_decl(id, FileSymbolKind::Trait); - self.collect_from_trait(id); - } - ModuleDefId::TypeAliasId(id) => { - self.push_decl_assoc(id, FileSymbolKind::TypeAlias); - } - // 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); - } - - for macro_def_id in scope.macro_declarations() { - self.push_decl_macro(macro_def_id.into()); - } - } - - fn collect_from_body(&mut self, body_id: impl Into) { - 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, 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 current_container_name(&self) -> Option { - self.current_container_name.clone() - } - - fn def_with_body_id_name(&self, body_id: DefWithBodyId) -> Option { - match body_id { - DefWithBodyId::FunctionId(id) => Some( - id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(), - ), - DefWithBodyId::StaticId(id) => Some( - id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(), - ), - DefWithBodyId::ConstId(id) => Some( - id.lookup(self.db.upcast()).source(self.db.upcast()).value.name()?.text().into(), - ), - } - } - - fn push_assoc_item(&mut self, assoc_item_id: AssocItemId) { - match assoc_item_id { - AssocItemId::FunctionId(id) => self.push_decl_assoc(id, FileSymbolKind::Function), - AssocItemId::ConstId(id) => self.push_decl_assoc(id, FileSymbolKind::Const), - AssocItemId::TypeAliasId(id) => self.push_decl_assoc(id, FileSymbolKind::TypeAlias), - } - } - - fn push_decl_assoc(&mut self, id: L, kind: FileSymbolKind) - where - L: Lookup>, - T: ItemTreeNode, - ::Source: HasName, - { - fn container_name(db: &dyn HirDatabase, container: ItemContainerId) -> Option { - match container { - ItemContainerId::ModuleId(module_id) => { - let module = Module::from(module_id); - module.name(db).and_then(|name| name.as_text()) - } - ItemContainerId::TraitId(trait_id) => { - let trait_data = db.trait_data(trait_id); - trait_data.name.as_text() - } - ItemContainerId::ImplId(_) | ItemContainerId::ExternBlockId(_) => None, - } - } - - self.push_file_symbol(|s| { - let loc = id.lookup(s.db.upcast()); - let source = loc.source(s.db.upcast()); - let name_node = source.value.name()?; - let container_name = - container_name(s.db.upcast(), loc.container).or_else(|| s.current_container_name()); - - Some(FileSymbol { - name: name_node.text().into(), - kind, - container_name, - loc: DeclarationLocation { - hir_file_id: source.file_id, - ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr: SyntaxNodePtr::new(name_node.syntax()), - }, - }) - }) - } - - fn push_decl(&mut self, id: L, kind: FileSymbolKind) - where - L: Lookup>, - T: ItemTreeNode, - ::Source: HasName, - { - self.push_file_symbol(|s| { - let loc = id.lookup(s.db.upcast()); - let source = loc.source(s.db.upcast()); - let name_node = source.value.name()?; - - Some(FileSymbol { - name: name_node.text().into(), - kind, - container_name: s.current_container_name(), - loc: DeclarationLocation { - hir_file_id: source.file_id, - ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr: SyntaxNodePtr::new(name_node.syntax()), - }, - }) - }) - } - - fn push_module(&mut self, module_id: ModuleId) { - self.push_file_symbol(|s| { - let def_map = module_id.def_map(s.db.upcast()); - let module_data = &def_map[module_id.local_id]; - let declaration = module_data.origin.declaration()?; - let module = declaration.to_node(s.db.upcast()); - let name_node = module.name()?; - - Some(FileSymbol { - name: name_node.text().into(), - kind: FileSymbolKind::Module, - container_name: s.current_container_name(), - loc: DeclarationLocation { - hir_file_id: declaration.file_id, - ptr: SyntaxNodePtr::new(module.syntax()), - name_ptr: SyntaxNodePtr::new(name_node.syntax()), - }, - }) - }) - } - - fn push_decl_macro(&mut self, macro_def: MacroDef) { - self.push_file_symbol(|s| { - let name = macro_def.name(s.db.upcast())?.as_text()?; - let source = macro_def.source(s.db.upcast())?; - - let (ptr, name_ptr) = match source.value { - Either::Left(m) => { - (SyntaxNodePtr::new(m.syntax()), SyntaxNodePtr::new(m.name()?.syntax())) - } - Either::Right(f) => { - (SyntaxNodePtr::new(f.syntax()), SyntaxNodePtr::new(f.name()?.syntax())) - } - }; - - Some(FileSymbol { - name, - kind: FileSymbolKind::Macro, - container_name: s.current_container_name(), - loc: DeclarationLocation { hir_file_id: source.file_id, name_ptr, ptr }, - }) - }) - } - - fn push_file_symbol(&mut self, f: impl FnOnce(&Self) -> Option) { - if let Some(file_symbol) = f(self) { - self.symbols.push(file_symbol); - } - } -} - #[cfg(test)] mod tests { use base_db::fixture::WithFixture; use expect_test::expect_file; + use hir::symbols::SymbolCollector; use super::*; @@ -794,9 +429,7 @@ struct StructInModB; let symbols: Vec<_> = module_ids_for_crate(db.upcast(), db.test_crate()) .into_iter() - .map(|module_id| { - (module_id, SymbolCollector::collect(&db as &dyn SymbolsDatabase, module_id)) - }) + .map(|module_id| (module_id, SymbolCollector::collect(&db, module_id))) .collect(); expect_file!["./test_data/test_symbol_index_collection.txt"].assert_debug_eq(&symbols);