Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir/src/symbols.rs')
| -rw-r--r-- | crates/hir/src/symbols.rs | 253 |
1 files changed, 169 insertions, 84 deletions
diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index f8416f86bf..a6b8ed70c3 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -1,27 +1,34 @@ //! File symbol extraction. +use either::Either; use hir_def::{ db::DefDatabase, - item_scope::ItemInNs, + item_scope::{ImportId, ImportOrExternCrate}, + per_ns::Item, src::{HasChildSource, HasSource}, - AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId, ModuleDefId, ModuleId, - TraitId, + visibility::{Visibility, VisibilityExplicitness}, + AdtId, AssocItemId, DefWithBodyId, ExternCrateId, HasModule, ImplId, Lookup, MacroId, + ModuleDefId, ModuleId, TraitId, }; -use hir_expand::HirFileId; +use hir_expand::{name::Name, HirFileId}; use hir_ty::{ db::HirDatabase, display::{hir_display_with_types_map, HirDisplay}, }; +use intern::Symbol; +use rustc_hash::FxHashMap; use span::Edition; use syntax::{ast::HasName, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, ToSmolStr}; use crate::{Module, ModuleDef, Semantics}; +pub type FxIndexSet<T> = indexmap::IndexSet<T, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>; + /// 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 name: Symbol, pub def: ModuleDef, pub loc: DeclarationLocation, pub container_name: Option<SmolStr>, @@ -37,7 +44,7 @@ pub struct DeclarationLocation { /// 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: AstPtr<syntax::ast::Name>, + pub name_ptr: AstPtr<Either<syntax::ast::Name, syntax::ast::NameRef>>, } impl DeclarationLocation { @@ -55,7 +62,7 @@ struct SymbolCollectorWork { pub struct SymbolCollector<'a> { db: &'a dyn HirDatabase, - symbols: Vec<FileSymbol>, + symbols: FxIndexSet<FileSymbol>, work: Vec<SymbolCollectorWork>, current_container_name: Option<SmolStr>, edition: Edition, @@ -86,11 +93,11 @@ impl<'a> SymbolCollector<'a> { } } - pub fn finish(self) -> Vec<FileSymbol> { - self.symbols + pub fn finish(self) -> Box<[FileSymbol]> { + self.symbols.into_iter().collect() } - pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Vec<FileSymbol> { + pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Box<[FileSymbol]> { let mut symbol_collector = SymbolCollector::new(db); symbol_collector.collect(module); symbol_collector.finish() @@ -104,96 +111,174 @@ impl<'a> SymbolCollector<'a> { } 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), + let push_decl = |this: &mut Self, def, name| { + match def { + ModuleDefId::ModuleId(id) => this.push_module(id, name), ModuleDefId::FunctionId(id) => { - self.push_decl(id, false); - self.collect_from_body(id); + this.push_decl(id, name, false); + this.collect_from_body(id); } - ModuleDefId::AdtId(AdtId::StructId(id)) => self.push_decl(id, false), - ModuleDefId::AdtId(AdtId::EnumId(id)) => self.push_decl(id, false), - ModuleDefId::AdtId(AdtId::UnionId(id)) => self.push_decl(id, false), + ModuleDefId::AdtId(AdtId::StructId(id)) => this.push_decl(id, name, false), + ModuleDefId::AdtId(AdtId::EnumId(id)) => this.push_decl(id, name, false), + ModuleDefId::AdtId(AdtId::UnionId(id)) => this.push_decl(id, name, false), ModuleDefId::ConstId(id) => { - self.push_decl(id, false); - self.collect_from_body(id); + this.push_decl(id, name, false); + this.collect_from_body(id); } ModuleDefId::StaticId(id) => { - self.push_decl(id, false); - self.collect_from_body(id); + this.push_decl(id, name, false); + this.collect_from_body(id); } ModuleDefId::TraitId(id) => { - self.push_decl(id, false); - self.collect_from_trait(id); + this.push_decl(id, name, false); + this.collect_from_trait(id); } ModuleDefId::TraitAliasId(id) => { - self.push_decl(id, false); + this.push_decl(id, name, false); } ModuleDefId::TypeAliasId(id) => { - self.push_decl(id, false); + this.push_decl(id, name, false); } ModuleDefId::MacroId(id) => match id { - MacroId::Macro2Id(id) => self.push_decl(id, false), - MacroId::MacroRulesId(id) => self.push_decl(id, false), - MacroId::ProcMacroId(id) => self.push_decl(id, false), + MacroId::Macro2Id(id) => this.push_decl(id, name, false), + MacroId::MacroRulesId(id) => this.push_decl(id, name, false), + MacroId::ProcMacroId(id) => this.push_decl(id, name, false), }, // Don't index these. ModuleDefId::BuiltinType(_) => {} ModuleDefId::EnumVariantId(_) => {} } - } + }; - for impl_id in scope.impls() { - self.collect_from_impl(impl_id); - } + // Nested trees are very common, so a cache here will hit a lot. + let import_child_source_cache = &mut FxHashMap::default(); + + let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId| { + let source = import_child_source_cache + .entry(i.import) + .or_insert_with(|| i.import.child_source(this.db.upcast())); + let Some(use_tree_src) = source.value.get(i.idx) else { return }; + let Some(name_ptr) = use_tree_src + .rename() + .and_then(|rename| rename.name()) + .map(Either::Left) + .or_else(|| use_tree_src.path()?.segment()?.name_ref().map(Either::Right)) + .map(|it| AstPtr::new(&it)) + else { + return; + }; + let dec_loc = DeclarationLocation { + hir_file_id: source.file_id, + ptr: SyntaxNodePtr::new(use_tree_src.syntax()), + name_ptr, + }; + this.symbols.insert(FileSymbol { + name: name.symbol().clone(), + def: def.into(), + container_name: this.current_container_name.clone(), + loc: dec_loc, + is_alias: false, + is_assoc: false, + }); + }; - // Record renamed imports. - // FIXME: In case it imports multiple items under different namespaces we just pick one arbitrarily - // for now. - for id in scope.imports() { - let source = id.import.child_source(self.db.upcast()); - let Some(use_tree_src) = source.value.get(id.idx) else { continue }; - let Some(rename) = use_tree_src.rename() else { continue }; - let Some(name) = rename.name() else { continue }; - - let res = scope.fully_resolve_import(self.db.upcast(), id); - res.iter_items().for_each(|(item, _)| { - let def = match item { - ItemInNs::Types(def) | ItemInNs::Values(def) => def, - ItemInNs::Macros(def) => ModuleDefId::from(def), - } - .into(); + let push_extern_crate = + |this: &mut Self, i: ExternCrateId, name: &Name, def: ModuleDefId| { + let loc = i.lookup(this.db.upcast()); + let source = loc.source(this.db.upcast()); + let Some(name_ptr) = source + .value + .rename() + .and_then(|rename| rename.name()) + .map(Either::Left) + .or_else(|| source.value.name_ref().map(Either::Right)) + .map(|it| AstPtr::new(&it)) + else { + return; + }; let dec_loc = DeclarationLocation { hir_file_id: source.file_id, - ptr: SyntaxNodePtr::new(use_tree_src.syntax()), - name_ptr: AstPtr::new(&name), + ptr: SyntaxNodePtr::new(source.value.syntax()), + name_ptr, }; - - self.symbols.push(FileSymbol { - name: name.text().into(), - def, - container_name: self.current_container_name.clone(), + this.symbols.insert(FileSymbol { + name: name.symbol().clone(), + def: def.into(), + container_name: this.current_container_name.clone(), loc: dec_loc, is_alias: false, is_assoc: false, }); - }); + }; + + let is_explicit_import = |vis| { + match vis { + Visibility::Module(_, VisibilityExplicitness::Explicit) => true, + Visibility::Module(_, VisibilityExplicitness::Implicit) => { + // consider imports in the crate root explicit, as these are visibly + // crate-wide anyways + module_id.is_crate_root() + } + Visibility::Public => true, + } + }; + + let def_map = module_id.def_map(self.db.upcast()); + let scope = &def_map[module_id.local_id].scope; + + for impl_id in scope.impls() { + self.collect_from_impl(impl_id); + } + + for (name, Item { def, vis, import }) in scope.types() { + if let Some(i) = import { + if is_explicit_import(vis) { + match i { + ImportOrExternCrate::Import(i) => push_import(self, i, name, def), + ImportOrExternCrate::ExternCrate(i) => { + push_extern_crate(self, i, name, def) + } + } + } + continue; + } + // self is a declaration + push_decl(self, def, name) + } + + for (name, Item { def, vis, import }) in scope.macros() { + if let Some(i) = import { + if is_explicit_import(vis) { + push_import(self, i, name, def.into()); + } + continue; + } + // self is a declaration + push_decl(self, def.into(), name) + } + + for (name, Item { def, vis, import }) in scope.values() { + if let Some(i) = import { + if is_explicit_import(vis) { + push_import(self, i, name, def); + } + continue; + } + // self is a declaration + push_decl(self, def, name) } for const_id in scope.unnamed_consts() { self.collect_from_body(const_id); } - for (_, id) in scope.legacy_macros() { + for (name, id) in scope.legacy_macros() { for &id in id { if id.module(self.db.upcast()) == module_id { match id { - MacroId::Macro2Id(id) => self.push_decl(id, false), - MacroId::MacroRulesId(id) => self.push_decl(id, false), - MacroId::ProcMacroId(id) => self.push_decl(id, false), + MacroId::Macro2Id(id) => self.push_decl(id, name, false), + MacroId::MacroRulesId(id) => self.push_decl(id, name, false), + MacroId::ProcMacroId(id) => self.push_decl(id, name, false), } } } @@ -223,8 +308,8 @@ impl<'a> SymbolCollector<'a> { .to_smolstr(), ); self.with_container_name(impl_name, |s| { - for &assoc_item_id in impl_data.items.iter() { - s.push_assoc_item(assoc_item_id) + for &(ref name, assoc_item_id) in &impl_data.items { + s.push_assoc_item(assoc_item_id, name) } }) } @@ -232,8 +317,8 @@ impl<'a> SymbolCollector<'a> { fn collect_from_trait(&mut self, trait_id: TraitId) { let trait_data = self.db.trait_data(trait_id); self.with_container_name(Some(trait_data.name.as_str().into()), |s| { - for &(_, assoc_item_id) in &trait_data.items { - s.push_assoc_item(assoc_item_id); + for &(ref name, assoc_item_id) in &trait_data.items { + s.push_assoc_item(assoc_item_id, name); } }); } @@ -266,15 +351,15 @@ impl<'a> SymbolCollector<'a> { } } - fn push_assoc_item(&mut self, assoc_item_id: AssocItemId) { + fn push_assoc_item(&mut self, assoc_item_id: AssocItemId, name: &Name) { match assoc_item_id { - AssocItemId::FunctionId(id) => self.push_decl(id, true), - AssocItemId::ConstId(id) => self.push_decl(id, true), - AssocItemId::TypeAliasId(id) => self.push_decl(id, true), + AssocItemId::FunctionId(id) => self.push_decl(id, name, true), + AssocItemId::ConstId(id) => self.push_decl(id, name, true), + AssocItemId::TypeAliasId(id) => self.push_decl(id, name, true), } } - fn push_decl<'db, L>(&mut self, id: L, is_assoc: bool) + fn push_decl<'db, L>(&mut self, id: L, name: &Name, is_assoc: bool) where L: Lookup<Database<'db> = dyn DefDatabase + 'db> + Into<ModuleDefId>, <L as Lookup>::Data: HasSource, @@ -287,13 +372,13 @@ impl<'a> SymbolCollector<'a> { let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr: AstPtr::new(&name_node), + name_ptr: AstPtr::new(&name_node).wrap_left(), }; if let Some(attrs) = def.attrs(self.db) { for alias in attrs.doc_aliases() { - self.symbols.push(FileSymbol { - name: alias.as_str().into(), + self.symbols.insert(FileSymbol { + name: alias.clone(), def, loc: dec_loc.clone(), container_name: self.current_container_name.clone(), @@ -303,8 +388,8 @@ impl<'a> SymbolCollector<'a> { } } - self.symbols.push(FileSymbol { - name: name_node.text().into(), + self.symbols.insert(FileSymbol { + name: name.symbol().clone(), def, container_name: self.current_container_name.clone(), loc: dec_loc, @@ -313,7 +398,7 @@ impl<'a> SymbolCollector<'a> { }); } - fn push_module(&mut self, module_id: ModuleId) { + fn push_module(&mut self, module_id: ModuleId, name: &Name) { let def_map = module_id.def_map(self.db.upcast()); let module_data = &def_map[module_id.local_id]; let Some(declaration) = module_data.origin.declaration() else { return }; @@ -322,15 +407,15 @@ impl<'a> SymbolCollector<'a> { let dec_loc = DeclarationLocation { hir_file_id: declaration.file_id, ptr: SyntaxNodePtr::new(module.syntax()), - name_ptr: AstPtr::new(&name_node), + name_ptr: AstPtr::new(&name_node).wrap_left(), }; let def = ModuleDef::Module(module_id.into()); if let Some(attrs) = def.attrs(self.db) { for alias in attrs.doc_aliases() { - self.symbols.push(FileSymbol { - name: alias.as_str().into(), + self.symbols.insert(FileSymbol { + name: alias.clone(), def, loc: dec_loc.clone(), container_name: self.current_container_name.clone(), @@ -340,8 +425,8 @@ impl<'a> SymbolCollector<'a> { } } - self.symbols.push(FileSymbol { - name: name_node.text().into(), + self.symbols.insert(FileSymbol { + name: name.symbol().clone(), def: ModuleDef::Module(module_id.into()), container_name: self.current_container_name.clone(), loc: dec_loc, |