Refactor reference searching to work with the ast
This commit is contained in:
parent
43ccbf4360
commit
d644728d82
8 changed files with 488 additions and 405 deletions
|
|
@ -10,7 +10,9 @@ use base_db::{FileId, FileRange, SourceDatabaseExt};
|
|||
use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility};
|
||||
use once_cell::unsync::Lazy;
|
||||
use rustc_hash::FxHashMap;
|
||||
use syntax::{ast, match_ast, AstNode, TextRange, TextSize};
|
||||
use syntax::{
|
||||
ast, match_ast, AstNode, NodeOrToken, SyntaxElement, SyntaxNode, TextRange, TextSize,
|
||||
};
|
||||
|
||||
use crate::defs::NameClass;
|
||||
use crate::{
|
||||
|
|
@ -18,6 +20,13 @@ use crate::{
|
|||
RootDatabase,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum NameKind {
|
||||
Name,
|
||||
NameRef,
|
||||
Lifetime,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct UsageSearchResult {
|
||||
pub references: FxHashMap<FileId, Vec<FileReference>>,
|
||||
|
|
@ -52,23 +61,53 @@ impl IntoIterator for UsageSearchResult {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum NameLike {
|
||||
NameRef(ast::NameRef),
|
||||
Name(ast::Name),
|
||||
Lifetime(ast::Lifetime),
|
||||
}
|
||||
|
||||
mod __ {
|
||||
use super::{
|
||||
ast::{Lifetime, Name, NameRef},
|
||||
NameLike,
|
||||
};
|
||||
stdx::impl_from!(NameRef, Name, Lifetime for NameLike);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FileReference {
|
||||
pub range: TextRange,
|
||||
pub kind: ReferenceKind,
|
||||
pub name: NameKind,
|
||||
pub access: Option<ReferenceAccess>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ReferenceKind {
|
||||
FieldShorthandForField,
|
||||
FieldShorthandForLocal,
|
||||
StructLiteral,
|
||||
RecordFieldExprOrPat,
|
||||
SelfParam,
|
||||
EnumLiteral,
|
||||
Lifetime,
|
||||
Other,
|
||||
impl FileReference {
|
||||
pub fn name_from_syntax(&self, root: &SyntaxNode) -> Option<NameLike> {
|
||||
let node = node_or_parent(root.covering_element(self.range));
|
||||
match self.name {
|
||||
NameKind::Name => ast::Name::cast(node).map(Into::into),
|
||||
NameKind::NameRef => ast::NameRef::cast(node).map(Into::into),
|
||||
NameKind::Lifetime => ast::Lifetime::cast(node).map(Into::into),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_name_ref(&self, root: &SyntaxNode) -> Option<ast::NameRef> {
|
||||
match self.name {
|
||||
NameKind::NameRef => {
|
||||
ast::NameRef::cast(node_or_parent(root.covering_element(self.range)))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn node_or_parent(ele: SyntaxElement) -> SyntaxNode {
|
||||
match ele {
|
||||
NodeOrToken::Node(node) => node,
|
||||
NodeOrToken::Token(token) => token.parent(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
|
|
@ -369,8 +408,7 @@ impl<'a> FindUsages<'a> {
|
|||
match NameRefClass::classify_lifetime(self.sema, lifetime) {
|
||||
Some(NameRefClass::Definition(def)) if &def == self.def => {
|
||||
let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax());
|
||||
let reference =
|
||||
FileReference { range, kind: ReferenceKind::Lifetime, access: None };
|
||||
let reference = FileReference { range, name: NameKind::Lifetime, access: None };
|
||||
sink(file_id, reference)
|
||||
}
|
||||
_ => false, // not a usage
|
||||
|
|
@ -384,19 +422,12 @@ impl<'a> FindUsages<'a> {
|
|||
) -> bool {
|
||||
match NameRefClass::classify(self.sema, &name_ref) {
|
||||
Some(NameRefClass::Definition(def)) if &def == self.def => {
|
||||
let kind = if is_record_field_expr_or_pat(&name_ref) {
|
||||
ReferenceKind::RecordFieldExprOrPat
|
||||
} else if is_record_lit_name_ref(&name_ref) || is_call_expr_name_ref(&name_ref) {
|
||||
ReferenceKind::StructLiteral
|
||||
} else if is_enum_lit_name_ref(&name_ref) {
|
||||
ReferenceKind::EnumLiteral
|
||||
} else {
|
||||
ReferenceKind::Other
|
||||
};
|
||||
|
||||
let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
|
||||
let reference =
|
||||
FileReference { range, kind, access: reference_access(&def, &name_ref) };
|
||||
let reference = FileReference {
|
||||
range,
|
||||
name: NameKind::NameRef,
|
||||
access: reference_access(&def, &name_ref),
|
||||
};
|
||||
sink(file_id, reference)
|
||||
}
|
||||
Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
|
||||
|
|
@ -404,12 +435,12 @@ impl<'a> FindUsages<'a> {
|
|||
let reference = match self.def {
|
||||
Definition::Field(_) if &field == self.def => FileReference {
|
||||
range,
|
||||
kind: ReferenceKind::FieldShorthandForField,
|
||||
name: NameKind::NameRef,
|
||||
access: reference_access(&field, &name_ref),
|
||||
},
|
||||
Definition::Local(l) if &local == l => FileReference {
|
||||
range,
|
||||
kind: ReferenceKind::FieldShorthandForLocal,
|
||||
name: NameKind::NameRef,
|
||||
access: reference_access(&Definition::Local(local), &name_ref),
|
||||
},
|
||||
_ => return false, // not a usage
|
||||
|
|
@ -433,7 +464,7 @@ impl<'a> FindUsages<'a> {
|
|||
let FileRange { file_id, range } = self.sema.original_range(name.syntax());
|
||||
let reference = FileReference {
|
||||
range,
|
||||
kind: ReferenceKind::FieldShorthandForField,
|
||||
name: NameKind::Name,
|
||||
// FIXME: mutable patterns should have `Write` access
|
||||
access: Some(ReferenceAccess::Read),
|
||||
};
|
||||
|
|
@ -473,54 +504,3 @@ fn reference_access(def: &Definition, name_ref: &ast::NameRef) -> Option<Referen
|
|||
// Default Locals and Fields to read
|
||||
mode.or(Some(ReferenceAccess::Read))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
fn is_record_lit_name_ref(name_ref: &ast::NameRef) -> bool {
|
||||
name_ref
|
||||
.syntax()
|
||||
.ancestors()
|
||||
.find_map(ast::RecordExpr::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 is_record_field_expr_or_pat(name_ref: &ast::NameRef) -> bool {
|
||||
if let Some(parent) = name_ref.syntax().parent() {
|
||||
match_ast! {
|
||||
match parent {
|
||||
ast::RecordExprField(it) => true,
|
||||
ast::RecordPatField(_it) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_enum_lit_name_ref(name_ref: &ast::NameRef) -> bool {
|
||||
name_ref
|
||||
.syntax()
|
||||
.ancestors()
|
||||
.find_map(ast::PathExpr::cast)
|
||||
.and_then(|p| p.path())
|
||||
.and_then(|p| p.qualifier())
|
||||
.and_then(|p| p.segment())
|
||||
.map(|p| p.name_ref().as_ref() == Some(name_ref))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue