Skip to content

Commit f79719b

Browse files
committed
Move find_refs_to_def
1 parent 2638bec commit f79719b

File tree

5 files changed

+153
-142
lines changed

5 files changed

+153
-142
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ra_ide/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ join_to_string = "0.1.3"
1919
log = "0.4.8"
2020
rustc-hash = "1.1.0"
2121
rand = { version = "0.7.3", features = ["small_rng"] }
22+
# TODO: check if can remove
2223
once_cell = "1.3.1"
2324

2425
ra_syntax = { path = "../ra_syntax" }

crates/ra_ide/src/references.rs

Lines changed: 4 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ mod rename;
1313
mod search_scope;
1414

1515
use hir::Semantics;
16-
use once_cell::unsync::Lazy;
17-
use ra_db::SourceDatabaseExt;
1816
use ra_ide_db::{
1917
defs::{classify_name, classify_name_ref, Definition},
2018
RootDatabase,
@@ -23,15 +21,16 @@ use ra_prof::profile;
2321
use ra_syntax::{
2422
algo::find_node_at_offset,
2523
ast::{self, NameOwner},
26-
match_ast, AstNode, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset,
24+
AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset,
2725
};
28-
use test_utils::tested_by;
2926

3027
use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo};
3128

3229
pub(crate) use self::rename::rename;
3330

34-
pub use ra_ide_db::search::{Reference, ReferenceAccess, ReferenceKind, SearchScope};
31+
pub use ra_ide_db::search::{
32+
find_refs_to_def, Reference, ReferenceAccess, ReferenceKind, SearchScope,
33+
};
3534

3635
#[derive(Debug, Clone)]
3736
pub struct ReferenceSearchResult {
@@ -122,84 +121,6 @@ pub(crate) fn find_all_refs(
122121
Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references }))
123122
}
124123

125-
pub(crate) fn find_refs_to_def(
126-
db: &RootDatabase,
127-
def: &Definition,
128-
search_scope: Option<SearchScope>,
129-
) -> Vec<Reference> {
130-
let _p = profile("find_refs_to_def");
131-
132-
let search_scope = {
133-
let base = SearchScope::for_def(&def, db);
134-
match search_scope {
135-
None => base,
136-
Some(scope) => base.intersection(&scope),
137-
}
138-
};
139-
140-
let name = match def.name(db) {
141-
None => return Vec::new(),
142-
Some(it) => it.to_string(),
143-
};
144-
145-
let pat = name.as_str();
146-
let mut refs = vec![];
147-
148-
for (file_id, search_range) in search_scope {
149-
let text = db.file_text(file_id);
150-
let search_range =
151-
search_range.unwrap_or(TextRange::offset_len(0.into(), TextUnit::of_str(&text)));
152-
153-
let sema = Semantics::new(db);
154-
let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
155-
156-
for (idx, _) in text.match_indices(pat) {
157-
let offset = TextUnit::from_usize(idx);
158-
if !search_range.contains_inclusive(offset) {
159-
tested_by!(search_filters_by_range);
160-
continue;
161-
}
162-
163-
let name_ref =
164-
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&tree, offset) {
165-
name_ref
166-
} else {
167-
// Handle macro token cases
168-
let token = match tree.token_at_offset(offset) {
169-
TokenAtOffset::None => continue,
170-
TokenAtOffset::Single(t) => t,
171-
TokenAtOffset::Between(_, t) => t,
172-
};
173-
let expanded = sema.descend_into_macros(token);
174-
match ast::NameRef::cast(expanded.parent()) {
175-
Some(name_ref) => name_ref,
176-
_ => continue,
177-
}
178-
};
179-
180-
if let Some(d) = classify_name_ref(&sema, &name_ref) {
181-
let d = d.definition();
182-
if &d == def {
183-
let kind =
184-
if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) {
185-
ReferenceKind::StructLiteral
186-
} else {
187-
ReferenceKind::Other
188-
};
189-
190-
let file_range = sema.original_range(name_ref.syntax());
191-
refs.push(Reference {
192-
file_range,
193-
kind,
194-
access: reference_access(&d, &name_ref),
195-
});
196-
}
197-
}
198-
}
199-
}
200-
refs
201-
}
202-
203124
fn find_name(
204125
sema: &Semantics<RootDatabase>,
205126
syntax: &SyntaxNode,
@@ -236,48 +157,6 @@ fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Optio
236157
None
237158
}
238159

