Skip to content

Commit 5dd8f8e

Browse files
Merge #2810
2810: Improves reference search by StructLiteral r=mikhail-m1 a=mikhail-m1 Hey, I've made some changes to improve search for struct literals, now it works for `struct Foo<|> {`, `struct Foo <|>{`, `struct Foo<|>(`. Unfortunately tuple creation is represented as a call expression, so for tuples it works only is search is started in a tuple declaration. It leads to incorrect classification of function calls during search phase, but from user perspective it's not visible and works as expected. May be it worth to add a comment or rename it to remove this misleading classification. Issue #2549. Co-authored-by: Mikhail Modin <[email protected]>
2 parents 5cfaf87 + fbc3ffc commit 5dd8f8e

File tree

1 file changed

+111
-27
lines changed

1 file changed

+111
-27
lines changed

crates/ra_ide/src/references.rs

Lines changed: 111 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -112,25 +112,20 @@ impl IntoIterator for ReferenceSearchResult {
112112

113113
pub(crate) fn find_all_refs(
114114
db: &RootDatabase,
115-
mut position: FilePosition,
115+
position: FilePosition,
116116
search_scope: Option<SearchScope>,
117117
) -> Option<RangeInfo<ReferenceSearchResult>> {
118118
let parse = db.parse(position.file_id);
119119
let syntax = parse.tree().syntax().clone();
120120

121-
let token = syntax.token_at_offset(position.offset);
122-
let mut search_kind = ReferenceKind::Other;
121+
let (opt_name, search_kind) =
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+
};
123127

124-
if let TokenAtOffset::Between(ref left, ref right) = token {
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)?;
128+
let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position, opt_name)?;
134129

135130
let declaration = match def.kind {
136131
NameKind::Macro(mac) => mac.to_nav(db),
@@ -170,9 +165,10 @@ fn find_name(
170165
db: &RootDatabase,
171166
syntax: &SyntaxNode,
172167
position: FilePosition,
168+
opt_name: Option<ast::Name>,
173169
) -> Option<RangeInfo<(String, NameDefinition)>> {
174170
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 {
176172
let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?;
177173
let range = name.syntax().text_range();
178174
return Some(RangeInfo::new(range, (name.text().to_string(), def)));
@@ -218,15 +214,8 @@ fn process_definition(
218214
if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref))
219215
{
220216
if d == def {
221-
let kind = if name_ref
222-
.syntax()
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)
217+
let kind = if is_record_lit_name_ref(&name_ref)
218+
|| is_call_expr_name_ref(&name_ref)
230219
{
231220
ReferenceKind::StructLiteral
232221
} else {
@@ -301,6 +290,49 @@ fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<Referenc
301290
mode.or(Some(ReferenceAccess::Read))
302291
}
303292

293+
fn 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+
304+
fn 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+
322+
fn 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+
304336
#[cfg(test)]
305337
mod tests {
306338
use crate::{
@@ -309,7 +341,7 @@ mod tests {
309341
};
310342

311343
#[test]
312-
fn test_struct_literal() {
344+
fn test_struct_literal_after_space() {
313345
let code = r#"
314346
struct Foo <|>{
315347
a: i32,
@@ -330,6 +362,58 @@ mod tests {
330362
);
331363
}
332364

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+
333417
#[test]
334418
fn test_find_all_refs_for_local() {
335419
let code = r#"
@@ -564,15 +648,15 @@ mod tests {
564648
check_result(
565649
refs,
566650
"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"],
568652
);
569653

570654
let refs =
571655
analysis.find_all_refs(pos, Some(SearchScope::single_file(bar))).unwrap().unwrap();
572656
check_result(
573657
refs,
574658
"quux FN_DEF FileId(1) [18; 34) [25; 29) Other",
575-
&["FileId(3) [16; 20) Other"],
659+
&["FileId(3) [16; 20) StructLiteral"],
576660
);
577661
}
578662

@@ -591,7 +675,7 @@ mod tests {
591675
check_result(
592676
refs,
593677
"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"],
595679
);
596680
}
597681

0 commit comments

Comments
 (0)