From 81b0976187d73eba4f9b14d8a0b8539ab8f06dcd Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 19 Aug 2020 18:58:48 +0200 Subject: [PATCH] Future proof find-usages API We might want to provide more efficient impls for check if usages exist, limiting the search, filtering and cancellation, so let's violate YAGNI a bit here. --- .../extract_struct_from_enum_variant.rs | 2 +- .../src/handlers/inline_local_variable.rs | 2 +- crates/ide/src/references.rs | 4 +- crates/ide_db/src/search.rs | 48 ++++++++++++++----- crates/ssr/src/search.rs | 2 +- 5 files changed, 41 insertions(+), 17 deletions(-) diff --git a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs index d62e06b4a81..c1124b9e2fb 100644 --- a/crates/assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/assists/src/handlers/extract_struct_from_enum_variant.rs @@ -53,7 +53,7 @@ pub(crate) fn extract_struct_from_enum_variant( target, |builder| { let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir)); - let res = definition.find_usages(&ctx.sema, None); + let res = definition.usages(&ctx.sema).all(); let start_offset = variant.parent_enum().syntax().text_range().start(); let mut visited_modules_set = FxHashSet::default(); visited_modules_set.insert(current_module); diff --git a/crates/assists/src/handlers/inline_local_variable.rs b/crates/assists/src/handlers/inline_local_variable.rs index 2b52b333b88..164bbce86fc 100644 --- a/crates/assists/src/handlers/inline_local_variable.rs +++ b/crates/assists/src/handlers/inline_local_variable.rs @@ -44,7 +44,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O let def = ctx.sema.to_def(&bind_pat)?; let def = Definition::Local(def); - let refs = def.find_usages(&ctx.sema, None); + let refs = def.usages(&ctx.sema).all(); if refs.is_empty() { mark::hit!(test_not_applicable_if_variable_unused); return None; diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index 0a76ec6b430..722c8f406e3 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -106,7 +106,9 @@ pub(crate) fn find_all_refs( let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; let references = def - .find_usages(sema, search_scope) + .usages(sema) + .set_scope(search_scope) + .all() .into_iter() .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) .collect(); diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs index b9360bf1291..ce7631c6927 100644 --- a/crates/ide_db/src/search.rs +++ b/crates/ide_db/src/search.rs @@ -181,22 +181,44 @@ impl Definition { SearchScope::new(res) } - pub fn find_usages( - &self, - sema: &Semantics, - search_scope: Option, - ) -> Vec { + pub fn usages<'a>(&'a self, sema: &'a Semantics) -> FindUsages<'a> { + FindUsages { def: self, sema, scope: None } + } +} + +pub struct FindUsages<'a> { + def: &'a Definition, + sema: &'a Semantics<'a, RootDatabase>, + scope: Option, +} + +impl<'a> FindUsages<'a> { + pub fn in_scope(self, scope: SearchScope) -> FindUsages<'a> { + self.set_scope(Some(scope)) + } + pub fn set_scope(mut self, scope: Option) -> FindUsages<'a> { + assert!(self.scope.is_none()); + self.scope = scope; + self + } + + pub fn at_least_one(self) -> bool { + self.all().is_empty() + } + + pub fn all(self) -> Vec { let _p = profile::span("Definition::find_usages"); + let sema = self.sema; let search_scope = { - let base = self.search_scope(sema.db); - match search_scope { + let base = self.def.search_scope(sema.db); + match self.scope { None => base, Some(scope) => base.intersection(&scope), } }; - let name = match self.name(sema.db) { + let name = match self.def.name(sema.db) { None => return Vec::new(), Some(it) => it.to_string(), }; @@ -225,7 +247,7 @@ impl Definition { }; match classify_name_ref(&sema, &name_ref) { - Some(NameRefClass::Definition(def)) if &def == self => { + Some(NameRefClass::Definition(def)) if &def == self.def => { let kind = if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) { @@ -242,14 +264,14 @@ impl Definition { }); } Some(NameRefClass::FieldShorthand { local, field }) => { - match self { - Definition::Field(_) if &field == self => refs.push(Reference { - file_range: sema.original_range(name_ref.syntax()), + match self.def { + Definition::Field(_) if &field == self.def => refs.push(Reference { + file_range: self.sema.original_range(name_ref.syntax()), kind: ReferenceKind::FieldShorthandForField, access: reference_access(&field, &name_ref), }), Definition::Local(l) if &local == l => refs.push(Reference { - file_range: sema.original_range(name_ref.syntax()), + file_range: self.sema.original_range(name_ref.syntax()), kind: ReferenceKind::FieldShorthandForLocal, access: reference_access(&Definition::Local(local), &name_ref), }), diff --git a/crates/ssr/src/search.rs b/crates/ssr/src/search.rs index 8509cfa4dea..a595fd269c9 100644 --- a/crates/ssr/src/search.rs +++ b/crates/ssr/src/search.rs @@ -114,7 +114,7 @@ impl<'db> MatchFinder<'db> { // cache miss. This is a limitation of NLL and is fixed with Polonius. For now we do two // lookups in the case of a cache hit. if usage_cache.find(&definition).is_none() { - let usages = definition.find_usages(&self.sema, Some(self.search_scope())); + let usages = definition.usages(&self.sema).in_scope(self.search_scope()).all(); usage_cache.usages.push((definition, usages)); return &usage_cache.usages.last().unwrap().1; }