From f79719b8ae4d1929acaa940802a1e293f7dd7a6b Mon Sep 17 00:00:00 2001
From: Aleksey Kladov <aleksey.kladov@gmail.com>
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<SearchScope>,
-) -> Vec<Reference> {
-    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::<ast::NameRef>(&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<RootDatabase>,
     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<ReferenceAccess> {
-    // 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<SearchScope>,
+) -> Vec<Reference> {
+    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::<ast::NameRef>(&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<ReferenceAccess> {
+    // 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