Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-db/src/search.rs')
| -rw-r--r-- | crates/ide-db/src/search.rs | 115 |
1 files changed, 70 insertions, 45 deletions
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index b2b0e49085..ada2821d6b 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -7,7 +7,9 @@ use std::{mem, sync::Arc}; use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; -use hir::{DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility}; +use hir::{ + AsAssocItem, DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility, +}; use memchr::memmem::Finder; use once_cell::unsync::Lazy; use parser::SyntaxKind; @@ -311,15 +313,15 @@ impl Definition { pub fn usages<'a>(self, sema: &'a Semantics<'_, RootDatabase>) -> FindUsages<'a> { FindUsages { - local_repr: match self { - Definition::Local(local) => Some(local.representative(sema.db)), - _ => None, - }, def: self, - trait_assoc_def: as_trait_assoc_def(sema.db, self), + assoc_item_container: self.as_assoc_item(sema.db).map(|a| a.container(sema.db)), sema, scope: None, include_self_kw_refs: None, + local_repr: match self { + Definition::Local(local) => Some(local.representative(sema.db)), + _ => None, + }, search_self_mod: false, } } @@ -328,12 +330,16 @@ impl Definition { #[derive(Clone)] pub struct FindUsages<'a> { def: Definition, - /// If def is an assoc item from a trait or trait impl, this is the corresponding item of the trait definition - trait_assoc_def: Option<Definition>, sema: &'a Semantics<'a, RootDatabase>, scope: Option<SearchScope>, + /// The container of our definition should it be an assoc item + assoc_item_container: Option<hir::AssocItemContainer>, + /// whether to search for the `Self` type of the definition include_self_kw_refs: Option<hir::Type>, + /// the local representative for the local definition we are searching for + /// (this is required for finding all local declarations in a or-pattern) local_repr: Option<hir::Local>, + /// whether to search for the `self` module search_self_mod: bool, } @@ -380,7 +386,9 @@ impl<'a> FindUsages<'a> { let sema = self.sema; let search_scope = { - let base = self.trait_assoc_def.unwrap_or(self.def).search_scope(sema.db); + // FIXME: Is the trait scope needed for trait impl assoc items? + let base = + as_trait_assoc_def(sema.db, self.def).unwrap_or(self.def).search_scope(sema.db); match &self.scope { None => base, Some(scope) => base.intersection(scope), @@ -494,20 +502,28 @@ impl<'a> FindUsages<'a> { } // Search for `super` and `crate` resolving to our module - match self.def { - Definition::Module(module) => { - let scope = search_scope - .intersection(&SearchScope::module_and_children(self.sema.db, module)); + if let Definition::Module(module) = self.def { + let scope = + search_scope.intersection(&SearchScope::module_and_children(self.sema.db, module)); - let is_crate_root = - module.is_crate_root(self.sema.db).then(|| Finder::new("crate")); - let finder = &Finder::new("super"); + let is_crate_root = module.is_crate_root(self.sema.db).then(|| Finder::new("crate")); + let finder = &Finder::new("super"); - for (text, file_id, search_range) in scope_files(sema, &scope) { - let tree = Lazy::new(move || sema.parse(file_id).syntax().clone()); + for (text, file_id, search_range) in scope_files(sema, &scope) { + let tree = Lazy::new(move || sema.parse(file_id).syntax().clone()); + for offset in match_indices(&text, finder, search_range) { + if let Some(iter) = find_nodes("super", &tree, offset) { + for name_ref in iter.filter_map(ast::NameRef::cast) { + if self.found_name_ref(&name_ref, sink) { + return; + } + } + } + } + if let Some(finder) = &is_crate_root { for offset in match_indices(&text, finder, search_range) { - if let Some(iter) = find_nodes("super", &tree, offset) { + if let Some(iter) = find_nodes("crate", &tree, offset) { for name_ref in iter.filter_map(ast::NameRef::cast) { if self.found_name_ref(&name_ref, sink) { return; @@ -515,20 +531,8 @@ impl<'a> FindUsages<'a> { } } } - if let Some(finder) = &is_crate_root { - for offset in match_indices(&text, finder, search_range) { - if let Some(iter) = find_nodes("crate", &tree, offset) { - for name_ref in iter.filter_map(ast::NameRef::cast) { - if self.found_name_ref(&name_ref, sink) { - return; - } - } - } - } - } } } - _ => (), } // search for module `self` references in our module's definition source @@ -655,13 +659,26 @@ impl<'a> FindUsages<'a> { sink(file_id, reference) } Some(NameRefClass::Definition(def)) - if match self.trait_assoc_def { - Some(trait_assoc_def) => { - // we have a trait assoc item, so force resolve all assoc items to their trait version - convert_to_def_in_trait(self.sema.db, def) == trait_assoc_def - } - None => self.def == def, - } => + if self.def == def + // is our def a trait assoc item? then we want to find all assoc items from trait impls of our trait + || matches!(self.assoc_item_container, Some(hir::AssocItemContainer::Trait(_))) + && convert_to_def_in_trait(self.sema.db, def) == self.def => + { + let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); + let reference = FileReference { + range, + name: ast::NameLike::NameRef(name_ref.clone()), + category: ReferenceCategory::new(&def, name_ref), + }; + sink(file_id, reference) + } + // FIXME: special case type aliases, we can't filter between impl and trait defs here as we lack the substitutions + // so we always resolve all assoc type aliases to both their trait def and impl defs + Some(NameRefClass::Definition(def)) + if self.assoc_item_container.is_some() + && matches!(self.def, Definition::TypeAlias(_)) + && convert_to_def_in_trait(self.sema.db, def) + == convert_to_def_in_trait(self.sema.db, self.def) => { let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax()); let reference = FileReference { @@ -752,13 +769,21 @@ impl<'a> FindUsages<'a> { false } Some(NameClass::Definition(def)) if def != self.def => { - // if the def we are looking for is a trait (impl) assoc item, we'll have to resolve the items to trait definition assoc item - if !matches!( - self.trait_assoc_def, - Some(trait_assoc_def) - if convert_to_def_in_trait(self.sema.db, def) == trait_assoc_def - ) { - return false; + match (&self.assoc_item_container, self.def) { + // for type aliases we always want to reference the trait def and all the trait impl counterparts + // FIXME: only until we can resolve them correctly, see FIXME above + (Some(_), Definition::TypeAlias(_)) + if convert_to_def_in_trait(self.sema.db, def) + != convert_to_def_in_trait(self.sema.db, self.def) => + { + return false + } + (Some(_), Definition::TypeAlias(_)) => {} + // We looking at an assoc item of a trait definition, so reference all the + // corresponding assoc items belonging to this trait's trait implementations + (Some(hir::AssocItemContainer::Trait(_)), _) + if convert_to_def_in_trait(self.sema.db, def) == self.def => {} + _ => return false, } let FileRange { file_id, range } = self.sema.original_range(name.syntax()); let reference = FileReference { |