aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_ide/src/references.rs138
1 files changed, 111 insertions, 27 deletions
diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs
index 5e2fe1905..ebded715d 100644
--- a/crates/ra_ide/src/references.rs
+++ b/crates/ra_ide/src/references.rs
@@ -112,25 +112,20 @@ impl IntoIterator for ReferenceSearchResult {
112 112
113pub(crate) fn find_all_refs( 113pub(crate) fn find_all_refs(
114 db: &RootDatabase, 114 db: &RootDatabase,
115 mut position: FilePosition, 115 position: FilePosition,
116 search_scope: Option<SearchScope>, 116 search_scope: Option<SearchScope>,
117) -> Option<RangeInfo<ReferenceSearchResult>> { 117) -> Option<RangeInfo<ReferenceSearchResult>> {
118 let parse = db.parse(position.file_id); 118 let parse = db.parse(position.file_id);
119 let syntax = parse.tree().syntax().clone(); 119 let syntax = parse.tree().syntax().clone();
120 120
121 let token = syntax.token_at_offset(position.offset); 121 let (opt_name, search_kind) =
122 let mut search_kind = ReferenceKind::Other; 122 if let Some(name) = get_struct_def_name_for_struc_litetal_search(&syntax, position) {
123 (Some(name), ReferenceKind::StructLiteral)
124 } else {
125 (find_node_at_offset::<ast::Name>(&syntax, position.offset), ReferenceKind::Other)
126 };
123 127
124 if let TokenAtOffset::Between(ref left, ref right) = token { 128 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 129
135 let declaration = match def.kind { 130 let declaration = match def.kind {
136 NameKind::Macro(mac) => mac.to_nav(db), 131 NameKind::Macro(mac) => mac.to_nav(db),
@@ -170,9 +165,10 @@ fn find_name(
170 db: &RootDatabase, 165 db: &RootDatabase,
171 syntax: &SyntaxNode, 166 syntax: &SyntaxNode,
172 position: FilePosition, 167 position: FilePosition,
168 opt_name: Option<ast::Name>,
173) -> Option<RangeInfo<(String, NameDefinition)>> { 169) -> Option<RangeInfo<(String, NameDefinition)>> {
174 let mut sb = SourceBinder::new(db); 170 let mut sb = SourceBinder::new(db);
175 if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { 171 if let Some(name) = opt_name {
176 let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?; 172 let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?;
177 let range = name.syntax().text_range(); 173 let range = name.syntax().text_range();
178 return Some(RangeInfo::new(range, (name.text().to_string(), def))); 174 return Some(RangeInfo::new(range, (name.text().to_string(), def)));
@@ -218,15 +214,8 @@ fn process_definition(
218 if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref)) 214 if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref))
219 { 215 {
220 if d == def { 216 if d == def {
221 let kind = if name_ref 217 let kind = if is_record_lit_name_ref(&name_ref)
222 .syntax() 218 || 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 { 219 {
231 ReferenceKind::StructLiteral 220 ReferenceKind::StructLiteral
232 } else { 221 } else {
@@ -301,6 +290,49 @@ fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<Referenc
301 mode.or(Some(ReferenceAccess::Read)) 290 mode.or(Some(ReferenceAccess::Read))
302} 291}
303 292
293fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool {
294 name_ref
295 .syntax()
296 .ancestors()
297 .find_map(ast::RecordLit::cast)
298 .and_then(|l| l.path())
299 .and_then(|p| p.segment())
300 .map(|p| p.name_ref().as_ref() == Some(name_ref))
301 .unwrap_or(false)
302}
303
304fn get_struct_def_name_for_struc_litetal_search(
305 syntax: &SyntaxNode,
306 position: FilePosition,
307) -> Option<ast::Name> {
308 if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) {
309 if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN {
310 return None;
311 }
312 if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, left.text_range().start()) {
313 return name.syntax().ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name());
314 }
315 if find_node_at_offset::<ast::TypeParamList>(&syntax, left.text_range().start()).is_some() {
316 return left.ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name());
317 }
318 }
319 None
320}
321
322fn is_call_expr_name_ref(name_ref: &ast::NameRef) -> bool {
323 name_ref
324 .syntax()
325 .ancestors()
326 .find_map(ast::CallExpr::cast)
327 .and_then(|c| match c.expr()? {
328 ast::Expr::PathExpr(p) => {
329 Some(p.path()?.segment()?.name_ref().as_ref() == Some(name_ref))
330 }
331 _ => None,
332 })
333 .unwrap_or(false)
334}
335
304#[cfg(test)] 336#[cfg(test)]
305mod tests { 337mod tests {
306 use crate::{ 338 use crate::{
@@ -309,7 +341,7 @@ mod tests {
309 }; 341 };
310 342
311 #[test] 343 #[test]
312 fn test_struct_literal() { 344 fn test_struct_literal_after_space() {
313 let code = r#" 345 let code = r#"
314 struct Foo <|>{ 346 struct Foo <|>{
315 a: i32, 347 a: i32,
@@ -331,6 +363,58 @@ mod tests {
331 } 363 }
332 364
333 #[test] 365 #[test]
366 fn test_struct_literal_befor_space() {
367 let code = r#"
368 struct Foo<|> {}
369 fn main() {
370 let f: Foo;
371 f = Foo {};
372 }"#;
373
374 let refs = get_all_refs(code);
375 check_result(
376 refs,
377 "Foo STRUCT_DEF FileId(1) [5; 18) [12; 15) Other",
378 &["FileId(1) [54; 57) Other", "FileId(1) [71; 74) StructLiteral"],
379 );
380 }
381
382 #[test]
383 fn test_struct_literal_with_generic_type() {
384 let code = r#"
385 struct Foo<T> <|>{}
386 fn main() {
387 let f: Foo::<i32>;
388 f = Foo {};
389 }"#;
390
391 let refs = get_all_refs(code);
392 check_result(
393 refs,
394 "Foo STRUCT_DEF FileId(1) [5; 21) [12; 15) Other",
395 &["FileId(1) [81; 84) StructLiteral"],
396 );
397 }
398
399 #[test]
400 fn test_struct_literal_for_tuple() {
401 let code = r#"
402 struct Foo<|>(i32);
403
404 fn main() {
405 let f: Foo;
406 f = Foo(1);
407 }"#;
408
409 let refs = get_all_refs(code);
410 check_result(
411 refs,
412 "Foo STRUCT_DEF FileId(1) [5; 21) [12; 15) Other",
413 &["FileId(1) [71; 74) StructLiteral"],
414 );
415 }
416
417 #[test]
334 fn test_find_all_refs_for_local() { 418 fn test_find_all_refs_for_local() {
335 let code = r#" 419 let code = r#"
336 fn main() { 420 fn main() {
@@ -564,7 +648,7 @@ mod tests {
564 check_result( 648 check_result(
565 refs, 649 refs,
566 "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", 650 "quux FN_DEF FileId(1) [18; 34) [25; 29) Other",
567 &["FileId(2) [16; 20) Other", "FileId(3) [16; 20) Other"], 651 &["FileId(2) [16; 20) StructLiteral", "FileId(3) [16; 20) StructLiteral"],
568 ); 652 );
569 653
570 let refs = 654 let refs =
@@ -572,7 +656,7 @@ mod tests {
572 check_result( 656 check_result(
573 refs, 657 refs,
574 "quux FN_DEF FileId(1) [18; 34) [25; 29) Other", 658 "quux FN_DEF FileId(1) [18; 34) [25; 29) Other",
575 &["FileId(3) [16; 20) Other"], 659 &["FileId(3) [16; 20) StructLiteral"],
576 ); 660 );
577 } 661 }
578 662
@@ -591,7 +675,7 @@ mod tests {
591 check_result( 675 check_result(
592 refs, 676 refs,
593 "m1 MACRO_CALL FileId(1) [9; 63) [46; 48) Other", 677 "m1 MACRO_CALL FileId(1) [9; 63) [46; 48) Other",
594 &["FileId(1) [96; 98) Other", "FileId(1) [114; 116) Other"], 678 &["FileId(1) [96; 98) StructLiteral", "FileId(1) [114; 116) StructLiteral"],
595 ); 679 );
596 } 680 }
597 681