From a549da7e3ea66fd336de331c7c8d62b186051521 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 4 Mar 2020 12:05:14 +0100 Subject: Move SearchScope --- crates/ra_ide/src/references.rs | 2 +- crates/ra_ide/src/references/search_scope.rs | 144 -------------------------- crates/ra_ide_db/src/lib.rs | 1 + crates/ra_ide_db/src/search.rs | 147 +++++++++++++++++++++++++++ 4 files changed, 149 insertions(+), 145 deletions(-) create mode 100644 crates/ra_ide_db/src/search.rs diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 95468d434..6fd46c52d 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs @@ -31,7 +31,7 @@ use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeI pub(crate) use self::rename::rename; -pub use self::search_scope::SearchScope; +pub use ra_ide_db::search::SearchScope; #[derive(Debug, Clone)] pub struct ReferenceSearchResult { diff --git a/crates/ra_ide/src/references/search_scope.rs b/crates/ra_ide/src/references/search_scope.rs index d98c84d91..8b1378917 100644 --- a/crates/ra_ide/src/references/search_scope.rs +++ b/crates/ra_ide/src/references/search_scope.rs @@ -1,145 +1 @@ -//! Generally, `search_scope` returns files that might contain references for the element. -//! For `pub(crate)` things it's a crate, for `pub` things it's a crate and dependant crates. -//! In some cases, the location of the references is known to within a `TextRange`, -//! e.g. for things like local variables. -use std::mem; -use hir::{DefWithBody, HasSource, ModuleSource}; -use ra_db::{FileId, SourceDatabaseExt}; -use ra_prof::profile; -use ra_syntax::{AstNode, TextRange}; -use rustc_hash::FxHashMap; - -use ra_ide_db::RootDatabase; - -use super::Definition; - -pub struct SearchScope { - entries: FxHashMap>, -} - -impl SearchScope { - fn empty() -> SearchScope { - SearchScope { entries: FxHashMap::default() } - } - - pub(crate) fn for_def(def: &Definition, db: &RootDatabase) -> SearchScope { - let _p = profile("search_scope"); - let module = match def.module(db) { - Some(it) => it, - None => return SearchScope::empty(), - }; - let module_src = module.definition_source(db); - let file_id = module_src.file_id.original_file(db); - - if let Definition::Local(var) = def { - let range = match var.parent(db) { - DefWithBody::Function(f) => f.source(db).value.syntax().text_range(), - DefWithBody::Const(c) => c.source(db).value.syntax().text_range(), - DefWithBody::Static(s) => s.source(db).value.syntax().text_range(), - }; - let mut res = FxHashMap::default(); - res.insert(file_id, Some(range)); - return SearchScope::new(res); - } - - let vis = def.visibility(db).as_ref().map(|v| v.syntax().to_string()).unwrap_or_default(); - - if vis.as_str() == "pub(super)" { - if let Some(parent_module) = module.parent(db) { - let mut res = FxHashMap::default(); - let parent_src = parent_module.definition_source(db); - let file_id = parent_src.file_id.original_file(db); - - match parent_src.value { - ModuleSource::Module(m) => { - let range = Some(m.syntax().text_range()); - res.insert(file_id, range); - } - ModuleSource::SourceFile(_) => { - res.insert(file_id, None); - res.extend(parent_module.children(db).map(|m| { - let src = m.definition_source(db); - (src.file_id.original_file(db), None) - })); - } - } - return SearchScope::new(res); - } - } - - if vis.as_str() != "" { - let source_root_id = db.file_source_root(file_id); - let source_root = db.source_root(source_root_id); - let mut res = source_root.walk().map(|id| (id, None)).collect::>(); - - // FIXME: add "pub(in path)" - - if vis.as_str() == "pub(crate)" { - return SearchScope::new(res); - } - if vis.as_str() == "pub" { - let krate = module.krate(); - for rev_dep in krate.reverse_dependencies(db) { - let root_file = rev_dep.root_file(db); - let source_root_id = db.file_source_root(root_file); - let source_root = db.source_root(source_root_id); - res.extend(source_root.walk().map(|id| (id, None))); - } - return SearchScope::new(res); - } - } - - let mut res = FxHashMap::default(); - let range = match module_src.value { - ModuleSource::Module(m) => Some(m.syntax().text_range()), - ModuleSource::SourceFile(_) => None, - }; - res.insert(file_id, range); - SearchScope::new(res) - } - - fn new(entries: FxHashMap>) -> SearchScope { - SearchScope { entries } - } - pub fn single_file(file: FileId) -> SearchScope { - SearchScope::new(std::iter::once((file, None)).collect()) - } - pub(crate) fn intersection(&self, other: &SearchScope) -> SearchScope { - let (mut small, mut large) = (&self.entries, &other.entries); - if small.len() > large.len() { - mem::swap(&mut small, &mut large) - } - - let res = small - .iter() - .filter_map(|(file_id, r1)| { - let r2 = large.get(file_id)?; - let r = intersect_ranges(*r1, *r2)?; - Some((*file_id, r)) - }) - .collect(); - return SearchScope::new(res); - - fn intersect_ranges( - r1: Option, - r2: Option, - ) -> Option> { - match (r1, r2) { - (None, r) | (r, None) => Some(r), - (Some(r1), Some(r2)) => { - let r = r1.intersection(&r2)?; - Some(Some(r)) - } - } - } - } -} - -impl IntoIterator for SearchScope { - type Item = (FileId, Option); - type IntoIter = std::collections::hash_map::IntoIter>; - fn into_iter(self) -> Self::IntoIter { - self.entries.into_iter() - } -} diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs index aa312c140..79f48c9e3 100644 --- a/crates/ra_ide_db/src/lib.rs +++ b/crates/ra_ide_db/src/lib.rs @@ -9,6 +9,7 @@ pub mod feature_flags; pub mod symbol_index; pub mod change; pub mod defs; +pub mod search; pub mod imports_locator; mod wasm_shims; diff --git a/crates/ra_ide_db/src/search.rs b/crates/ra_ide_db/src/search.rs new file mode 100644 index 000000000..add63b870 --- /dev/null +++ b/crates/ra_ide_db/src/search.rs @@ -0,0 +1,147 @@ +//! Generally, `search_scope` returns files that might contain references for the element. +//! For `pub(crate)` things it's a crate, for `pub` things it's a crate and dependant crates. +//! In some cases, the location of the references is known to within a `TextRange`, +//! e.g. for things like local variables. +use std::mem; + +use hir::{DefWithBody, HasSource, ModuleSource}; +use ra_db::{FileId, SourceDatabaseExt}; +use ra_prof::profile; +use ra_syntax::{AstNode, TextRange}; +use rustc_hash::FxHashMap; + +use crate::{defs::Definition, RootDatabase}; + +pub struct SearchScope { + entries: FxHashMap>, +} + +impl SearchScope { + fn new(entries: FxHashMap>) -> SearchScope { + SearchScope { entries } + } + + pub fn empty() -> SearchScope { + SearchScope::new(FxHashMap::default()) + } + + pub fn single_file(file: FileId) -> SearchScope { + SearchScope::new(std::iter::once((file, None)).collect()) + } + + pub fn for_def(def: &Definition, db: &RootDatabase) -> SearchScope { + let _p = profile("search_scope"); + let module = match def.module(db) { + Some(it) => it, + None => return SearchScope::empty(), + }; + let module_src = module.definition_source(db); + let file_id = module_src.file_id.original_file(db); + + if let Definition::Local(var) = def { + let range = match var.parent(db) { + DefWithBody::Function(f) => f.source(db).value.syntax().text_range(), + DefWithBody::Const(c) => c.source(db).value.syntax().text_range(), + DefWithBody::Static(s) => s.source(db).value.syntax().text_range(), + }; + let mut res = FxHashMap::default(); + res.insert(file_id, Some(range)); + return SearchScope::new(res); + } + + let vis = def.visibility(db).as_ref().map(|v| v.syntax().to_string()).unwrap_or_default(); + + if vis.as_str() == "pub(super)" { + if let Some(parent_module) = module.parent(db) { + let mut res = FxHashMap::default(); + let parent_src = parent_module.definition_source(db); + let file_id = parent_src.file_id.original_file(db); + + match parent_src.value { + ModuleSource::Module(m) => { + let range = Some(m.syntax().text_range()); + res.insert(file_id, range); + } + ModuleSource::SourceFile(_) => { + res.insert(file_id, None); + res.extend(parent_module.children(db).map(|m| { + let src = m.definition_source(db); + (src.file_id.original_file(db), None) + })); + } + } + return SearchScope::new(res); + } + } + + if vis.as_str() != "" { + let source_root_id = db.file_source_root(file_id); + let source_root = db.source_root(source_root_id); + let mut res = source_root.walk().map(|id| (id, None)).collect::>(); + + // FIXME: add "pub(in path)" + + if vis.as_str() == "pub(crate)" { + return SearchScope::new(res); + } + if vis.as_str() == "pub" { + let krate = module.krate(); + for rev_dep in krate.reverse_dependencies(db) { + let root_file = rev_dep.root_file(db); + let source_root_id = db.file_source_root(root_file); + let source_root = db.source_root(source_root_id); + res.extend(source_root.walk().map(|id| (id, None))); + } + return SearchScope::new(res); + } + } + + let mut res = FxHashMap::default(); + let range = match module_src.value { + ModuleSource::Module(m) => Some(m.syntax().text_range()), + ModuleSource::SourceFile(_) => None, + }; + res.insert(file_id, range); + SearchScope::new(res) + } + + pub fn intersection(&self, other: &SearchScope) -> SearchScope { + let (mut small, mut large) = (&self.entries, &other.entries); + if small.len() > large.len() { + mem::swap(&mut small, &mut large) + } + + let res = small + .iter() + .filter_map(|(file_id, r1)| { + let r2 = large.get(file_id)?; + let r = intersect_ranges(*r1, *r2)?; + Some((*file_id, r)) + }) + .collect(); + + return SearchScope::new(res); + + fn intersect_ranges( + r1: Option, + r2: Option, + ) -> Option> { + match (r1, r2) { + (None, r) | (r, None) => Some(r), + (Some(r1), Some(r2)) => { + let r = r1.intersection(&r2)?; + Some(Some(r)) + } + } + } + } +} + +impl IntoIterator for SearchScope { + type Item = (FileId, Option); + type IntoIter = std::collections::hash_map::IntoIter>; + + fn into_iter(self) -> Self::IntoIter { + self.entries.into_iter() + } +} -- cgit v1.2.3 From 1874b6738ab4b849e5cbcb37fe2019192f950e5e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 4 Mar 2020 12:06:37 +0100 Subject: Move ReferenceKind --- crates/ra_ide/src/references.rs | 21 +-------------------- crates/ra_ide_db/src/search.rs | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 6fd46c52d..c797918b0 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs @@ -31,7 +31,7 @@ use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeI pub(crate) use self::rename::rename; -pub use ra_ide_db::search::SearchScope; +pub use ra_ide_db::search::{Reference, ReferenceAccess, ReferenceKind, SearchScope}; #[derive(Debug, Clone)] pub struct ReferenceSearchResult { @@ -46,25 +46,6 @@ pub struct Declaration { pub access: Option, } -#[derive(Debug, Clone)] -pub struct Reference { - pub file_range: FileRange, - pub kind: ReferenceKind, - pub access: Option, -} - -#[derive(Debug, Clone, PartialEq)] -pub enum ReferenceKind { - StructLiteral, - Other, -} - -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum ReferenceAccess { - Read, - Write, -} - impl ReferenceSearchResult { pub fn declaration(&self) -> &Declaration { &self.declaration diff --git a/crates/ra_ide_db/src/search.rs b/crates/ra_ide_db/src/search.rs index add63b870..ca458388c 100644 --- a/crates/ra_ide_db/src/search.rs +++ b/crates/ra_ide_db/src/search.rs @@ -5,13 +5,32 @@ use std::mem; use hir::{DefWithBody, HasSource, ModuleSource}; -use ra_db::{FileId, SourceDatabaseExt}; +use ra_db::{FileId, FileRange, SourceDatabaseExt}; use ra_prof::profile; use ra_syntax::{AstNode, TextRange}; use rustc_hash::FxHashMap; use crate::{defs::Definition, RootDatabase}; +#[derive(Debug, Clone)] +pub struct Reference { + pub file_range: FileRange, + pub kind: ReferenceKind, + pub access: Option, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum ReferenceKind { + StructLiteral, + Other, +} + +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum ReferenceAccess { + Read, + Write, +} + pub struct SearchScope { entries: FxHashMap>, } -- cgit v1.2.3 From 2638bec66cb05ff51a0804181a47546a0f55afbf Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 4 Mar 2020 12:07:44 +0100 Subject: Merge refs_to_def and process_def --- crates/ra_ide/src/references.rs | 49 +++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index c797918b0..ee065b6f9 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs @@ -127,6 +127,8 @@ pub(crate) fn find_refs_to_def( def: &Definition, search_scope: Option, ) -> Vec { + let _p = profile("find_refs_to_def"); + let search_scope = { let base = SearchScope::for_def(&def, db); match search_scope { @@ -140,38 +142,10 @@ pub(crate) fn find_refs_to_def( Some(it) => it.to_string(), }; - process_definition(db, def, name, search_scope) -} - -fn find_name( - sema: &Semantics, - syntax: &SyntaxNode, - position: FilePosition, - opt_name: Option, -) -> Option> { - if let Some(name) = opt_name { - let def = classify_name(sema, &name)?.definition(); - let range = name.syntax().text_range(); - return Some(RangeInfo::new(range, def)); - } - let name_ref = find_node_at_offset::(&syntax, position.offset)?; - let def = classify_name_ref(sema, &name_ref)?.definition(); - let range = name_ref.syntax().text_range(); - Some(RangeInfo::new(range, def)) -} - -fn process_definition( - db: &RootDatabase, - def: &Definition, - name: String, - scope: SearchScope, -) -> Vec { - let _p = profile("process_definition"); - let pat = name.as_str(); let mut refs = vec![]; - for (file_id, search_range) in scope { + for (file_id, search_range) in search_scope { let text = db.file_text(file_id); let search_range = search_range.unwrap_or(TextRange::offset_len(0.into(), TextUnit::of_str(&text))); @@ -226,6 +200,23 @@ fn process_definition( refs } +fn find_name( + sema: &Semantics, + syntax: &SyntaxNode, + position: FilePosition, + opt_name: Option, +) -> Option> { + if let Some(name) = opt_name { + let def = classify_name(sema, &name)?.definition(); + let range = name.syntax().text_range(); + return Some(RangeInfo::new(range, def)); + } + let name_ref = find_node_at_offset::(&syntax, position.offset)?; + let def = classify_name_ref(sema, &name_ref)?.definition(); + let range = name_ref.syntax().text_range(); + Some(RangeInfo::new(range, def)) +} + fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option { match def { Definition::Local(_) | Definition::StructField(_) => {} -- cgit v1.2.3 From f79719b8ae4d1929acaa940802a1e293f7dd7a6b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 4 Mar 2020 12:14:48 +0100 Subject: Move find_refs_to_def --- Cargo.lock | 1 + crates/ra_ide/Cargo.toml | 1 + crates/ra_ide/src/references.rs | 143 ++------------------------------------ crates/ra_ide_db/Cargo.toml | 1 + crates/ra_ide_db/src/search.rs | 149 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 153 insertions(+), 142 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87c288346..4ad436b2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1060,6 +1060,7 @@ version = "0.1.0" dependencies = [ "fst", "log", + "once_cell", "ra_db", "ra_hir", "ra_prof", diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml index 7625fc8c8..adee7c493 100644 --- a/crates/ra_ide/Cargo.toml +++ b/crates/ra_ide/Cargo.toml @@ -19,6 +19,7 @@ join_to_string = "0.1.3" log = "0.4.8" rustc-hash = "1.1.0" rand = { version = "0.7.3", features = ["small_rng"] } +# TODO: check if can remove once_cell = "1.3.1" ra_syntax = { path = "../ra_syntax" } diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index ee065b6f9..abecca2bb 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs @@ -13,8 +13,6 @@ mod rename; mod search_scope; use hir::Semantics; -use once_cell::unsync::Lazy; -use ra_db::SourceDatabaseExt; use ra_ide_db::{ defs::{classify_name, classify_name_ref, Definition}, RootDatabase, @@ -23,15 +21,16 @@ use ra_prof::profile; use ra_syntax::{ algo::find_node_at_offset, ast::{self, NameOwner}, - match_ast, AstNode, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset, + AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset, }; -use test_utils::tested_by; use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo}; pub(crate) use self::rename::rename; -pub use ra_ide_db::search::{Reference, ReferenceAccess, ReferenceKind, SearchScope}; +pub use ra_ide_db::search::{ + find_refs_to_def, Reference, ReferenceAccess, ReferenceKind, SearchScope, +}; #[derive(Debug, Clone)] pub struct ReferenceSearchResult { @@ -122,84 +121,6 @@ pub(crate) fn find_all_refs( Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })) } -pub(crate) fn find_refs_to_def( - db: &RootDatabase, - def: &Definition, - search_scope: Option, -) -> Vec { - let _p = profile("find_refs_to_def"); - - let search_scope = { - let base = SearchScope::for_def(&def, db); - match search_scope { - None => base, - Some(scope) => base.intersection(&scope), - } - }; - - let name = match def.name(db) { - None => return Vec::new(), - Some(it) => it.to_string(), - }; - - let pat = name.as_str(); - let mut refs = vec![]; - - for (file_id, search_range) in search_scope { - let text = db.file_text(file_id); - let search_range = - search_range.unwrap_or(TextRange::offset_len(0.into(), TextUnit::of_str(&text))); - - let sema = Semantics::new(db); - let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); - - for (idx, _) in text.match_indices(pat) { - let offset = TextUnit::from_usize(idx); - if !search_range.contains_inclusive(offset) { - tested_by!(search_filters_by_range); - continue; - } - - let name_ref = - if let Some(name_ref) = find_node_at_offset::(&tree, offset) { - name_ref - } else { - // Handle macro token cases - let token = match tree.token_at_offset(offset) { - TokenAtOffset::None => continue, - TokenAtOffset::Single(t) => t, - TokenAtOffset::Between(_, t) => t, - }; - let expanded = sema.descend_into_macros(token); - match ast::NameRef::cast(expanded.parent()) { - Some(name_ref) => name_ref, - _ => continue, - } - }; - - if let Some(d) = classify_name_ref(&sema, &name_ref) { - let d = d.definition(); - if &d == def { - let kind = - if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) { - ReferenceKind::StructLiteral - } else { - ReferenceKind::Other - }; - - let file_range = sema.original_range(name_ref.syntax()); - refs.push(Reference { - file_range, - kind, - access: reference_access(&d, &name_ref), - }); - } - } - } - } - refs -} - fn find_name( sema: &Semantics, syntax: &SyntaxNode, @@ -236,48 +157,6 @@ fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Optio None } -fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option { - // Only Locals and Fields have accesses for now. - match def { - Definition::Local(_) | Definition::StructField(_) => {} - _ => return None, - }; - - let mode = name_ref.syntax().ancestors().find_map(|node| { - match_ast! { - match (node) { - ast::BinExpr(expr) => { - if expr.op_kind()?.is_assignment() { - // If the variable or field ends on the LHS's end then it's a Write (covers fields and locals). - // FIXME: This is not terribly accurate. - if let Some(lhs) = expr.lhs() { - if lhs.syntax().text_range().end() == name_ref.syntax().text_range().end() { - return Some(ReferenceAccess::Write); - } - } - } - Some(ReferenceAccess::Read) - }, - _ => {None} - } - } - }); - - // Default Locals and Fields to read - mode.or(Some(ReferenceAccess::Read)) -} - -fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool { - name_ref - .syntax() - .ancestors() - .find_map(ast::RecordLit::cast) - .and_then(|l| l.path()) - .and_then(|p| p.segment()) - .map(|p| p.name_ref().as_ref() == Some(name_ref)) - .unwrap_or(false) -} - fn get_struct_def_name_for_struc_litetal_search( syntax: &SyntaxNode, position: FilePosition, @@ -296,20 +175,6 @@ fn get_struct_def_name_for_struc_litetal_search( None } -fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool { - name_ref - .syntax() - .ancestors() - .find_map(ast::CallExpr::cast) - .and_then(|c| match c.expr()? { - ast::Expr::PathExpr(p) => { - Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref)) - } - _ => None, - }) - .unwrap_or(false) -} - #[cfg(test)] mod tests { use test_utils::covers; diff --git a/crates/ra_ide_db/Cargo.toml b/crates/ra_ide_db/Cargo.toml index 7ff1a536e..52f0f23df 100644 --- a/crates/ra_ide_db/Cargo.toml +++ b/crates/ra_ide_db/Cargo.toml @@ -16,6 +16,7 @@ rayon = "1.3.0" fst = { version = "0.3.5", default-features = false } rustc-hash = "1.1.0" superslice = "1.0.0" +once_cell = "1.3.1" ra_syntax = { path = "../ra_syntax" } ra_text_edit = { path = "../ra_text_edit" } diff --git a/crates/ra_ide_db/src/search.rs b/crates/ra_ide_db/src/search.rs index ca458388c..efd43f4c1 100644 --- a/crates/ra_ide_db/src/search.rs +++ b/crates/ra_ide_db/src/search.rs @@ -4,13 +4,19 @@ //! e.g. for things like local variables. use std::mem; -use hir::{DefWithBody, HasSource, ModuleSource}; +use hir::{DefWithBody, HasSource, ModuleSource, Semantics}; +use once_cell::unsync::Lazy; use ra_db::{FileId, FileRange, SourceDatabaseExt}; use ra_prof::profile; -use ra_syntax::{AstNode, TextRange}; +use ra_syntax::{ + algo::find_node_at_offset, ast, match_ast, AstNode, TextRange, TextUnit, TokenAtOffset, +}; use rustc_hash::FxHashMap; -use crate::{defs::Definition, RootDatabase}; +use crate::{ + defs::{classify_name_ref, Definition}, + RootDatabase, +}; #[derive(Debug, Clone)] pub struct Reference { @@ -164,3 +170,140 @@ impl IntoIterator for SearchScope { self.entries.into_iter() } } + +pub fn find_refs_to_def( + db: &RootDatabase, + def: &Definition, + search_scope: Option, +) -> Vec { + let _p = profile("find_refs_to_def"); + + let search_scope = { + let base = SearchScope::for_def(&def, db); + match search_scope { + None => base, + Some(scope) => base.intersection(&scope), + } + }; + + let name = match def.name(db) { + None => return Vec::new(), + Some(it) => it.to_string(), + }; + + let pat = name.as_str(); + let mut refs = vec![]; + + for (file_id, search_range) in search_scope { + let text = db.file_text(file_id); + let search_range = + search_range.unwrap_or(TextRange::offset_len(0.into(), TextUnit::of_str(&text))); + + let sema = Semantics::new(db); + let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); + + for (idx, _) in text.match_indices(pat) { + let offset = TextUnit::from_usize(idx); + if !search_range.contains_inclusive(offset) { + // tested_by!(search_filters_by_range); + continue; + } + + let name_ref = + if let Some(name_ref) = find_node_at_offset::(&tree, offset) { + name_ref + } else { + // Handle macro token cases + let token = match tree.token_at_offset(offset) { + TokenAtOffset::None => continue, + TokenAtOffset::Single(t) => t, + TokenAtOffset::Between(_, t) => t, + }; + let expanded = sema.descend_into_macros(token); + match ast::NameRef::cast(expanded.parent()) { + Some(name_ref) => name_ref, + _ => continue, + } + }; + + // FIXME: reuse sb + // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098 + + if let Some(d) = classify_name_ref(&sema, &name_ref) { + let d = d.definition(); + if &d == def { + let kind = + if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) { + ReferenceKind::StructLiteral + } else { + ReferenceKind::Other + }; + + let file_range = sema.original_range(name_ref.syntax()); + refs.push(Reference { + file_range, + kind, + access: reference_access(&d, &name_ref), + }); + } + } + } + } + refs +} + +fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option { + // Only Locals and Fields have accesses for now. + match def { + Definition::Local(_) | Definition::StructField(_) => {} + _ => return None, + }; + + let mode = name_ref.syntax().ancestors().find_map(|node| { + match_ast! { + match (node) { + ast::BinExpr(expr) => { + if expr.op_kind()?.is_assignment() { + // If the variable or field ends on the LHS's end then it's a Write (covers fields and locals). + // FIXME: This is not terribly accurate. + if let Some(lhs) = expr.lhs() { + if lhs.syntax().text_range().end() == name_ref.syntax().text_range().end() { + return Some(ReferenceAccess::Write); + } + } + } + Some(ReferenceAccess::Read) + }, + _ => {None} + } + } + }); + + // Default Locals and Fields to read + mode.or(Some(ReferenceAccess::Read)) +} + +fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool { + name_ref + .syntax() + .ancestors() + .find_map(ast::CallExpr::cast) + .and_then(|c| match c.expr()? { + ast::Expr::PathExpr(p) => { + Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref)) + } + _ => None, + }) + .unwrap_or(false) +} + +fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool { + name_ref + .syntax() + .ancestors() + .find_map(ast::RecordLit::cast) + .and_then(|l| l.path()) + .and_then(|p| p.segment()) + .map(|p| p.name_ref().as_ref() == Some(name_ref)) + .unwrap_or(false) +} -- cgit v1.2.3 From e839d793ae4afd2a9ca2df1f6b4b3c56dce12ab8 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 4 Mar 2020 12:15:01 +0100 Subject: Drop one once_cell dep --- Cargo.lock | 1 - crates/ra_ide/Cargo.toml | 2 -- 2 files changed, 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4ad436b2c..316dae053 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1039,7 +1039,6 @@ dependencies = [ "itertools", "join_to_string", "log", - "once_cell", "ra_assists", "ra_cfg", "ra_db", diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml index adee7c493..410d8de62 100644 --- a/crates/ra_ide/Cargo.toml +++ b/crates/ra_ide/Cargo.toml @@ -19,8 +19,6 @@ join_to_string = "0.1.3" log = "0.4.8" rustc-hash = "1.1.0" rand = { version = "0.7.3", features = ["small_rng"] } -# TODO: check if can remove -once_cell = "1.3.1" ra_syntax = { path = "../ra_syntax" } ra_text_edit = { path = "../ra_text_edit" } -- cgit v1.2.3 From 994000b5ddfd5bb3b7dc7d66aabe02a0f337770f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 4 Mar 2020 12:17:41 +0100 Subject: Use method instead of a free function --- crates/ra_ide/src/references.rs | 7 +-- crates/ra_ide_db/src/search.rs | 125 ++++++++++++++++++++-------------------- 2 files changed, 67 insertions(+), 65 deletions(-) diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index abecca2bb..6440707d7 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs @@ -28,9 +28,7 @@ use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeI pub(crate) use self::rename::rename; -pub use ra_ide_db::search::{ - find_refs_to_def, Reference, ReferenceAccess, ReferenceKind, SearchScope, -}; +pub use ra_ide_db::search::{Reference, ReferenceAccess, ReferenceKind, SearchScope}; #[derive(Debug, Clone)] pub struct ReferenceSearchResult { @@ -105,7 +103,8 @@ pub(crate) fn find_all_refs( let RangeInfo { range, info: def } = find_name(&sema, &syntax, position, opt_name)?; - let references = find_refs_to_def(db, &def, search_scope) + let references = def + .find_usages(db, search_scope) .into_iter() .filter(|r| search_kind == ReferenceKind::Other || search_kind == r.kind) .collect(); diff --git a/crates/ra_ide_db/src/search.rs b/crates/ra_ide_db/src/search.rs index efd43f4c1..23dd278b5 100644 --- a/crates/ra_ide_db/src/search.rs +++ b/crates/ra_ide_db/src/search.rs @@ -171,85 +171,88 @@ impl IntoIterator for SearchScope { } } -pub fn find_refs_to_def( - db: &RootDatabase, - def: &Definition, - search_scope: Option, -) -> Vec { - let _p = profile("find_refs_to_def"); - - let search_scope = { - let base = SearchScope::for_def(&def, db); - match search_scope { - None => base, - Some(scope) => base.intersection(&scope), - } - }; +impl Definition { + pub fn find_usages( + &self, + db: &RootDatabase, + search_scope: Option, + ) -> Vec { + let _p = profile("Definition::find_usages"); + + let search_scope = { + let base = SearchScope::for_def(self, db); + match search_scope { + None => base, + Some(scope) => base.intersection(&scope), + } + }; - let name = match def.name(db) { - None => return Vec::new(), - Some(it) => it.to_string(), - }; + let name = match self.name(db) { + None => return Vec::new(), + Some(it) => it.to_string(), + }; - let pat = name.as_str(); - let mut refs = vec![]; + let pat = name.as_str(); + let mut refs = vec![]; - for (file_id, search_range) in search_scope { - let text = db.file_text(file_id); - let search_range = - search_range.unwrap_or(TextRange::offset_len(0.into(), TextUnit::of_str(&text))); + for (file_id, search_range) in search_scope { + let text = db.file_text(file_id); + let search_range = + search_range.unwrap_or(TextRange::offset_len(0.into(), TextUnit::of_str(&text))); - let sema = Semantics::new(db); - let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); + let sema = Semantics::new(db); + let tree = Lazy::new(|| sema.parse(file_id).syntax().clone()); - for (idx, _) in text.match_indices(pat) { - let offset = TextUnit::from_usize(idx); - if !search_range.contains_inclusive(offset) { - // tested_by!(search_filters_by_range); - continue; - } + for (idx, _) in text.match_indices(pat) { + let offset = TextUnit::from_usize(idx); + if !search_range.contains_inclusive(offset) { + // tested_by!(search_filters_by_range); + continue; + } - let name_ref = - if let Some(name_ref) = find_node_at_offset::(&tree, offset) { - name_ref - } else { - // Handle macro token cases - let token = match tree.token_at_offset(offset) { - TokenAtOffset::None => continue, - TokenAtOffset::Single(t) => t, - TokenAtOffset::Between(_, t) => t, + let name_ref = + if let Some(name_ref) = find_node_at_offset::(&tree, offset) { + name_ref + } else { + // Handle macro token cases + let token = match tree.token_at_offset(offset) { + TokenAtOffset::None => continue, + TokenAtOffset::Single(t) => t, + TokenAtOffset::Between(_, t) => t, + }; + let expanded = sema.descend_into_macros(token); + match ast::NameRef::cast(expanded.parent()) { + Some(name_ref) => name_ref, + _ => continue, + } }; - let expanded = sema.descend_into_macros(token); - match ast::NameRef::cast(expanded.parent()) { - Some(name_ref) => name_ref, - _ => continue, - } - }; - // FIXME: reuse sb - // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098 + // FIXME: reuse sb + // See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098 - if let Some(d) = classify_name_ref(&sema, &name_ref) { - let d = d.definition(); - if &d == def { - let kind = - if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) { + if let Some(d) = classify_name_ref(&sema, &name_ref) { + let d = d.definition(); + if &d == self { + let kind = if is_record_lit_name_ref(&name_ref) + || is_call_expr_name_ref(&name_ref) + { ReferenceKind::StructLiteral } else { ReferenceKind::Other }; - let file_range = sema.original_range(name_ref.syntax()); - refs.push(Reference { - file_range, - kind, - access: reference_access(&d, &name_ref), - }); + let file_range = sema.original_range(name_ref.syntax()); + refs.push(Reference { + file_range, + kind, + access: reference_access(&d, &name_ref), + }); + } } } } + refs } - refs } fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option { -- cgit v1.2.3 From 072ec1a8ae7271324a907ea082521226a6f3d1d4 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 4 Mar 2020 12:20:32 +0100 Subject: Fix tests --- crates/ra_ide/src/references.rs | 30 ++++++++++++++---------------- crates/ra_ide_db/src/marks.rs | 1 + crates/ra_ide_db/src/search.rs | 3 ++- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 6440707d7..8eba13e99 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs @@ -287,7 +287,7 @@ mod tests { #[test] fn search_filters_by_range() { - covers!(search_filters_by_range); + covers!(ra_ide_db::search_filters_by_range); let code = r#" fn foo() { let spam<|> = 92; @@ -603,7 +603,10 @@ mod tests { fn check_result(res: ReferenceSearchResult, expected_decl: &str, expected_refs: &[&str]) { res.declaration().assert_match(expected_decl); assert_eq!(res.references.len(), expected_refs.len()); - res.references().iter().enumerate().for_each(|(i, r)| r.assert_match(expected_refs[i])); + res.references() + .iter() + .enumerate() + .for_each(|(i, r)| ref_assert_match(r, expected_refs[i])); } impl Declaration { @@ -621,21 +624,16 @@ mod tests { } } - impl Reference { - fn debug_render(&self) -> String { - let mut s = format!( - "{:?} {:?} {:?}", - self.file_range.file_id, self.file_range.range, self.kind - ); - if let Some(access) = self.access { - s.push_str(&format!(" {:?}", access)); - } - s + fn ref_debug_render(r: &Reference) -> String { + let mut s = format!("{:?} {:?} {:?}", r.file_range.file_id, r.file_range.range, r.kind); + if let Some(access) = r.access { + s.push_str(&format!(" {:?}", access)); } + s + } - fn assert_match(&self, expected: &str) { - let actual = self.debug_render(); - test_utils::assert_eq_text!(expected.trim(), actual.trim(),); - } + fn ref_assert_match(r: &Reference, expected: &str) { + let actual = ref_debug_render(r); + test_utils::assert_eq_text!(expected.trim(), actual.trim(),); } } diff --git a/crates/ra_ide_db/src/marks.rs b/crates/ra_ide_db/src/marks.rs index d088fa257..4f0a22af0 100644 --- a/crates/ra_ide_db/src/marks.rs +++ b/crates/ra_ide_db/src/marks.rs @@ -6,4 +6,5 @@ test_utils::marks![ goto_def_for_fields goto_def_for_record_fields goto_def_for_field_init_shorthand + search_filters_by_range ]; diff --git a/crates/ra_ide_db/src/search.rs b/crates/ra_ide_db/src/search.rs index 23dd278b5..0963ea257 100644 --- a/crates/ra_ide_db/src/search.rs +++ b/crates/ra_ide_db/src/search.rs @@ -12,6 +12,7 @@ use ra_syntax::{ algo::find_node_at_offset, ast, match_ast, AstNode, TextRange, TextUnit, TokenAtOffset, }; use rustc_hash::FxHashMap; +use test_utils::tested_by; use crate::{ defs::{classify_name_ref, Definition}, @@ -206,7 +207,7 @@ impl Definition { for (idx, _) in text.match_indices(pat) { let offset = TextUnit::from_usize(idx); if !search_range.contains_inclusive(offset) { - // tested_by!(search_filters_by_range); + tested_by!(search_filters_by_range; force); continue; } -- cgit v1.2.3 From 98d68fa6beec87020b89d01c060288794dc948f1 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 4 Mar 2020 12:46:40 +0100 Subject: Cleanup API --- crates/ra_ide/src/lib.rs | 5 +- crates/ra_ide/src/references.rs | 4 +- crates/ra_ide/src/references/search_scope.rs | 1 - crates/ra_ide_db/src/search.rs | 94 ++++++++++++++-------------- 4 files changed, 51 insertions(+), 53 deletions(-) delete mode 100644 crates/ra_ide/src/references/search_scope.rs diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index f61028f78..5a41f702e 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -68,9 +68,7 @@ pub use crate::{ folding_ranges::{Fold, FoldKind}, hover::HoverResult, inlay_hints::{InlayHint, InlayKind}, - references::{ - Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult, SearchScope, - }, + references::{Declaration, Reference, ReferenceAccess, ReferenceKind, ReferenceSearchResult}, runnables::{Runnable, RunnableKind, TestId}, source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, ssr::SsrError, @@ -88,6 +86,7 @@ pub use ra_ide_db::{ feature_flags::FeatureFlags, line_index::{LineCol, LineIndex}, line_index_utils::translate_offset_with_edit, + search::SearchScope, symbol_index::Query, RootDatabase, }; diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index 8eba13e99..6e2bf4ded 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs @@ -10,11 +10,11 @@ //! resolved to the search element definition, we get a reference. mod rename; -mod search_scope; use hir::Semantics; use ra_ide_db::{ defs::{classify_name, classify_name_ref, Definition}, + search::SearchScope, RootDatabase, }; use ra_prof::profile; @@ -28,7 +28,7 @@ use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeI pub(crate) use self::rename::rename; -pub use ra_ide_db::search::{Reference, ReferenceAccess, ReferenceKind, SearchScope}; +pub use ra_ide_db::search::{Reference, ReferenceAccess, ReferenceKind}; #[derive(Debug, Clone)] pub struct ReferenceSearchResult { diff --git a/crates/ra_ide/src/references/search_scope.rs b/crates/ra_ide/src/references/search_scope.rs deleted file mode 100644 index 8b1378917..000000000 --- a/crates/ra_ide/src/references/search_scope.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/ra_ide_db/src/search.rs b/crates/ra_ide_db/src/search.rs index 0963ea257..7bd99d4cc 100644 --- a/crates/ra_ide_db/src/search.rs +++ b/crates/ra_ide_db/src/search.rs @@ -55,16 +55,58 @@ impl SearchScope { SearchScope::new(std::iter::once((file, None)).collect()) } - pub fn for_def(def: &Definition, db: &RootDatabase) -> SearchScope { + pub fn intersection(&self, other: &SearchScope) -> SearchScope { + let (mut small, mut large) = (&self.entries, &other.entries); + if small.len() > large.len() { + mem::swap(&mut small, &mut large) + } + + let res = small + .iter() + .filter_map(|(file_id, r1)| { + let r2 = large.get(file_id)?; + let r = intersect_ranges(*r1, *r2)?; + Some((*file_id, r)) + }) + .collect(); + + return SearchScope::new(res); + + fn intersect_ranges( + r1: Option, + r2: Option, + ) -> Option> { + match (r1, r2) { + (None, r) | (r, None) => Some(r), + (Some(r1), Some(r2)) => { + let r = r1.intersection(&r2)?; + Some(Some(r)) + } + } + } + } +} + +impl IntoIterator for SearchScope { + type Item = (FileId, Option); + type IntoIter = std::collections::hash_map::IntoIter>; + + fn into_iter(self) -> Self::IntoIter { + self.entries.into_iter() + } +} + +impl Definition { + fn search_scope(&self, db: &RootDatabase) -> SearchScope { let _p = profile("search_scope"); - let module = match def.module(db) { + let module = match self.module(db) { Some(it) => it, None => return SearchScope::empty(), }; let module_src = module.definition_source(db); let file_id = module_src.file_id.original_file(db); - if let Definition::Local(var) = def { + if let Definition::Local(var) = self { let range = match var.parent(db) { DefWithBody::Function(f) => f.source(db).value.syntax().text_range(), DefWithBody::Const(c) => c.source(db).value.syntax().text_range(), @@ -75,7 +117,7 @@ impl SearchScope { return SearchScope::new(res); } - let vis = def.visibility(db).as_ref().map(|v| v.syntax().to_string()).unwrap_or_default(); + let vis = self.visibility(db).as_ref().map(|v| v.syntax().to_string()).unwrap_or_default(); if vis.as_str() == "pub(super)" { if let Some(parent_module) = module.parent(db) { @@ -131,48 +173,6 @@ impl SearchScope { SearchScope::new(res) } - pub fn intersection(&self, other: &SearchScope) -> SearchScope { - let (mut small, mut large) = (&self.entries, &other.entries); - if small.len() > large.len() { - mem::swap(&mut small, &mut large) - } - - let res = small - .iter() - .filter_map(|(file_id, r1)| { - let r2 = large.get(file_id)?; - let r = intersect_ranges(*r1, *r2)?; - Some((*file_id, r)) - }) - .collect(); - - return SearchScope::new(res); - - fn intersect_ranges( - r1: Option, - r2: Option, - ) -> Option> { - match (r1, r2) { - (None, r) | (r, None) => Some(r), - (Some(r1), Some(r2)) => { - let r = r1.intersection(&r2)?; - Some(Some(r)) - } - } - } - } -} - -impl IntoIterator for SearchScope { - type Item = (FileId, Option); - type IntoIter = std::collections::hash_map::IntoIter>; - - fn into_iter(self) -> Self::IntoIter { - self.entries.into_iter() - } -} - -impl Definition { pub fn find_usages( &self, db: &RootDatabase, @@ -181,7 +181,7 @@ impl Definition { let _p = profile("Definition::find_usages"); let search_scope = { - let base = SearchScope::for_def(self, db); + let base = self.search_scope(db); match search_scope { None => base, Some(scope) => base.intersection(&scope), -- cgit v1.2.3 From 4f50a3718762c9272b1929162ce62415a75eec8f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 4 Mar 2020 12:48:50 +0100 Subject: Fix docs --- crates/ra_ide_db/src/search.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/ra_ide_db/src/search.rs b/crates/ra_ide_db/src/search.rs index 7bd99d4cc..6f198df04 100644 --- a/crates/ra_ide_db/src/search.rs +++ b/crates/ra_ide_db/src/search.rs @@ -1,7 +1,9 @@ -//! Generally, `search_scope` returns files that might contain references for the element. -//! For `pub(crate)` things it's a crate, for `pub` things it's a crate and dependant crates. -//! In some cases, the location of the references is known to within a `TextRange`, -//! e.g. for things like local variables. +//! Implementation of find-usages functionality. +//! +//! It is based on the standard ide trick: first, we run a fast text search to +//! get a super-set of matches. Then, we we confirm each match using precise +//! name resolution. + use std::mem; use hir::{DefWithBody, HasSource, ModuleSource, Semantics}; @@ -38,6 +40,10 @@ pub enum ReferenceAccess { Write, } +/// Generally, `search_scope` returns files that might contain references for the element. +/// For `pub(crate)` things it's a crate, for `pub` things it's a crate and dependant crates. +/// In some cases, the location of the references is known to within a `TextRange`, +/// e.g. for things like local variables. pub struct SearchScope { entries: FxHashMap>, } -- cgit v1.2.3