mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 00:03:43 +00:00
Merge #7699
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:
commit
4054525c41
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
};
|
||||
|
@ -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::*,
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user