mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 21:43:37 +00:00
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 <mikhailm1@gmail.com>
This commit is contained in:
commit
5dd8f8e26f
1 changed files with 111 additions and 27 deletions
|
@ -112,25 +112,20 @@ impl IntoIterator for ReferenceSearchResult {
|
|||
|
||||
pub(crate) fn find_all_refs(
|
||||
db: &RootDatabase,
|
||||
mut position: FilePosition,
|
||||
position: FilePosition,
|
||||
search_scope: Option<SearchScope>,
|
||||
) -> Option<RangeInfo<ReferenceSearchResult>> {
|
||||
let parse = db.parse(position.file_id);
|
||||
let syntax = parse.tree().syntax().clone();
|
||||
|
||||
let token = syntax.token_at_offset(position.offset);
|
||||
let mut search_kind = ReferenceKind::Other;
|
||||
let (opt_name, search_kind) =
|
||||
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 {
|
||||
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 RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position, opt_name)?;
|
||||
|
||||
let declaration = match def.kind {
|
||||
NameKind::Macro(mac) => mac.to_nav(db),
|
||||
|
@ -170,9 +165,10 @@ fn find_name(
|
|||
db: &RootDatabase,
|
||||
syntax: &SyntaxNode,
|
||||
position: FilePosition,
|
||||
opt_name: Option<ast::Name>,
|
||||
) -> Option<RangeInfo<(String, NameDefinition)>> {
|
||||
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 range = name.syntax().text_range();
|
||||
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 d == def {
|
||||
let kind = if name_ref
|
||||
.syntax()
|
||||
.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)
|
||||
let kind = if is_record_lit_name_ref(&name_ref)
|
||||
|| is_call_expr_name_ref(&name_ref)
|
||||
{
|
||||
ReferenceKind::StructLiteral
|
||||
} else {
|
||||
|
@ -301,6 +290,49 @@ fn reference_access(kind: &NameKind, name_ref: &ast::NameRef) -> Option<Referenc
|
|||
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)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
|
@ -309,7 +341,7 @@ mod tests {
|
|||
};
|
||||
|
||||
#[test]
|
||||
fn test_struct_literal() {
|
||||
fn test_struct_literal_after_space() {
|
||||
let code = r#"
|
||||
struct Foo <|>{
|
||||
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]
|
||||
fn test_find_all_refs_for_local() {
|
||||
let code = r#"
|
||||
|
@ -564,7 +648,7 @@ mod tests {
|
|||
check_result(
|
||||
refs,
|
||||
"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 =
|
||||
|
@ -572,7 +656,7 @@ mod tests {
|
|||
check_result(
|
||||
refs,
|
||||
"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(
|
||||
refs,
|
||||
"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"],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue