mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-28 14:03:35 +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(
|
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"],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue