aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/references.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/references.rs')
-rw-r--r--crates/ra_ide/src/references.rs157
1 files changed, 119 insertions, 38 deletions
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index 5e2fe1905..a6320bd2f 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -10,13 +10,13 @@
10//! resolved to the search element definition, we get a reference. 10//! resolved to the search element definition, we get a reference.
11 11
12mod classify; 12mod classify;
13mod name_definition;
14mod rename; 13mod rename;
15mod search_scope; 14mod search_scope;
16 15
17use hir::{InFile, SourceBinder}; 16use hir::{InFile, SourceBinder};
18use once_cell::unsync::Lazy; 17use once_cell::unsync::Lazy;
19use ra_db::{SourceDatabase, SourceDatabaseExt}; 18use ra_db::{SourceDatabase, SourceDatabaseExt};
19use ra_ide_db::RootDatabase;
20use ra_prof::profile; 20use ra_prof::profile;
21use ra_syntax::{ 21use ra_syntax::{
22 algo::find_node_at_offset, 22 algo::find_node_at_offset,
@@ -24,15 +24,13 @@ use ra_syntax::{
24 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset, 24 match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, TextUnit, TokenAtOffset,
25}; 25};
26 26
27use crate::{ 27use crate::{display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo};
28 db::RootDatabase, display::ToNav, FilePosition, FileRange, NavigationTarget, RangeInfo,
29};
30 28
31pub(crate) use self::{ 29pub(crate) use self::{
32 classify::{classify_name, classify_name_ref}, 30 classify::{classify_name, classify_name_ref},
33 name_definition::{NameDefinition, NameKind},
34 rename::rename, 31 rename::rename,
35}; 32};
33pub(crate) use ra_ide_db::defs::{NameDefinition, NameKind};
36 34
37pub use self::search_scope::SearchScope; 35pub use self::search_scope::SearchScope;
38 36
@@ -112,38 +110,32 @@ impl IntoIterator for ReferenceSearchResult {
112 110
113pub(crate) fn find_all_refs( 111pub(crate) fn find_all_refs(
114 db: &RootDatabase, 112 db: &RootDatabase,
115 mut position: FilePosition, 113 position: FilePosition,
116 search_scope: Option<SearchScope>, 114 search_scope: Option<SearchScope>,
117) -> Option<RangeInfo<ReferenceSearchResult>> { 115) -> Option<RangeInfo<ReferenceSearchResult>> {
118 let parse = db.parse(position.file_id); 116 let parse = db.parse(position.file_id);
119 let syntax = parse.tree().syntax().clone(); 117 let syntax = parse.tree().syntax().clone();
120 118
121 let token = syntax.token_at_offset(position.offset); 119 let (opt_name, search_kind) =
122 let mut search_kind = ReferenceKind::Other; 120 if let Some(name) = get_struct_def_name_for_struc_litetal_search(&syntax, position) {
121 (Some(name), ReferenceKind::StructLiteral)
122 } else {
123 (find_node_at_offset::<ast::Name>(&syntax, position.offset), ReferenceKind::Other)
124 };
123 125
124 if let TokenAtOffset::Between(ref left, ref right) = token { 126 let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position, opt_name)?;
125 if (right.kind() == SyntaxKind::L_CURLY || right.kind() == SyntaxKind::L_PAREN)
126 && left.kind() != SyntaxKind::IDENT
127 {
128 position = FilePosition { offset: left.text_range().start(), ..position };
129 search_kind = ReferenceKind::StructLiteral;
130 }
131 }
132
133 let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position)?;
134 127
135 let declaration = match def.kind { 128 let declaration = match def.kind {
136 NameKind::Macro(mac) => mac.to_nav(db), 129 NameKind::Macro(mac) => mac.to_nav(db),
137 NameKind::Field(field) => field.to_nav(db), 130 NameKind::StructField(field) => field.to_nav(db),
138 NameKind::AssocItem(assoc) => assoc.to_nav(db), 131 NameKind::ModuleDef(def) => NavigationTarget::from_def(db, def)?,
139 NameKind::Def(def) => NavigationTarget::from_def(db, def)?,
140 NameKind::SelfType(imp) => imp.to_nav(db), 132 NameKind::SelfType(imp) => imp.to_nav(db),
141 NameKind::Local(local) => local.to_nav(db), 133 NameKind::Local(local) => local.to_nav(db),
142 NameKind::TypeParam(_) => return None, 134 NameKind::TypeParam(_) => return None,
143 }; 135 };
144 136
145 let search_scope = { 137 let search_scope = {
146 let base = def.search_scope(db); 138 let base = SearchScope::for_def(&def, db);
147 match search_scope { 139 match search_scope {
148 None => base, 140 None => base,
149 Some(scope) => base.intersection(&scope), 141 Some(scope) => base.intersection(&scope),
@@ -170,9 +162,10 @@ fn find_name(
170 db: &RootDatabase, 162 db: &RootDatabase,
171 syntax: &SyntaxNode, 163 syntax: &SyntaxNode,
172 position: FilePosition, 164 position: FilePosition,
165 opt_name: Option<ast::Name>,
173) -> Option<RangeInfo<(String, NameDefinition)>> { 166) -> Option<RangeInfo<(String, NameDefinition)>> {
174 let mut sb = SourceBinder::new(db); 167 let mut sb = SourceBinder::new(db);
175 if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { 168 if let Some(name) = opt_name {
176 let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?; 169 let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?;
177 let range = name.syntax().text_range(); 170 let range = name.syntax().text_range();
178 return Some(RangeInfo::new(range, (name.text().to_string(), def))); 171 return Some(RangeInfo::new(range, (name.text().to_string(), def)));
@@ -218,15 +211,8 @@ fn process_definition(
218 if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref)) 211 if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref))
219 { 212 {
220 if d == def { 213 if d == def {
221 let kind = if name_ref 214 let kind = if is_record_lit_name_ref(&name_ref)
222 .syntax() 215 || is_call_expr_name_ref(&name_ref)
223 .ancestors()
224 .find_map(ast::RecordLit::cast)
225 .and_then(|l| l.path())
226 .and_then(|p| p.segment())
227 .and_then(|p| p.name_ref())
228 .map(|n| n == name_ref)
229 .unwrap_or(false)
230 { 216 {
231 ReferenceKind::StructLiteral 217 ReferenceKind::StructLiteral
232 } else { 218 } else {
@@ -253,7 +239,7 @@ fn decl_access(
253 range: TextRange, 239 range: TextRange,
254) -> Option<ReferenceAccess> { 240) -> Option<ReferenceAccess> {
255 match kind { 241 match kind {
256 NameKind::Local(_) | NameKind::Field(_) => {} 242 NameKind::Local(_) | NameKind::StructField(_) => {}
257 _ => return None, 243 _ => return None,
258 }; 244 };
259 245
@@ -273,7 +259,7 @@ fn decl_access(
273fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<ReferenceAccess> { 259fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<ReferenceAccess> {
274 // Only Locals and Fields have accesses for now. 260 // Only Locals and Fields have accesses for now.
275 match kind { 261 match kind {
276 NameKind::Local(_) | NameKind::Field(_) => {} 262 NameKind::Local(_) | NameKind::StructField(_) => {}
277 _ => return None, 263 _ => return None,
278 }; 264 };
279 265
@@ -301,6 +287,49 @@ fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<Referenc
301 mode.or(Some(ReferenceAccess::Read)) 287 mode.or(Some(ReferenceAccess::Read))
302} 288}
303 289
290fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool {
291 name_ref
292 .syntax()
293 .ancestors()
294 .find_map(ast::RecordLit::cast)
295 .and_then(|l| l.path())
296 .and_then(|p| p.segment())
297 .map(|p| p.name_ref().as_ref() == Some(name_ref))
298 .unwrap_or(false)
299}
300
301fn get_struct_def_name_for_struc_litetal_search(
302 syntax: &SyntaxNode,
303 position: FilePosition,
304) -> Option<ast::Name> {
305 if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) {
306 if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN {
307 return None;
308 }
309 if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, left.text_range().start()) {
310 return name.syntax().ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name());
311 }
312 if find_node_at_offset::<ast::TypeParamList>(&syntax, left.text_range().start()).is_some() {
313 return left.ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name());
314 }
315 }
316 None
317}
318
319fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool {
320 name_ref
321 .syntax()
322 .ancestors()
323 .find_map(ast::CallExpr::cast)
324 .and_then(|c| match c.expr()? {
325 ast::Expr::PathExpr(p) => {
326 Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref))
327 }
328 _ => None,
329 })
330 .unwrap_or(false)
331}
332
304#[cfg(test)] 333#[cfg(test)]
305mod tests { 334mod tests {
306 use crate::{ 335 use crate::{
@@ -309,7 +338,7 @@ mod tests {
309 }; 338 };
310 339
311 #[test] 340 #[test]
312 fn test_struct_literal() { 341 fn test_struct_literal_after_space() {
313 let code = r#" 342 let code = r#"
314 struct Foo <|>{ 343 struct Foo <|>{
315 a: i32, 344 a: i32,
@@ -331,6 +360,58 @@ mod tests {
331 } 360 }
332 361
333 #[test] 362 #[test]
363 fn test_struct_literal_befor_space() {
364 let code = r#"
365 struct Foo<|> {}
366 fn main() {
367 let f: Foo;
368 f = Foo {};
369 }"#;
370
371 let refs = get_all_refs(code);
372 check_result(
373 refs,
374 "Foo STRUCT_DEF FileId(1) [5; 18) [12; 15) Other",
375 &["FileId(1) [54; 57) Other", "FileId(1) [71; 74) StructLiteral"],
376 );
377 }
378
379 #[test]
380 fn test_struct_literal_with_generic_type() {
381 let code = r#"
382 struct Foo<T> <|>{}
383 fn main() {
384 let f: Foo::<i32>;
385 f = Foo {};
386 }"#;
387
388 let refs = get_all_refs(code);
389 check_result(
390 refs,
391 "Foo STRUCT_DEF FileId(1) [5; 21) [12; 15) Other",
392 &["FileId(1) [81; 84) StructLiteral"],
393 );
394 }
395
396 #[test]
397 fn test_struct_literal_for_tuple() {
398 let code = r#"
399 struct Foo<|>(i32);
400
401 fn main() {
402 let f: Foo;
403 f = Foo(1);
404 }"#;
405
406 let refs = get_all_refs(code);
407 check_result(
408 refs,
409 "Foo STRUCT_DEF FileId(1) [5; 21) [12; 15) Other",
410 &["FileId(1) [71; 74) StructLiteral"],
411 );
412 }
413
414 #[test]
334 fn test_find_all_refs_for_local() { 415 fn test_find_all_refs_for_local() {
335 let code = r#" 416 let code = r#"
336 fn main() { 417 fn main() {
@@ -564,7 +645,7 @@ mod tests {
564 check_result( 645 check_result(
565 refs, 646 refs,
566 "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", 647 "quux FN_DEF FileId(1) [18; 34) [25; 29) Other",
567 &["FileId(2) [16; 20) Other", "FileId(3) [16; 20) Other"], 648 &["FileId(2) [16; 20) StructLiteral", "FileId(3) [16; 20) StructLiteral"],
568 ); 649 );
569 650
570 let refs = 651 let refs =
@@ -572,7 +653,7 @@ mod tests {
572 check_result( 653 check_result(
573 refs, 654 refs,
574 "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", 655 "quux FN_DEF FileId(1) [18; 34) [25; 29) Other",
575 &["FileId(3) [16; 20) Other"], 656 &["FileId(3) [16; 20) StructLiteral"],
576 ); 657 );
577 } 658 }
578 659
@@ -591,7 +672,7 @@ mod tests {
591 check_result( 672 check_result(
592 refs, 673 refs,
593 "m1 MACRO_CALL FileId(1) [9; 63) [46; 48) Other", 674 "m1 MACRO_CALL FileId(1) [9; 63) [46; 48) Other",
594 &["FileId(1) [96; 98) Other", "FileId(1) [114; 116) Other"], 675 &["FileId(1) [96; 98) StructLiteral", "FileId(1) [114; 116) StructLiteral"],
595 ); 676 );
596 } 677 }
597 678