mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Support self in reference search
This commit is contained in:
parent
a6f26ded00
commit
de4ada22d4
2 changed files with 103 additions and 1 deletions
|
@ -21,7 +21,7 @@ use ide_db::{
|
||||||
use syntax::{
|
use syntax::{
|
||||||
algo::find_node_at_offset,
|
algo::find_node_at_offset,
|
||||||
ast::{self, NameOwner},
|
ast::{self, NameOwner},
|
||||||
AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset,
|
match_ast, AstNode, SyntaxKind, SyntaxNode, TextRange, TokenAtOffset,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo};
|
use crate::{display::TryToNav, FilePosition, FileRange, NavigationTarget, RangeInfo};
|
||||||
|
@ -89,6 +89,10 @@ pub(crate) fn find_all_refs(
|
||||||
let _p = profile::span("find_all_refs");
|
let _p = profile::span("find_all_refs");
|
||||||
let syntax = sema.parse(position.file_id).syntax().clone();
|
let syntax = sema.parse(position.file_id).syntax().clone();
|
||||||
|
|
||||||
|
if let Some(res) = try_find_self_references(&syntax, position) {
|
||||||
|
return Some(res);
|
||||||
|
}
|
||||||
|
|
||||||
let (opt_name, search_kind) = if let Some(name) =
|
let (opt_name, search_kind) = if let Some(name) =
|
||||||
get_struct_def_name_for_struct_literal_search(&sema, &syntax, position)
|
get_struct_def_name_for_struct_literal_search(&sema, &syntax, position)
|
||||||
{
|
{
|
||||||
|
@ -194,6 +198,77 @@ fn get_struct_def_name_for_struct_literal_search(
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn try_find_self_references(
|
||||||
|
syntax: &SyntaxNode,
|
||||||
|
position: FilePosition,
|
||||||
|
) -> Option<RangeInfo<ReferenceSearchResult>> {
|
||||||
|
let self_token =
|
||||||
|
syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)?;
|
||||||
|
let parent = self_token.parent();
|
||||||
|
match_ast! {
|
||||||
|
match parent {
|
||||||
|
ast::SelfParam(it) => (),
|
||||||
|
ast::PathSegment(segment) => {
|
||||||
|
segment.self_token()?;
|
||||||
|
let path = segment.parent_path();
|
||||||
|
if path.qualifier().is_some() && !ast::PathExpr::can_cast(path.syntax().parent()?.kind()) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let function = parent.ancestors().find_map(ast::Fn::cast)?;
|
||||||
|
let self_param = function.param_list()?.self_param()?;
|
||||||
|
let param_self_token = self_param.self_token()?;
|
||||||
|
|
||||||
|
let declaration = Declaration {
|
||||||
|
nav: NavigationTarget {
|
||||||
|
file_id: position.file_id,
|
||||||
|
full_range: self_param.syntax().text_range(),
|
||||||
|
focus_range: Some(param_self_token.text_range()),
|
||||||
|
name: param_self_token.text().clone(),
|
||||||
|
kind: param_self_token.kind(),
|
||||||
|
container_name: None,
|
||||||
|
description: None,
|
||||||
|
docs: None,
|
||||||
|
},
|
||||||
|
kind: ReferenceKind::SelfKw,
|
||||||
|
access: Some(if self_param.mut_token().is_some() {
|
||||||
|
ReferenceAccess::Write
|
||||||
|
} else {
|
||||||
|
ReferenceAccess::Read
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
let references = function
|
||||||
|
.body()
|
||||||
|
.map(|body| {
|
||||||
|
body.syntax()
|
||||||
|
.descendants()
|
||||||
|
.filter_map(ast::PathExpr::cast)
|
||||||
|
.filter_map(|expr| {
|
||||||
|
let path = expr.path()?;
|
||||||
|
if path.qualifier().is_none() {
|
||||||
|
path.segment()?.self_token()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|token| Reference {
|
||||||
|
file_range: FileRange { file_id: position.file_id, range: token.text_range() },
|
||||||
|
kind: ReferenceKind::SelfKw,
|
||||||
|
access: declaration.access, // FIXME: properly check access kind here instead of copying it from the declaration
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
Some(RangeInfo::new(
|
||||||
|
param_self_token.text_range(),
|
||||||
|
ReferenceSearchResult { declaration, references },
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use expect_test::{expect, Expect};
|
use expect_test::{expect, Expect};
|
||||||
|
@ -762,6 +837,32 @@ fn f() -> m::En {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_find_self_refs() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Foo { bar: i32 }
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn foo(self) {
|
||||||
|
let x = self<|>.bar;
|
||||||
|
if true {
|
||||||
|
let _ = match () {
|
||||||
|
() => self,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
self SELF_KW FileId(0) 47..51 47..51 SelfKw Read
|
||||||
|
|
||||||
|
FileId(0) 71..75 SelfKw Read
|
||||||
|
FileId(0) 152..156 SelfKw Read
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn check(ra_fixture: &str, expect: Expect) {
|
fn check(ra_fixture: &str, expect: Expect) {
|
||||||
check_with_scope(ra_fixture, None, expect)
|
check_with_scope(ra_fixture, None, expect)
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ pub enum ReferenceKind {
|
||||||
FieldShorthandForLocal,
|
FieldShorthandForLocal,
|
||||||
StructLiteral,
|
StructLiteral,
|
||||||
RecordFieldExprOrPat,
|
RecordFieldExprOrPat,
|
||||||
|
SelfKw,
|
||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue