7699: Implement ast::AstNode for NameLike and move it to node_ext r=matklad a=Veykril

With this `search`(and 2 other modules) don't necessarily go through 3 calls of `find_node_at_offset_with_descend` to find the correct node. Also makes the code that searches for NameLikes a bit easier on the eyes imo, though that can be fixed with just a helper function as well so its not that relevant.

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2021-02-17 13:45:27 +00:00 committed by GitHub
commit 4054525c41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 119 additions and 116 deletions

View File

@ -87,11 +87,18 @@ impl AsName for ast::Name {
}
}
impl AsName for ast::NameOrNameRef {
impl AsName for ast::Lifetime {
fn as_name(&self) -> Name {
Name::resolve(self.text())
}
}
impl AsName for ast::NameLike {
fn as_name(&self) -> Name {
match self {
ast::NameOrNameRef::Name(it) => it.as_name(),
ast::NameOrNameRef::NameRef(it) => it.as_name(),
ast::NameLike::Name(it) => it.as_name(),
ast::NameLike::NameRef(it) => it.as_name(),
ast::NameLike::Lifetime(it) => it.as_name(),
}
}
}

View File

@ -101,29 +101,21 @@ fn find_def(
syntax: &SyntaxNode,
position: FilePosition,
) -> Option<Definition> {
if let Some(name) = sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset)
{
let class = NameClass::classify(sema, &name)?;
Some(class.referenced_or_defined(sema.db))
} else if let Some(lifetime) =
sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset)
{
let def = if let Some(def) =
NameRefClass::classify_lifetime(sema, &lifetime).map(|class| class.referenced(sema.db))
{
def
} else {
NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db)
};
Some(def)
} else if let Some(name_ref) =
sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)
{
let class = NameRefClass::classify(sema, &name_ref)?;
Some(class.referenced(sema.db))
} else {
None
}
let def = match sema.find_node_at_offset_with_descend(syntax, position.offset)? {
ast::NameLike::NameRef(name_ref) => {
NameRefClass::classify(sema, &name_ref)?.referenced(sema.db)
}
ast::NameLike::Name(name) => {
NameClass::classify(sema, &name)?.referenced_or_defined(sema.db)
}
ast::NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime)
.map(|class| class.referenced(sema.db))
.or_else(|| {
NameClass::classify_lifetime(sema, &lifetime)
.map(|class| class.referenced_or_defined(sema.db))
})?,
};
Some(def)
}
fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> {

View File

@ -6,7 +6,7 @@ use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics};
use ide_db::{
base_db::{AnchoredPathBuf, FileId},
defs::{Definition, NameClass, NameRefClass},
search::{FileReference, NameLike},
search::FileReference,
RootDatabase,
};
use stdx::never;
@ -47,12 +47,13 @@ pub(crate) fn prepare_rename(
let sema = Semantics::new(db);
let source_file = sema.parse(position.file_id);
let syntax = source_file.syntax();
let range = match &find_name_like(&sema, &syntax, position)
let range = match &sema
.find_node_at_offset_with_descend(&syntax, position.offset)
.ok_or_else(|| format_err!("No references found at position"))?
{
NameLike::Name(it) => it.syntax(),
NameLike::NameRef(it) => it.syntax(),
NameLike::Lifetime(it) => it.syntax(),
ast::NameLike::Name(it) => it.syntax(),
ast::NameLike::NameRef(it) => it.syntax(),
ast::NameLike::Lifetime(it) => it.syntax(),
}
.text_range();
Ok(RangeInfo::new(range, ()))
@ -121,50 +122,28 @@ fn check_identifier(new_name: &str) -> RenameResult<IdentifierKind> {
}
}
fn find_name_like(
sema: &Semantics<RootDatabase>,
syntax: &SyntaxNode,
position: FilePosition,
) -> Option<NameLike> {
let namelike = if let Some(name_ref) =
sema.find_node_at_offset_with_descend::<ast::NameRef>(syntax, position.offset)
{
NameLike::NameRef(name_ref)
} else if let Some(name) =
sema.find_node_at_offset_with_descend::<ast::Name>(syntax, position.offset)
{
NameLike::Name(name)
} else if let Some(lifetime) =
sema.find_node_at_offset_with_descend::<ast::Lifetime>(syntax, position.offset)
{
NameLike::Lifetime(lifetime)
} else {
return None;
};
Some(namelike)
}
fn find_definition(
sema: &Semantics<RootDatabase>,
syntax: &SyntaxNode,
position: FilePosition,
) -> RenameResult<Definition> {
match find_name_like(sema, syntax, position)
match sema
.find_node_at_offset_with_descend(syntax, position.offset)
.ok_or_else(|| format_err!("No references found at position"))?
{
// renaming aliases would rename the item being aliased as the HIR doesn't track aliases yet
NameLike::Name(name)
ast::NameLike::Name(name)
if name.syntax().parent().map_or(false, |it| ast::Rename::can_cast(it.kind())) =>
{
bail!("Renaming aliases is currently unsupported")
}
NameLike::Name(name) => {
ast::NameLike::Name(name) => {
NameClass::classify(sema, &name).map(|class| class.referenced_or_defined(sema.db))
}
NameLike::NameRef(name_ref) => {
ast::NameLike::NameRef(name_ref) => {
NameRefClass::classify(sema, &name_ref).map(|class| class.referenced(sema.db))
}
NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime)
ast::NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime)
.map(|class| NameRefClass::referenced(class, sema.db))
.or_else(|| {
NameClass::classify_lifetime(sema, &lifetime)
@ -187,10 +166,12 @@ fn source_edit_from_references(
// if the ranges differ then the node is inside a macro call, we can't really attempt
// to make special rewrites like shorthand syntax and such, so just rename the node in
// the macro input
NameLike::NameRef(name_ref) if name_ref.syntax().text_range() == reference.range => {
ast::NameLike::NameRef(name_ref)
if name_ref.syntax().text_range() == reference.range =>
{
source_edit_from_name_ref(name_ref, new_name, def)
}
NameLike::Name(name) if name.syntax().text_range() == reference.range => {
ast::NameLike::Name(name) if name.syntax().text_range() == reference.range => {
source_edit_from_name(name, new_name)
}
_ => None,

View File

@ -52,34 +52,10 @@ impl IntoIterator for UsageSearchResult {
}
}
#[derive(Debug, Clone)]
pub enum NameLike {
NameRef(ast::NameRef),
Name(ast::Name),
Lifetime(ast::Lifetime),
}
impl NameLike {
pub fn as_name_ref(&self) -> Option<&ast::NameRef> {
match self {
NameLike::NameRef(name_ref) => Some(name_ref),
_ => None,
}
}
}
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 name: NameLike,
pub name: ast::NameLike,
pub access: Option<ReferenceAccess>,
}
@ -300,6 +276,7 @@ impl<'a> FindUsages<'a> {
pub fn in_scope(self, scope: SearchScope) -> FindUsages<'a> {
self.set_scope(Some(scope))
}
pub fn set_scope(mut self, scope: Option<SearchScope>) -> FindUsages<'a> {
assert!(self.scope.is_none());
self.scope = scope;
@ -355,18 +332,23 @@ impl<'a> FindUsages<'a> {
continue;
}
if let Some(name_ref) = sema.find_node_at_offset_with_descend(&tree, offset) {
if self.found_name_ref(&name_ref, sink) {
return;
}
} else if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) {
if self.found_name(&name, sink) {
return;
}
} else if let Some(lifetime) = sema.find_node_at_offset_with_descend(&tree, offset)
{
if self.found_lifetime(&lifetime, sink) {
return;
if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) {
match name {
ast::NameLike::NameRef(name_ref) => {
if self.found_name_ref(&name_ref, sink) {
return;
}
}
ast::NameLike::Name(name) => {
if self.found_name(&name, sink) {
return;
}
}
ast::NameLike::Lifetime(lifetime) => {
if self.found_lifetime(&lifetime, sink) {
return;
}
}
}
}
}
@ -383,7 +365,7 @@ impl<'a> FindUsages<'a> {
let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax());
let reference = FileReference {
range,
name: NameLike::Lifetime(lifetime.clone()),
name: ast::NameLike::Lifetime(lifetime.clone()),
access: None,
};
sink(file_id, reference)
@ -402,7 +384,7 @@ impl<'a> FindUsages<'a> {
let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
let reference = FileReference {
range,
name: NameLike::NameRef(name_ref.clone()),
name: ast::NameLike::NameRef(name_ref.clone()),
access: reference_access(&def, &name_ref),
};
sink(file_id, reference)
@ -412,12 +394,12 @@ impl<'a> FindUsages<'a> {
let reference = match self.def {
Definition::Field(_) if &field == self.def => FileReference {
range,
name: NameLike::NameRef(name_ref.clone()),
name: ast::NameLike::NameRef(name_ref.clone()),
access: reference_access(&field, &name_ref),
},
Definition::Local(l) if &local == l => FileReference {
range,
name: NameLike::NameRef(name_ref.clone()),
name: ast::NameLike::NameRef(name_ref.clone()),
access: reference_access(&Definition::Local(local), &name_ref),
},
_ => return false, // not a usage
@ -441,7 +423,7 @@ impl<'a> FindUsages<'a> {
let FileRange { file_id, range } = self.sema.original_range(name.syntax());
let reference = FileReference {
range,
name: NameLike::Name(name.clone()),
name: ast::NameLike::Name(name.clone()),
// FIXME: mutable patterns should have `Write` access
access: Some(ReferenceAccess::Read),
};

View File

@ -19,8 +19,8 @@ pub use self::{
expr_ext::{ArrayExprKind, BinOp, Effect, ElseBranch, LiteralKind, PrefixOp, RangeOp},
generated::{nodes::*, tokens::*},
node_ext::{
AttrKind, FieldKind, Macro, NameOrNameRef, PathSegmentKind, SelfParamKind,
SlicePatComponents, StructKind, TypeBoundKind, VisibilityKind,
AttrKind, FieldKind, Macro, NameLike, PathSegmentKind, SelfParamKind, SlicePatComponents,
StructKind, TypeBoundKind, VisibilityKind,
},
token_ext::*,
traits::*,

View File

@ -298,25 +298,66 @@ impl ast::RecordExprField {
}
#[derive(Debug, Clone, PartialEq)]
pub enum NameOrNameRef {
Name(ast::Name),
pub enum NameLike {
NameRef(ast::NameRef),
Name(ast::Name),
Lifetime(ast::Lifetime),
}
impl fmt::Display for NameOrNameRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
impl NameLike {
pub fn as_name_ref(&self) -> Option<&ast::NameRef> {
match self {
NameOrNameRef::Name(it) => fmt::Display::fmt(it, f),
NameOrNameRef::NameRef(it) => fmt::Display::fmt(it, f),
NameLike::NameRef(name_ref) => Some(name_ref),
_ => None,
}
}
}
impl ast::AstNode for NameLike {
fn can_cast(kind: SyntaxKind) -> bool {
matches!(kind, SyntaxKind::NAME | SyntaxKind::NAME_REF | SyntaxKind::LIFETIME)
}
fn cast(syntax: SyntaxNode) -> Option<Self> {
let res = match syntax.kind() {
SyntaxKind::NAME => NameLike::Name(ast::Name { syntax }),
SyntaxKind::NAME_REF => NameLike::NameRef(ast::NameRef { syntax }),
SyntaxKind::LIFETIME => NameLike::Lifetime(ast::Lifetime { syntax }),
_ => return None,
};
Some(res)
}
fn syntax(&self) -> &SyntaxNode {
match self {
NameLike::NameRef(it) => it.syntax(),
NameLike::Name(it) => it.syntax(),
NameLike::Lifetime(it) => it.syntax(),
}
}
}
impl fmt::Display for NameLike {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
NameLike::Name(it) => fmt::Display::fmt(it, f),
NameLike::NameRef(it) => fmt::Display::fmt(it, f),
NameLike::Lifetime(it) => fmt::Display::fmt(it, f),
}
}
}
mod __ {
use super::{
ast::{Lifetime, Name, NameRef},
NameLike,
};
stdx::impl_from!(NameRef, Name, Lifetime for NameLike);
}
impl ast::RecordPatField {
pub fn for_field_name_ref(field_name: &ast::NameRef) -> Option<ast::RecordPatField> {
let candidate = field_name.syntax().parent().and_then(ast::RecordPatField::cast)?;
match candidate.field_name()? {
NameOrNameRef::NameRef(name_ref) if name_ref == *field_name => Some(candidate),
NameLike::NameRef(name_ref) if name_ref == *field_name => Some(candidate),
_ => None,
}
}
@ -325,19 +366,19 @@ impl ast::RecordPatField {
let candidate =
field_name.syntax().ancestors().nth(2).and_then(ast::RecordPatField::cast)?;
match candidate.field_name()? {
NameOrNameRef::Name(name) if name == *field_name => Some(candidate),
NameLike::Name(name) if name == *field_name => Some(candidate),
_ => None,
}
}
/// Deals with field init shorthand
pub fn field_name(&self) -> Option<NameOrNameRef> {
pub fn field_name(&self) -> Option<NameLike> {
if let Some(name_ref) = self.name_ref() {
return Some(NameOrNameRef::NameRef(name_ref));
return Some(NameLike::NameRef(name_ref));
}
if let Some(ast::Pat::IdentPat(pat)) = self.pat() {
let name = pat.name()?;
return Some(NameOrNameRef::Name(name));
return Some(NameLike::Name(name));
}
None
}