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 <mikhailm1@gmail.com>
This commit is contained in:
bors[bot] 2020-01-27 21:58:35 +00:00 committed by GitHub
commit 5dd8f8e26f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -112,25 +112,20 @@ impl IntoIterator for ReferenceSearchResult {
pub(crate) fn find_all_refs( pub(crate) fn find_all_refs(
db: &RootDatabase, db: &RootDatabase,
mut position: FilePosition, position: FilePosition,
search_scope: Option<SearchScope>, search_scope: Option<SearchScope>,
) -> Option<RangeInfo<ReferenceSearchResult>> { ) -> Option<RangeInfo<ReferenceSearchResult>> {
let parse = db.parse(position.file_id); let parse = db.parse(position.file_id);
let syntax = parse.tree().syntax().clone(); let syntax = parse.tree().syntax().clone();
let token = syntax.token_at_offset(position.offset); let (opt_name, search_kind) =
let mut search_kind = ReferenceKind::Other; if let Some(name) = get_struct_def_name_for_struc_litetal_search(&syntax, position) {
(Some(name), ReferenceKind::StructLiteral)
} else {
(find_node_at_offset::<ast::Name>(&syntax, position.offset), ReferenceKind::Other)
};
if let TokenAtOffset::Between(ref left, ref right) = token { let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position, opt_name)?;
if (right.kind() == SyntaxKind::L_CURLY || right.kind() == SyntaxKind::L_PAREN)
&& left.kind() != SyntaxKind::IDENT
{
position = FilePosition { offset: left.text_range().start(), ..position };
search_kind = ReferenceKind::StructLiteral;
}
}
let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position)?;
let declaration = match def.kind { let declaration = match def.kind {
NameKind::Macro(mac) => mac.to_nav(db), NameKind::Macro(mac) => mac.to_nav(db),
@ -170,9 +165,10 @@ fn find_name(
db: &RootDatabase, db: &RootDatabase,
syntax: &SyntaxNode, syntax: &SyntaxNode,
position: FilePosition, position: FilePosition,
opt_name: Option<ast::Name>,
) -> Option<RangeInfo<(String, NameDefinition)>> { ) -> Option<RangeInfo<(String, NameDefinition)>> {
let mut sb = SourceBinder::new(db); let mut sb = SourceBinder::new(db);
if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, position.offset) { if let Some(name) = opt_name {
let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?; let def = classify_name(&mut sb, InFile::new(position.file_id.into(), &name))?;
let range = name.syntax().text_range(); let range = name.syntax().text_range();
return Some(RangeInfo::new(range, (name.text().to_string(), def))); return Some(RangeInfo::new(range, (name.text().to_string(), def)));
@ -218,15 +214,8 @@ fn process_definition(
if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref)) if let Some(d) = classify_name_ref(&mut sb, InFile::new(file_id.into(), &name_ref))
{ {
if d == def { if d == def {
let kind = if name_ref let kind = if is_record_lit_name_ref(&name_ref)
.syntax() || is_call_expr_name_ref(&name_ref)
.ancestors()
.find_map(ast::RecordLit::cast)
.and_then(|l| l.path())
.and_then(|p| p.segment())
.and_then(|p| p.name_ref())
.map(|n| n == name_ref)
.unwrap_or(false)
{ {
ReferenceKind::StructLiteral ReferenceKind::StructLiteral
} else { } else {
@ -301,6 +290,49 @@ fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<Referenc
mode.or(Some(ReferenceAccess::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,
) -> Option<ast::Name> {
if let TokenAtOffset::Between(ref left, ref right) = syntax.token_at_offset(position.offset) {
if right.kind() != SyntaxKind::L_CURLY && right.kind() != SyntaxKind::L_PAREN {
return None;
}
if let Some(name) = find_node_at_offset::<ast::Name>(&syntax, left.text_range().start()) {
return name.syntax().ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name());
}
if find_node_at_offset::<ast::TypeParamList>(&syntax, left.text_range().start()).is_some() {
return left.ancestors().find_map(ast::StructDef::cast).and_then(|l| l.name());
}
}
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)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
@ -309,7 +341,7 @@ mod tests {
}; };
#[test] #[test]
fn test_struct_literal() { fn test_struct_literal_after_space() {
let code = r#" let code = r#"
struct Foo <|>{ struct Foo <|>{
a: i32, a: i32,
@ -330,6 +362,58 @@ mod tests {
); );
} }
#[test]
fn test_struct_literal_befor_space() {
let code = r#"
struct Foo<|> {}
fn main() {
let f: Foo;
f = Foo {};
}"#;
let refs = get_all_refs(code);
check_result(
refs,
"Foo STRUCT_DEF FileId(1) [5; 18) [12; 15) Other",
&["FileId(1) [54; 57) Other", "FileId(1) [71; 74) StructLiteral"],
);
}
#[test]
fn test_struct_literal_with_generic_type() {
let code = r#"
struct Foo<T> <|>{}
fn main() {
let f: Foo::<i32>;
f = Foo {};
}"#;
let refs = get_all_refs(code);
check_result(
refs,
"Foo STRUCT_DEF FileId(1) [5; 21) [12; 15) Other",
&["FileId(1) [81; 84) StructLiteral"],
);
}
#[test]
fn test_struct_literal_for_tuple() {
let code = r#"
struct Foo<|>(i32);
fn main() {
let f: Foo;
f = Foo(1);
}"#;
let refs = get_all_refs(code);
check_result(
refs,
"Foo STRUCT_DEF FileId(1) [5; 21) [12; 15) Other",
&["FileId(1) [71; 74) StructLiteral"],
);
}
#[test] #[test]
fn test_find_all_refs_for_local() { fn test_find_all_refs_for_local() {
let code = r#" let code = r#"
@ -564,7 +648,7 @@ mod tests {
check_result( check_result(
refs, refs,
"quux FN_DEF FileId(1) [18; 34) [25; 29) Other", "quux FN_DEF FileId(1) [18; 34) [25; 29) Other",
&["FileId(2) [16; 20) Other", "FileId(3) [16; 20) Other"], &["FileId(2) [16; 20) StructLiteral", "FileId(3) [16; 20) StructLiteral"],
); );
let refs = let refs =
@ -572,7 +656,7 @@ mod tests {
check_result( check_result(
refs, refs,
"quux FN_DEF FileId(1) [18; 34) [25; 29) Other", "quux FN_DEF FileId(1) [18; 34) [25; 29) Other",
&["FileId(3) [16; 20) Other"], &["FileId(3) [16; 20) StructLiteral"],
); );
} }
@ -591,7 +675,7 @@ mod tests {
check_result( check_result(
refs, refs,
"m1 MACRO_CALL FileId(1) [9; 63) [46; 48) Other", "m1 MACRO_CALL FileId(1) [9; 63) [46; 48) Other",
&["FileId(1) [96; 98) Other", "FileId(1) [114; 116) Other"], &["FileId(1) [96; 98) StructLiteral", "FileId(1) [114; 116) StructLiteral"],
); );
} }