diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index 9baae92144..5326a5a440 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs @@ -19,8 +19,8 @@ use crate::{ pub const WORKSPACE: SourceRootId = SourceRootId(0); pub trait WithFixture: Default + SourceDatabaseExt + 'static { - fn with_single_file(text: &str) -> (Self, FileId) { - let fixture = ChangeFixture::parse(text); + fn with_single_file(ra_fixture: &str) -> (Self, FileId) { + let fixture = ChangeFixture::parse(ra_fixture); let mut db = Self::default(); fixture.change.apply(&mut db); assert_eq!(fixture.files.len(), 1); diff --git a/crates/base_db/src/lib.rs b/crates/base_db/src/lib.rs index d4070457cd..d80660f7c9 100644 --- a/crates/base_db/src/lib.rs +++ b/crates/base_db/src/lib.rs @@ -102,7 +102,7 @@ fn source_root_crates(db: &dyn SourceDatabaseExt, id: SourceRootId) -> Arc>(); + .collect(); Arc::new(res) } diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index badd9ac589..e0dc921c9f 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -40,18 +40,14 @@ use hir_def::{ adt::{ReprKind, VariantData}, body::{BodyDiagnostic, SyntheticSyntax}, expr::{BindingAnnotation, LabelId, Pat, PatId}, - item_tree::ItemTreeNode, lang_item::LangItemTarget, nameres, per_ns::PerNs, resolver::{HasResolver, Resolver}, - src::HasSource as _, - AdtId, AssocContainerId, AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, - DefWithBodyId, EnumId, FunctionId, GenericDefId, HasModule, ImplId, LifetimeParamId, - LocalEnumVariantId, LocalFieldId, Lookup, ModuleId, StaticId, StructId, TraitId, TypeAliasId, - TypeParamId, UnionId, + AttrDefId, ConstId, ConstParamId, EnumId, FunctionId, GenericDefId, HasModule, LifetimeParamId, + LocalEnumVariantId, LocalFieldId, StaticId, StructId, TypeAliasId, TypeParamId, UnionId, }; -use hir_expand::{name::name, MacroCallKind, MacroDefId, MacroDefKind}; +use hir_expand::{name::name, MacroCallKind, MacroDefKind}; use hir_ty::{ autoderef, consteval::ConstExt, @@ -109,14 +105,28 @@ pub use { attr::{Attr, Attrs, AttrsWithOwner, Documentation}, find_path::PrefixKind, import_map, - nameres::ModuleSource, + item_scope::ItemScope, + item_tree::ItemTreeNode, + nameres::{DefMap, ModuleData, ModuleOrigin, ModuleSource}, path::{ModPath, PathKind}, + src::HasSource as DefHasSource, // xx: I don't like this shadowing of HasSource... :( type_ref::{Mutability, TypeRef}, visibility::Visibility, + AdtId, + AssocContainerId, + AssocItemId, + AssocItemLoc, + DefWithBodyId, + ImplId, + ItemLoc, + Lookup, + ModuleDefId, + ModuleId, + TraitId, }, hir_expand::{ name::{known, Name}, - ExpandResult, HirFileId, InFile, MacroFile, Origin, + ExpandResult, HirFileId, InFile, MacroDefId, MacroFile, Origin, }, hir_ty::display::HirDisplay, }; diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 281e6c65dc..27ba42ce1a 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -143,6 +143,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.parse(file_id) } + pub fn parse_or_expand(&self, file_id: HirFileId) -> Option { + self.imp.parse_or_expand(file_id) + } + pub fn expand(&self, macro_call: &ast::MacroCall) -> Option { self.imp.expand(macro_call) } @@ -416,6 +420,12 @@ impl<'db> SemanticsImpl<'db> { tree } + fn parse_or_expand(&self, file_id: HirFileId) -> Option { + let node = self.db.parse_or_expand(file_id)?; + self.cache(node.clone(), file_id); + Some(node) + } + fn expand(&self, macro_call: &ast::MacroCall) -> Option { let sa = self.analyze(macro_call.syntax()); let file_id = sa.expand(self.db, InFile::new(sa.file_id, macro_call))?; diff --git a/crates/hir_def/src/item_scope.rs b/crates/hir_def/src/item_scope.rs index 0a2a6719ed..37599371f6 100644 --- a/crates/hir_def/src/item_scope.rs +++ b/crates/hir_def/src/item_scope.rs @@ -44,6 +44,8 @@ pub struct ItemScope { /// The defs declared in this scope. Each def has a single scope where it is /// declared. declarations: Vec, + macro_declarations: Vec, + impls: Vec, unnamed_consts: Vec, /// Traits imported via `use Trait as _;`. @@ -101,6 +103,10 @@ impl ItemScope { self.declarations.iter().copied() } + pub fn macro_declarations(&self) -> impl Iterator + '_ { + self.macro_declarations.iter().copied() + } + pub fn impls(&self) -> impl Iterator + ExactSizeIterator + '_ { self.impls.iter().copied() } @@ -163,6 +169,10 @@ impl ItemScope { self.declarations.push(def) } + pub(crate) fn declare_macro(&mut self, def: MacroDefId) { + self.macro_declarations.push(def); + } + pub(crate) fn get_legacy_macro(&self, name: &Name) -> Option { self.legacy_macros.get(name).copied() } @@ -336,7 +346,8 @@ impl ItemScope { values, macros, unresolved, - declarations: defs, + declarations, + macro_declarations, impls, unnamed_consts, unnamed_trait_imports, @@ -348,7 +359,8 @@ impl ItemScope { values.shrink_to_fit(); macros.shrink_to_fit(); unresolved.shrink_to_fit(); - defs.shrink_to_fit(); + declarations.shrink_to_fit(); + macro_declarations.shrink_to_fit(); impls.shrink_to_fit(); unnamed_consts.shrink_to_fit(); unnamed_trait_imports.shrink_to_fit(); diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 882d54c996..cf670e5cc1 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs @@ -148,7 +148,7 @@ pub enum ModuleOrigin { } impl ModuleOrigin { - fn declaration(&self) -> Option> { + pub fn declaration(&self) -> Option> { match self { ModuleOrigin::File { declaration: module, .. } | ModuleOrigin::Inline { definition: module, .. } => Some(*module), diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index a0210bc503..d7a35caf29 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs @@ -594,6 +594,7 @@ impl DefCollector<'_> { ) { // Textual scoping self.define_legacy_macro(module_id, name.clone(), macro_); + self.def_map.modules[module_id].scope.declare_macro(macro_); // Module scoping // In Rust, `#[macro_export]` macros are unconditionally visible at the @@ -632,6 +633,7 @@ impl DefCollector<'_> { ) { let vis = self.def_map.resolve_visibility(self.db, module_id, vis).unwrap_or(Visibility::Public); + self.def_map.modules[module_id].scope.declare_macro(macro_); self.update(module_id, &[(Some(name), PerNs::macros(macro_, vis))], vis, ImportType::Named); } @@ -640,6 +642,7 @@ impl DefCollector<'_> { /// A proc macro is similar to normal macro scope, but it would not visible in legacy textual scoped. /// And unconditionally exported. fn define_proc_macro(&mut self, name: Name, macro_: MacroDefId) { + self.def_map.modules[self.def_map.root].scope.declare_macro(macro_); self.update( self.def_map.root, &[(Some(name), PerNs::macros(macro_, Visibility::Public))], diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 121baa86f1..e7feb042b2 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -367,8 +367,8 @@ impl Analysis { pub fn symbol_search(&self, query: Query) -> Cancellable> { self.with_db(|db| { symbol_index::world_symbols(db, query) - .into_iter() - .map(|s| s.to_nav(db)) + .into_iter() // xx: should we make this a par iter? + .filter_map(|s| s.try_to_nav(db)) .collect::>() }) } diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 3121cdd4a2..7deb6cae38 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -167,10 +167,13 @@ impl NavigationTarget { } } -impl ToNav for FileSymbol { - fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { - NavigationTarget { - file_id: self.file_id, +impl TryToNav for FileSymbol { + fn try_to_nav(&self, db: &RootDatabase) -> Option { + let full_range = self.loc.original_range(db)?; + let name_range = self.loc.original_name_range(db)?; + + Some(NavigationTarget { + file_id: full_range.file_id, name: self.name.clone(), kind: Some(match self.kind { FileSymbolKind::Function => SymbolKind::Function, @@ -184,12 +187,12 @@ impl ToNav for FileSymbol { FileSymbolKind::Macro => SymbolKind::Macro, FileSymbolKind::Union => SymbolKind::Union, }), - full_range: self.range, - focus_range: self.name_range, + full_range: full_range.range, + focus_range: Some(name_range.range), container_name: self.container_name.clone(), description: description_from_symbol(db, self), docs: None, - } + }) } } @@ -517,8 +520,7 @@ impl TryToNav for hir::ConstParam { /// e.g. `struct Name`, `enum Name`, `fn Name` pub(crate) fn description_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option { let sema = Semantics::new(db); - let parse = sema.parse(symbol.file_id); - let node = symbol.ptr.to_node(parse.syntax()); + let node = symbol.loc.syntax(&sema)?; match_ast! { match node { diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs index d11c2c35e4..9f589c1ea1 100644 --- a/crates/ide/src/status.rs +++ b/crates/ide/src/status.rs @@ -11,7 +11,6 @@ use ide_db::{ }; use itertools::Itertools; use profile::{memory_usage, Bytes}; -use rustc_hash::FxHashMap; use std::env; use stdx::format_to; use syntax::{ast, Parse, SyntaxNode}; @@ -149,20 +148,16 @@ impl fmt::Display for LibrarySymbolsStats { } } -impl FromIterator>>> - for LibrarySymbolsStats -{ +impl FromIterator>> for LibrarySymbolsStats { fn from_iter(iter: T) -> LibrarySymbolsStats where - T: IntoIterator>>>, + T: IntoIterator>>, { let mut res = LibrarySymbolsStats::default(); for entry in iter { - let value = entry.value.unwrap(); - for symbols in value.values() { - res.total += symbols.len(); - res.size += symbols.memory_size(); - } + let symbols = entry.value.unwrap(); + res.total += symbols.len(); + res.size += symbols.memory_size(); } res } diff --git a/crates/ide_db/src/apply_change.rs b/crates/ide_db/src/apply_change.rs index bba270421c..6c085ffc97 100644 --- a/crates/ide_db/src/apply_change.rs +++ b/crates/ide_db/src/apply_change.rs @@ -137,7 +137,7 @@ impl RootDatabase { hir::db::InternTypeParamIdQuery // SymbolsDatabase - crate::symbol_index::FileSymbolsQuery + crate::symbol_index::ModuleSymbolsQuery crate::symbol_index::LibrarySymbolsQuery crate::symbol_index::LocalRootsQuery crate::symbol_index::LibraryRootsQuery diff --git a/crates/ide_db/src/items_locator.rs b/crates/ide_db/src/items_locator.rs index a3ea3edc97..e0dbe6caf0 100644 --- a/crates/ide_db/src/items_locator.rs +++ b/crates/ide_db/src/items_locator.rs @@ -133,9 +133,8 @@ fn get_name_definition( import_candidate: &FileSymbol, ) -> Option { let _p = profile::span("get_name_definition"); - let file_id = import_candidate.file_id; - let candidate_node = import_candidate.ptr.to_node(sema.parse(file_id).syntax()); + let candidate_node = import_candidate.loc.syntax(sema)?; let candidate_name_node = if candidate_node.kind() != NAME { candidate_node.children().find(|it| it.kind() == NAME)? } else { diff --git a/crates/ide_db/src/symbol_index.rs b/crates/ide_db/src/symbol_index.rs index 54ceafa67f..cc2b2bbb7b 100644 --- a/crates/ide_db/src/symbol_index.rs +++ b/crates/ide_db/src/symbol_index.rs @@ -30,18 +30,19 @@ use std::{ use base_db::{ salsa::{self, ParallelDatabase}, - CrateId, FileId, SourceDatabaseExt, SourceRootId, + CrateId, FileRange, SourceDatabaseExt, SourceRootId, Upcast, }; +use either::Either; use fst::{self, Streamer}; -use hir::db::DefDatabase; -use rayon::prelude::*; -use rustc_hash::{FxHashMap, FxHashSet}; -use syntax::{ - ast::{self, HasName}, - match_ast, AstNode, Parse, SmolStr, SourceFile, - SyntaxKind::*, - SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent, +use hir::{ + db::{DefDatabase, HirDatabase}, + AdtId, AssocContainerId, AssocItemId, AssocItemLoc, DefHasSource, DefWithBodyId, HasSource, + HirFileId, ImplId, InFile, ItemLoc, ItemTreeNode, Lookup, MacroDef, Module, ModuleDefId, + ModuleId, Semantics, TraitId, }; +use rayon::prelude::*; +use rustc_hash::FxHashSet; +use syntax::{ast::HasName, AstNode, SmolStr, SyntaxNode, SyntaxNodePtr}; use crate::RootDatabase; @@ -92,58 +93,68 @@ impl Query { } #[salsa::query_group(SymbolsDatabaseStorage)] -pub trait SymbolsDatabase: hir::db::HirDatabase + SourceDatabaseExt { - fn file_symbols(&self, file_id: FileId) -> Arc; - fn library_symbols(&self) -> Arc>; +pub trait SymbolsDatabase: HirDatabase + SourceDatabaseExt + Upcast { + /// The symbol index for a given module. These modules should only be in source roots that + /// are inside local_roots. + fn module_symbols(&self, module_id: ModuleId) -> Arc; + + /// The symbol index for a given source root within library_roots. + fn library_symbols(&self, source_root_id: SourceRootId) -> Arc; + /// The set of "local" (that is, from the current workspace) roots. /// Files in local roots are assumed to change frequently. #[salsa::input] fn local_roots(&self) -> Arc>; + /// The set of roots for crates.io libraries. /// Files in libraries are assumed to never change. #[salsa::input] fn library_roots(&self) -> Arc>; } -fn library_symbols(db: &dyn SymbolsDatabase) -> Arc> { +fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Arc { let _p = profile::span("library_symbols"); - let roots = db.library_roots(); - let res = roots + // todo: this could be parallelized, once I figure out how to do that... + let symbols = db + .source_root_crates(source_root_id) .iter() - .map(|&root_id| { - let root = db.source_root(root_id); - let files = root - .iter() - .map(|it| (it, SourceDatabaseExt::file_text(db, it))) - .collect::>(); - let symbol_index = SymbolIndex::for_files( - files.into_par_iter().map(|(file, text)| (file, SourceFile::parse(&text))), - ); - (root_id, symbol_index) - }) + .flat_map(|&krate| module_ids_for_crate(db.upcast(), krate)) + // 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)) + .flatten() .collect(); - Arc::new(res) + + Arc::new(SymbolIndex::new(symbols)) } -fn file_symbols(db: &dyn SymbolsDatabase, file_id: FileId) -> Arc { - db.unwind_if_cancelled(); - let parse = db.parse(file_id); - - let symbols = source_file_to_file_symbols(&parse.tree(), file_id); - - // FIXME: add macros here - +fn module_symbols(db: &dyn SymbolsDatabase, module_id: ModuleId) -> Arc { + let _p = profile::span("module_symbols"); + let symbols = SymbolCollector::collect(db, module_id); Arc::new(SymbolIndex::new(symbols)) } /// Need to wrap Snapshot to provide `Clone` impl for `map_with` struct Snap(DB); +impl Snap> { + fn new(db: &DB) -> Self { + Self(db.snapshot()) + } +} impl Clone for Snap> { fn clone(&self) -> Snap> { Snap(self.0.snapshot()) } } +impl std::ops::Deref for Snap { + type Target = DB; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} // Feature: Workspace Symbol // @@ -174,51 +185,45 @@ impl Clone for Snap> { pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec { let _p = profile::span("world_symbols").detail(|| query.query.clone()); - let tmp1; - let tmp2; - let buf: Vec<&SymbolIndex> = if query.libs { - tmp1 = db.library_symbols(); - tmp1.values().collect() + let indices: Vec<_> = if query.libs { + db.library_roots() + .par_iter() + .map_with(Snap::new(db), |snap, &root| snap.library_symbols(root)) + .collect() } else { - let mut files = Vec::new(); + let mut module_ids = Vec::new(); + for &root in db.local_roots().iter() { - let sr = db.source_root(root); - files.extend(sr.iter()) + let crates = db.source_root_crates(root); + for &krate in crates.iter() { + module_ids.extend(module_ids_for_crate(db, krate)); + } } - let snap = Snap(db.snapshot()); - tmp2 = files + module_ids .par_iter() - .map_with(snap, |db, &file_id| db.0.file_symbols(file_id)) - .collect::>(); - tmp2.iter().map(|it| &**it).collect() + .map_with(Snap::new(db), |snap, &module_id| snap.module_symbols(module_id)) + .collect() }; - query.search(&buf) + + query.search(&indices) } pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec { let _p = profile::span("crate_symbols").detail(|| format!("{:?}", query)); - // FIXME(#4842): This now depends on DefMap, why not build the entire symbol index from - // that instead? - let def_map = db.crate_def_map(krate); - let mut files = Vec::new(); - let mut modules = vec![def_map.root()]; - while let Some(module) = modules.pop() { - let data = &def_map[module]; - files.extend(data.origin.file_id()); - modules.extend(data.children.values()); - } - - let snap = Snap(db.snapshot()); - - let buf = files + let module_ids = module_ids_for_crate(db, krate); + let indices: Vec<_> = module_ids .par_iter() - .map_with(snap, |db, &file_id| db.0.file_symbols(file_id)) - .collect::>(); - let buf = buf.iter().map(|it| &**it).collect::>(); + .map_with(Snap::new(db), |snap, &module_id| snap.module_symbols(module_id)) + .collect(); - query.search(&buf) + query.search(&indices) +} + +fn module_ids_for_crate(db: &dyn DefDatabase, krate: CrateId) -> Vec { + let def_map = db.crate_def_map(krate); + def_map.modules().map(|(id, _)| def_map.module_id(id)).collect() } pub fn index_resolve(db: &RootDatabase, name: &str) -> Vec { @@ -297,15 +302,6 @@ impl SymbolIndex { self.map.as_fst().size() + self.symbols.len() * mem::size_of::() } - pub(crate) fn for_files( - files: impl ParallelIterator)>, - ) -> SymbolIndex { - let symbols = files - .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id)) - .collect::>(); - SymbolIndex::new(symbols) - } - fn range_to_map_value(start: usize, end: usize) -> u64 { debug_assert![start <= (std::u32::MAX as usize)]; debug_assert![end <= (std::u32::MAX as usize)]; @@ -321,7 +317,7 @@ impl SymbolIndex { } impl Query { - pub(crate) fn search(self, indices: &[&SymbolIndex]) -> Vec { + pub(crate) fn search(self, indices: &[Arc]) -> Vec { let _p = profile::span("symbol_index::Query::search"); let mut op = fst::map::OpBuilder::new(); for file_symbols in indices.iter() { @@ -364,15 +360,49 @@ impl Query { /// possible. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct FileSymbol { - pub file_id: FileId, pub name: SmolStr, + pub loc: DeclarationLocation, pub kind: FileSymbolKind, - pub range: TextRange, - pub ptr: SyntaxNodePtr, - pub name_range: Option, 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, @@ -400,79 +430,358 @@ impl FileSymbolKind { } } -fn source_file_to_file_symbols(source_file: &SourceFile, file_id: FileId) -> Vec { - let mut symbols = Vec::new(); - let mut stack = Vec::new(); +/// Represents an outstanding module that the symbol collector must collect symbols from. +struct SymbolCollectorWork { + module_id: ModuleId, + parent: Option, +} - for event in source_file.syntax().preorder() { - match event { - WalkEvent::Enter(node) => { - if let Some(mut symbol) = to_file_symbol(&node, file_id) { - symbol.container_name = stack.last().cloned(); +struct SymbolCollector<'a> { + db: &'a dyn SymbolsDatabase, + symbols: Vec, + work: Vec, + current_container_name: Option, +} - stack.push(symbol.name.clone()); - symbols.push(symbol); +/// 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(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(_) => {} } + } - WalkEvent::Leave(node) => { - if to_symbol(&node).is_some() { - stack.pop(); - } + 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), + }); } } } - symbols -} - -fn to_symbol(node: &SyntaxNode) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> { - fn decl(node: N) -> Option<(SmolStr, SyntaxNodePtr, TextRange)> { - let name = node.name()?; - let name_range = name.syntax().text_range(); - let name = name.text().into(); - let ptr = SyntaxNodePtr::new(node.syntax()); - - Some((name, ptr, name_range)) + 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) + } } - match_ast! { - match node { - ast::Fn(it) => decl(it), - ast::Struct(it) => decl(it), - ast::Enum(it) => decl(it), - ast::Trait(it) => decl(it), - ast::Module(it) => decl(it), - ast::TypeAlias(it) => decl(it), - ast::Const(it) => decl(it), - ast::Static(it) => decl(it), - ast::Macro(it) => decl(it), - ast::Union(it) => decl(it), - _ => None, + + 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: AssocContainerId) -> Option { + match container { + AssocContainerId::ModuleId(module_id) => { + let module = Module::from(module_id); + module.name(db).and_then(|name| name.as_text()) + } + AssocContainerId::TraitId(trait_id) => { + let trait_data = db.trait_data(trait_id); + trait_data.name.as_text() + } + AssocContainerId::ImplId(_) => 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); } } } -fn to_file_symbol(node: &SyntaxNode, file_id: FileId) -> Option { - to_symbol(node).map(move |(name, ptr, name_range)| FileSymbol { - name, - kind: match node.kind() { - FN => FileSymbolKind::Function, - STRUCT => FileSymbolKind::Struct, - ENUM => FileSymbolKind::Enum, - TRAIT => FileSymbolKind::Trait, - MODULE => FileSymbolKind::Module, - TYPE_ALIAS => FileSymbolKind::TypeAlias, - CONST => FileSymbolKind::Const, - STATIC => FileSymbolKind::Static, - MACRO_RULES => FileSymbolKind::Macro, - MACRO_DEF => FileSymbolKind::Macro, - UNION => FileSymbolKind::Union, - kind => unreachable!("{:?}", kind), - }, - range: node.text_range(), - ptr, - file_id, - name_range: Some(name_range), - container_name: None, - }) +#[cfg(test)] +mod tests { + + use base_db::fixture::WithFixture; + use expect_test::expect_file; + + use super::*; + + #[test] + fn test_symbol_index_collection() { + let (db, _) = RootDatabase::with_many_files( + r#" +//- /main.rs + +macro_rules! macro_rules_macro { + () => {} +}; + +macro_rules! define_struct { + () => { + struct StructFromMacro; + } +}; + +define_struct!(); + +macro Macro { } + +struct Struct; +enum Enum { + A, B +} +union Union {} + +impl Struct { + fn impl_fn() {} +} + +trait Trait { + fn trait_fn(&self); +} + +fn main() { + struct StructInFn; +} + +const CONST: u32 = 1; +static STATIC: &'static str = "2"; +type Alias = Struct; + +mod a_mod { + struct StructInModA; +} + +const _: () = { + struct StructInUnnamedConst; + + () +}; + +const CONST_WITH_INNER: () = { + struct StructInNamedConst; + + () +}; + +mod b_mod; + +//- /b_mod.rs +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)) + }) + .collect(); + + expect_file!["./test_data/test_symbol_index_collection.txt"].assert_debug_eq(&symbols); + } } diff --git a/crates/ide_db/src/test_data/test_symbol_index_collection.txt b/crates/ide_db/src/test_data/test_symbol_index_collection.txt new file mode 100644 index 0000000000..fe6eebc4f0 --- /dev/null +++ b/crates/ide_db/src/test_data/test_symbol_index_collection.txt @@ -0,0 +1,527 @@ +[ + ( + ModuleId { + krate: CrateId( + 0, + ), + block: None, + local_id: Idx::(0), + }, + [ + FileSymbol { + name: "StructFromMacro", + loc: DeclarationLocation { + hir_file_id: HirFileId( + MacroFile( + MacroFile { + macro_call_id: MacroCallId( + 0, + ), + }, + ), + ), + ptr: SyntaxNodePtr { + range: 0..22, + kind: STRUCT, + }, + name_ptr: SyntaxNodePtr { + range: 6..21, + kind: NAME, + }, + }, + kind: Struct, + container_name: None, + }, + FileSymbol { + name: "Struct", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 170..184, + kind: STRUCT, + }, + name_ptr: SyntaxNodePtr { + range: 177..183, + kind: NAME, + }, + }, + kind: Struct, + container_name: None, + }, + FileSymbol { + name: "Enum", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 185..207, + kind: ENUM, + }, + name_ptr: SyntaxNodePtr { + range: 190..194, + kind: NAME, + }, + }, + kind: Enum, + container_name: None, + }, + FileSymbol { + name: "Union", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 208..222, + kind: UNION, + }, + name_ptr: SyntaxNodePtr { + range: 214..219, + kind: NAME, + }, + }, + kind: Union, + container_name: None, + }, + FileSymbol { + name: "Trait", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 261..300, + kind: TRAIT, + }, + name_ptr: SyntaxNodePtr { + range: 267..272, + kind: NAME, + }, + }, + kind: Trait, + container_name: None, + }, + FileSymbol { + name: "trait_fn", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 279..298, + kind: FN, + }, + name_ptr: SyntaxNodePtr { + range: 282..290, + kind: NAME, + }, + }, + kind: Function, + container_name: Some( + "Trait", + ), + }, + FileSymbol { + name: "main", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 302..338, + kind: FN, + }, + name_ptr: SyntaxNodePtr { + range: 305..309, + kind: NAME, + }, + }, + kind: Function, + container_name: None, + }, + FileSymbol { + name: "CONST", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 340..361, + kind: CONST, + }, + name_ptr: SyntaxNodePtr { + range: 346..351, + kind: NAME, + }, + }, + kind: Const, + container_name: None, + }, + FileSymbol { + name: "STATIC", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 362..396, + kind: STATIC, + }, + name_ptr: SyntaxNodePtr { + range: 369..375, + kind: NAME, + }, + }, + kind: Static, + container_name: None, + }, + FileSymbol { + name: "Alias", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 397..417, + kind: TYPE_ALIAS, + }, + name_ptr: SyntaxNodePtr { + range: 402..407, + kind: NAME, + }, + }, + kind: TypeAlias, + container_name: None, + }, + FileSymbol { + name: "a_mod", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 419..457, + kind: MODULE, + }, + name_ptr: SyntaxNodePtr { + range: 423..428, + kind: NAME, + }, + }, + kind: Module, + container_name: None, + }, + FileSymbol { + name: "CONST_WITH_INNER", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 520..592, + kind: CONST, + }, + name_ptr: SyntaxNodePtr { + range: 526..542, + kind: NAME, + }, + }, + kind: Const, + container_name: None, + }, + FileSymbol { + name: "b_mod", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 594..604, + kind: MODULE, + }, + name_ptr: SyntaxNodePtr { + range: 598..603, + kind: NAME, + }, + }, + kind: Module, + container_name: None, + }, + FileSymbol { + name: "impl_fn", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 242..257, + kind: FN, + }, + name_ptr: SyntaxNodePtr { + range: 245..252, + kind: NAME, + }, + }, + kind: Function, + container_name: None, + }, + FileSymbol { + name: "macro_rules_macro", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 1..48, + kind: MACRO_RULES, + }, + name_ptr: SyntaxNodePtr { + range: 14..31, + kind: NAME, + }, + }, + kind: Macro, + container_name: None, + }, + FileSymbol { + name: "define_struct", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 51..131, + kind: MACRO_RULES, + }, + name_ptr: SyntaxNodePtr { + range: 64..77, + kind: NAME, + }, + }, + kind: Macro, + container_name: None, + }, + FileSymbol { + name: "Macro", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 153..168, + kind: MACRO_DEF, + }, + name_ptr: SyntaxNodePtr { + range: 159..164, + kind: NAME, + }, + }, + kind: Macro, + container_name: None, + }, + FileSymbol { + name: "StructInUnnamedConst", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 479..507, + kind: STRUCT, + }, + name_ptr: SyntaxNodePtr { + range: 486..506, + kind: NAME, + }, + }, + kind: Struct, + container_name: None, + }, + FileSymbol { + name: "StructInNamedConst", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 555..581, + kind: STRUCT, + }, + name_ptr: SyntaxNodePtr { + range: 562..580, + kind: NAME, + }, + }, + kind: Struct, + container_name: Some( + "CONST_WITH_INNER", + ), + }, + FileSymbol { + name: "StructInFn", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 318..336, + kind: STRUCT, + }, + name_ptr: SyntaxNodePtr { + range: 325..335, + kind: NAME, + }, + }, + kind: Struct, + container_name: Some( + "main", + ), + }, + ], + ), + ( + ModuleId { + krate: CrateId( + 0, + ), + block: None, + local_id: Idx::(1), + }, + [ + FileSymbol { + name: "StructInModA", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 0, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 435..455, + kind: STRUCT, + }, + name_ptr: SyntaxNodePtr { + range: 442..454, + kind: NAME, + }, + }, + kind: Struct, + container_name: None, + }, + ], + ), + ( + ModuleId { + krate: CrateId( + 0, + ), + block: None, + local_id: Idx::(2), + }, + [ + FileSymbol { + name: "StructInModB", + loc: DeclarationLocation { + hir_file_id: HirFileId( + FileId( + FileId( + 1, + ), + ), + ), + ptr: SyntaxNodePtr { + range: 0..20, + kind: STRUCT, + }, + name_ptr: SyntaxNodePtr { + range: 7..19, + kind: NAME, + }, + }, + kind: Struct, + container_name: None, + }, + ], + ), +]