239-
fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<ReferenceAccess> {
240-
// Only Locals and Fields have accesses for now.
241-
match def {
242-
Definition::Local(_) | Definition::StructField(_) => {}
243-
_ => return None,
244-
};
245-
246-
let mode = name_ref.syntax().ancestors().find_map(|node| {
247-
match_ast! {
248-
match (node) {
249-
ast::BinExpr(expr) => {
250-
if expr.op_kind()?.is_assignment() {
251-
// If the variable or field ends on the LHS's end then it's a Write (covers fields and locals).
252-
// FIXME: This is not terribly accurate.
253-
if let Some(lhs) = expr.lhs() {
254-
if lhs.syntax().text_range().end() == name_ref.syntax().text_range().end() {
255-
return Some(ReferenceAccess::Write);
256-
}
257-
}
258-
}
259-
Some(ReferenceAccess::Read)
260-
},
261-
_ => {None}
262-
}
263-
}
264-
});
265-
266-
// Default Locals and Fields to read
267-
mode.or(Some(ReferenceAccess::Read))
268-
}
269-
270-
fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool {
271-
name_ref
272-
.syntax()
273-
.ancestors()
274-
.find_map(ast::RecordLit::cast)
275-
.and_then(|l| l.path())
276-
.and_then(|p| p.segment())
277-
.map(|p| p.name_ref().as_ref() == Some(name_ref))
278-
.unwrap_or(false)
279-
}
280-
281160
fn get_struct_def_name_for_struc_litetal_search(
282161
syntax: &SyntaxNode,
283162
position: FilePosition,
@@ -296,20 +175,6 @@ fn get_struct_def_name_for_struc_litetal_search(
296175
None
297176
}
298177

299-
fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool {
300-
name_ref
301-
.syntax()
302-
.ancestors()
303-
.find_map(ast::CallExpr::cast)
304-
.and_then(|c| match c.expr()? {
305-
ast::Expr::PathExpr(p) => {
306-
Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref))
307-
}
308-
_ => None,
309-
})
310-
.unwrap_or(false)
311-
}
312-
313178
#[cfg(test)]
314179
mod tests {
315180
use test_utils::covers;

crates/ra_ide_db/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ rayon = "1.3.0"
1616
fst = { version = "0.3.5", default-features = false }
1717
rustc-hash = "1.1.0"
1818
superslice = "1.0.0"
19+
once_cell = "1.3.1"
1920

2021
ra_syntax = { path = "../ra_syntax" }
2122
ra_text_edit = { path = "../ra_text_edit" }

crates/ra_ide_db/src/search.rs

Lines changed: 146 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,19 @@
44
//! e.g. for things like local variables.
55
use std::mem;
66

7-
use hir::{DefWithBody, HasSource, ModuleSource};
7+
use hir::{DefWithBody, HasSource, ModuleSource, Semantics};
8+
use once_cell::unsync::Lazy;
89
use ra_db::{FileId, FileRange, SourceDatabaseExt};
910
use ra_prof::profile;
10-
use ra_syntax::{AstNode, TextRange};
11+
use ra_syntax::{
12+
algo::find_node_at_offset, ast, match_ast, AstNode, TextRange, TextUnit, TokenAtOffset,
13+
};
1114
use rustc_hash::FxHashMap;
1215

13-
use crate::{defs::Definition, RootDatabase};
16+
use crate::{
17+
defs::{classify_name_ref, Definition},
18+
RootDatabase,
19+
};
1420

1521
#[derive(Debug, Clone)]
1622
pub struct Reference {
@@ -164,3 +170,140 @@ impl IntoIterator for SearchScope {
164170
self.entries.into_iter()
165171
}
166172
}
173+
174+
pub fn find_refs_to_def(
175+
db: &RootDatabase,
176+
def: &Definition,
177+
search_scope: Option<SearchScope>,
178+
) -> Vec<Reference> {
179+
let _p = profile("find_refs_to_def");
180+
181+
let search_scope = {
182+
let base = SearchScope::for_def(&def, db);
183+
match search_scope {
184+
None => base,
185+
Some(scope) => base.intersection(&scope),
186+
}
187+
};
188+
189+
let name = match def.name(db) {
190+
None => return Vec::new(),
191+
Some(it) => it.to_string(),
192+
};
193+
194+
let pat = name.as_str();
195+
let mut refs = vec![];
196+
197+
for (file_id, search_range) in search_scope {
198+
let text = db.file_text(file_id);
199+
let search_range =
200+
search_range.unwrap_or(TextRange::offset_len(0.into(), TextUnit::of_str(&text)));
201+
202+
let sema = Semantics::new(db);
203+
let tree = Lazy::new(|| sema.parse(file_id).syntax().clone());
204+
205+
for (idx, _) in text.match_indices(pat) {
206+
let offset = TextUnit::from_usize(idx);
207+
if !search_range.contains_inclusive(offset) {
208+
// tested_by!(search_filters_by_range);
209+
continue;
210+
}
211+
212+
let name_ref =
213+
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&tree, offset) {
214+
name_ref
215+
} else {
216+
// Handle macro token cases
217+
let token = match tree.token_at_offset(offset) {
218+
TokenAtOffset::None => continue,
219+
TokenAtOffset::Single(t) => t,
220+
TokenAtOffset::Between(_, t) => t,
221+
};
222+
let expanded = sema.descend_into_macros(token);
223+
match ast::NameRef::cast(expanded.parent()) {
224+
Some(name_ref) => name_ref,
225+
_ => continue,
226+
}
227+
};
228+
229+
// FIXME: reuse sb
230+
// See https://github.com/rust-lang/rust/pull/68198#issuecomment-574269098
231+
232+
if let Some(d) = classify_name_ref(&sema, &name_ref) {
233+
let d = d.definition();
234+
if &d == def {
235+
let kind =
236+
if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) {
237+
ReferenceKind::StructLiteral
238+
} else {
239+
ReferenceKind::Other
240+
};
241+
242+
let file_range = sema.original_range(name_ref.syntax());
243+
refs.push(Reference {
244+
file_range,
245+
kind,
246+
access: reference_access(&d, &name_ref),
247+
});
248+
}
249+
}
250+
}
251+
}
252+
refs
253+
}
254+
255+
fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<ReferenceAccess> {
256+
// Only Locals and Fields have accesses for now.
257+
match def {
258+
Definition::Local(_) | Definition::StructField(_) => {}
259+
_ => return None,
260+
};
261+
262+
let mode = name_ref.syntax().ancestors().find_map(|node| {
263+
match_ast! {
264+
match (node) {
265+
ast::BinExpr(expr) => {
266+
if expr.op_kind()?.is_assignment() {
267+
// If the variable or field ends on the LHS's end then it's a Write (covers fields and locals).
268+
// FIXME: This is not terribly accurate.
269+
if let Some(lhs) = expr.lhs() {
270+
if lhs.syntax().text_range().end() == name_ref.syntax().text_range().end() {
271+
return Some(ReferenceAccess::Write);
272+
}
273+
}
274+
}
275+
Some(ReferenceAccess::Read)
276+
},
277+
_ => {None}
278+
}
279+
}
280+
});
281+
282+
// Default Locals and Fields to read
283+
mode.or(Some(ReferenceAccess::Read))
284+
}
285+
286+
fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool {
287+
name_ref
288+
.syntax()
289+
.ancestors()
290+
.find_map(ast::CallExpr::cast)
291+
.and_then(|c| match c.expr()? {
292+
ast::Expr::PathExpr(p) => {
293+
Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref))
294+
}
295+
_ => None,
296+
})
297+
.unwrap_or(false)
298+
}
299+
300+
fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool {
301+
name_ref
302+
.syntax()
303+
.ancestors()
304+
.find_map(ast::RecordLit::cast)
305+
.and_then(|l| l.path())
306+
.and_then(|p| p.segment())
307+
.map(|p| p.name_ref().as_ref() == Some(name_ref))
308+
.unwrap_or(false)
309+
}

0 commit comments

Comments
 (0)