mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-16 17:58:16 +00:00
Merge #7358
7358: Refactor reference searching to work with the ast r=matklad a=Veykril Addresses #4290 This PR is still a bit unpolished. Its main purpose for now is to discuss the direction of the changes as to whether this seems to be the right approach or not. I annotated a few parts with reviews to give a better overwiew without having to read into it too much. Big part of the diff are test output changes in the `references` module. Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
commit
88253907f4
8 changed files with 460 additions and 426 deletions
|
@ -1,7 +1,6 @@
|
|||
use ide_db::{
|
||||
defs::Definition,
|
||||
search::{FileReference, ReferenceKind},
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use ide_db::{defs::Definition, search::FileReference};
|
||||
use syntax::{
|
||||
ast::{self, AstNode, AstToken},
|
||||
TextRange,
|
||||
|
@ -68,44 +67,51 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
|
|||
|
||||
let wrap_in_parens = usages
|
||||
.references
|
||||
.values()
|
||||
.flatten()
|
||||
.map(|&FileReference { range, .. }| {
|
||||
let usage_node =
|
||||
ctx.covering_node_for_range(range).ancestors().find_map(ast::PathExpr::cast)?;
|
||||
let usage_parent_option = usage_node.syntax().parent().and_then(ast::Expr::cast);
|
||||
let usage_parent = match usage_parent_option {
|
||||
Some(u) => u,
|
||||
None => return Ok(false),
|
||||
};
|
||||
.iter()
|
||||
.map(|(&file_id, refs)| {
|
||||
refs.iter()
|
||||
.map(|&FileReference { range, .. }| {
|
||||
let usage_node = ctx
|
||||
.covering_node_for_range(range)
|
||||
.ancestors()
|
||||
.find_map(ast::PathExpr::cast)?;
|
||||
let usage_parent_option =
|
||||
usage_node.syntax().parent().and_then(ast::Expr::cast);
|
||||
let usage_parent = match usage_parent_option {
|
||||
Some(u) => u,
|
||||
None => return Ok(false),
|
||||
};
|
||||
|
||||
Ok(!matches!(
|
||||
(&initializer_expr, usage_parent),
|
||||
(ast::Expr::CallExpr(_), _)
|
||||
| (ast::Expr::IndexExpr(_), _)
|
||||
| (ast::Expr::MethodCallExpr(_), _)
|
||||
| (ast::Expr::FieldExpr(_), _)
|
||||
| (ast::Expr::TryExpr(_), _)
|
||||
| (ast::Expr::RefExpr(_), _)
|
||||
| (ast::Expr::Literal(_), _)
|
||||
| (ast::Expr::TupleExpr(_), _)
|
||||
| (ast::Expr::ArrayExpr(_), _)
|
||||
| (ast::Expr::ParenExpr(_), _)
|
||||
| (ast::Expr::PathExpr(_), _)
|
||||
| (ast::Expr::BlockExpr(_), _)
|
||||
| (ast::Expr::EffectExpr(_), _)
|
||||
| (_, ast::Expr::CallExpr(_))
|
||||
| (_, ast::Expr::TupleExpr(_))
|
||||
| (_, ast::Expr::ArrayExpr(_))
|
||||
| (_, ast::Expr::ParenExpr(_))
|
||||
| (_, ast::Expr::ForExpr(_))
|
||||
| (_, ast::Expr::WhileExpr(_))
|
||||
| (_, ast::Expr::BreakExpr(_))
|
||||
| (_, ast::Expr::ReturnExpr(_))
|
||||
| (_, ast::Expr::MatchExpr(_))
|
||||
))
|
||||
Ok(!matches!(
|
||||
(&initializer_expr, usage_parent),
|
||||
(ast::Expr::CallExpr(_), _)
|
||||
| (ast::Expr::IndexExpr(_), _)
|
||||
| (ast::Expr::MethodCallExpr(_), _)
|
||||
| (ast::Expr::FieldExpr(_), _)
|
||||
| (ast::Expr::TryExpr(_), _)
|
||||
| (ast::Expr::RefExpr(_), _)
|
||||
| (ast::Expr::Literal(_), _)
|
||||
| (ast::Expr::TupleExpr(_), _)
|
||||
| (ast::Expr::ArrayExpr(_), _)
|
||||
| (ast::Expr::ParenExpr(_), _)
|
||||
| (ast::Expr::PathExpr(_), _)
|
||||
| (ast::Expr::BlockExpr(_), _)
|
||||
| (ast::Expr::EffectExpr(_), _)
|
||||
| (_, ast::Expr::CallExpr(_))
|
||||
| (_, ast::Expr::TupleExpr(_))
|
||||
| (_, ast::Expr::ArrayExpr(_))
|
||||
| (_, ast::Expr::ParenExpr(_))
|
||||
| (_, ast::Expr::ForExpr(_))
|
||||
| (_, ast::Expr::WhileExpr(_))
|
||||
| (_, ast::Expr::BreakExpr(_))
|
||||
| (_, ast::Expr::ReturnExpr(_))
|
||||
| (_, ast::Expr::MatchExpr(_))
|
||||
))
|
||||
})
|
||||
.collect::<Result<_, _>>()
|
||||
.map(|b| (file_id, b))
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
.collect::<Result<HashMap<_, Vec<_>>, _>>()?;
|
||||
|
||||
let init_str = initializer_expr.syntax().text().to_string();
|
||||
let init_in_paren = format!("({})", &init_str);
|
||||
|
@ -117,16 +123,19 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
|
|||
target,
|
||||
move |builder| {
|
||||
builder.delete(delete_range);
|
||||
for (reference, should_wrap) in usages.references.values().flatten().zip(wrap_in_parens)
|
||||
{
|
||||
let replacement =
|
||||
if should_wrap { init_in_paren.clone() } else { init_str.clone() };
|
||||
match reference.kind {
|
||||
ReferenceKind::FieldShorthandForLocal => {
|
||||
mark::hit!(inline_field_shorthand);
|
||||
builder.insert(reference.range.end(), format!(": {}", replacement))
|
||||
for (file_id, references) in usages.references {
|
||||
for (&should_wrap, reference) in wrap_in_parens[&file_id].iter().zip(references) {
|
||||
let replacement =
|
||||
if should_wrap { init_in_paren.clone() } else { init_str.clone() };
|
||||
match reference.name.as_name_ref() {
|
||||
Some(name_ref)
|
||||
if ast::RecordExprField::for_field_name(name_ref).is_some() =>
|
||||
{
|
||||
mark::hit!(inline_field_shorthand);
|
||||
builder.insert(reference.range.end(), format!(": {}", replacement));
|
||||
}
|
||||
_ => builder.replace(reference.range, replacement),
|
||||
}
|
||||
_ => builder.replace(reference.range, replacement),
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -47,11 +47,11 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
|
|||
|
||||
let mut calls = CallLocations::default();
|
||||
|
||||
for (&file_id, references) in refs.references().iter() {
|
||||
for (file_id, references) in refs.references {
|
||||
let file = sema.parse(file_id);
|
||||
let file = file.syntax();
|
||||
for reference in references {
|
||||
let token = file.token_at_offset(reference.range.start()).next()?;
|
||||
for (r_range, _) in references {
|
||||
let token = file.token_at_offset(r_range.start()).next()?;
|
||||
let token = sema.descend_into_macros(token);
|
||||
let syntax = token.parent();
|
||||
|
||||
|
@ -61,7 +61,7 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
|
|||
let def = sema.to_def(&fn_)?;
|
||||
def.try_to_nav(sema.db)
|
||||
}) {
|
||||
let relative_range = reference.range;
|
||||
let relative_range = r_range;
|
||||
calls.add(&nav, relative_range);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ pub use crate::{
|
|||
inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
|
||||
markup::Markup,
|
||||
prime_caches::PrimeCachesProgress,
|
||||
references::{rename::RenameError, Declaration, ReferenceSearchResult},
|
||||
references::{rename::RenameError, ReferenceSearchResult},
|
||||
runnables::{Runnable, RunnableKind, TestId},
|
||||
syntax_highlighting::{
|
||||
tags::{Highlight, HlMod, HlMods, HlPunct, HlTag},
|
||||
|
@ -94,7 +94,7 @@ pub use ide_db::{
|
|||
call_info::CallInfo,
|
||||
label::Label,
|
||||
line_index::{LineCol, LineIndex},
|
||||
search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope},
|
||||
search::{ReferenceAccess, SearchScope},
|
||||
source_change::{FileSystemEdit, SourceChange},
|
||||
symbol_index::Query,
|
||||
RootDatabase,
|
||||
|
|
|
@ -11,14 +11,14 @@
|
|||
|
||||
pub(crate) mod rename;
|
||||
|
||||
use either::Either;
|
||||
use hir::Semantics;
|
||||
use ide_db::{
|
||||
base_db::FileId,
|
||||
defs::{Definition, NameClass, NameRefClass},
|
||||
search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope, UsageSearchResult},
|
||||
search::{ReferenceAccess, SearchScope},
|
||||
RootDatabase,
|
||||
};
|
||||
use rustc_hash::FxHashMap;
|
||||
use syntax::{
|
||||
algo::find_node_at_offset,
|
||||
ast::{self, NameOwner},
|
||||
|
@ -29,52 +29,16 @@ use crate::{display::TryToNav, FilePosition, NavigationTarget};
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ReferenceSearchResult {
|
||||
declaration: Declaration,
|
||||
references: UsageSearchResult,
|
||||
pub declaration: Declaration,
|
||||
pub references: FxHashMap<FileId, Vec<(TextRange, Option<ReferenceAccess>)>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Declaration {
|
||||
pub nav: NavigationTarget,
|
||||
pub kind: ReferenceKind,
|
||||
pub access: Option<ReferenceAccess>,
|
||||
}
|
||||
|
||||
impl ReferenceSearchResult {
|
||||
pub fn references(&self) -> &UsageSearchResult {
|
||||
&self.references
|
||||
}
|
||||
|
||||
pub fn references_with_declaration(mut self) -> UsageSearchResult {
|
||||
let decl_ref = FileReference {
|
||||
range: self.declaration.nav.focus_or_full_range(),
|
||||
kind: self.declaration.kind,
|
||||
access: self.declaration.access,
|
||||
};
|
||||
let file_id = self.declaration.nav.file_id;
|
||||
self.references.references.entry(file_id).or_default().push(decl_ref);
|
||||
self.references
|
||||
}
|
||||
|
||||
/// Total number of references
|
||||
/// At least 1 since all valid references should
|
||||
/// Have a declaration
|
||||
pub fn len(&self) -> usize {
|
||||
self.references.len() + 1
|
||||
}
|
||||
}
|
||||
|
||||
// allow turning ReferenceSearchResult into an iterator
|
||||
// over References
|
||||
impl IntoIterator for ReferenceSearchResult {
|
||||
type Item = (FileId, Vec<FileReference>);
|
||||
type IntoIter = std::collections::hash_map::IntoIter<FileId, Vec<FileReference>>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.references_with_declaration().into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn find_all_refs(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
position: FilePosition,
|
||||
|
@ -83,83 +47,72 @@ pub(crate) fn find_all_refs(
|
|||
let _p = profile::span("find_all_refs");
|
||||
let syntax = sema.parse(position.file_id).syntax().clone();
|
||||
|
||||
let (opt_name, search_kind) = if let Some(name) =
|
||||
let (opt_name, ctor_filter): (_, Option<fn(&_) -> bool>) = if let Some(name) =
|
||||
get_struct_def_name_for_struct_literal_search(&sema, &syntax, position)
|
||||
{
|
||||
(Some(name), ReferenceKind::StructLiteral)
|
||||
(
|
||||
Some(name),
|
||||
Some(|name_ref| is_record_lit_name_ref(name_ref) || is_call_expr_name_ref(name_ref)),
|
||||
)
|
||||
} else if let Some(name) = get_enum_def_name_for_struct_literal_search(&sema, &syntax, position)
|
||||
{
|
||||
(Some(name), ReferenceKind::EnumLiteral)
|
||||
(Some(name), Some(is_enum_lit_name_ref))
|
||||
} else {
|
||||
(
|
||||
sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset),
|
||||
ReferenceKind::Other,
|
||||
)
|
||||
(sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset), None)
|
||||
};
|
||||
|
||||
let def = find_name(&sema, &syntax, position, opt_name)?;
|
||||
let def = find_def(&sema, &syntax, position, opt_name)?;
|
||||
|
||||
let mut usages = def.usages(sema).set_scope(search_scope).all();
|
||||
usages
|
||||
.references
|
||||
.values_mut()
|
||||
.for_each(|it| it.retain(|r| search_kind == ReferenceKind::Other || search_kind == r.kind));
|
||||
usages.references.retain(|_, it| !it.is_empty());
|
||||
|
||||
if let Some(ctor_filter) = ctor_filter {
|
||||
// filter for constructor-literals
|
||||
usages.references.values_mut().for_each(|it| {
|
||||
it.retain(|reference| reference.name.as_name_ref().map_or(false, ctor_filter));
|
||||
});
|
||||
usages.references.retain(|_, it| !it.is_empty());
|
||||
}
|
||||
let nav = def.try_to_nav(sema.db)?;
|
||||
let decl_range = nav.focus_or_full_range();
|
||||
|
||||
let mut kind = ReferenceKind::Other;
|
||||
if let Definition::Local(local) = def {
|
||||
match local.source(sema.db).value {
|
||||
Either::Left(pat) => {
|
||||
if matches!(
|
||||
pat.syntax().parent().and_then(ast::RecordPatField::cast),
|
||||
Some(pat_field) if pat_field.name_ref().is_none()
|
||||
) {
|
||||
kind = ReferenceKind::FieldShorthandForLocal;
|
||||
}
|
||||
}
|
||||
Either::Right(_) => kind = ReferenceKind::SelfParam,
|
||||
}
|
||||
} else if matches!(
|
||||
def,
|
||||
Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_)
|
||||
) {
|
||||
kind = ReferenceKind::Lifetime;
|
||||
};
|
||||
let declaration = Declaration { nav, access: decl_access(&def, &syntax, decl_range) };
|
||||
let references = usages
|
||||
.into_iter()
|
||||
.map(|(file_id, refs)| {
|
||||
(file_id, refs.into_iter().map(|file_ref| (file_ref.range, file_ref.access)).collect())
|
||||
})
|
||||
.collect();
|
||||
|
||||
let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) };
|
||||
|
||||
Some(ReferenceSearchResult { declaration, references: usages })
|
||||
Some(ReferenceSearchResult { declaration, references })
|
||||
}
|
||||
|
||||
fn find_name(
|
||||
fn find_def(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
syntax: &SyntaxNode,
|
||||
position: FilePosition,
|
||||
opt_name: Option<ast::Name>,
|
||||
) -> Option<Definition> {
|
||||
let def = if let Some(name) = opt_name {
|
||||
NameClass::classify(sema, &name)?.referenced_or_defined(sema.db)
|
||||
if let Some(name) = opt_name {
|
||||
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)
|
||||
{
|
||||
if let Some(def) =
|
||||
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)
|
||||
{
|
||||
NameRefClass::classify(sema, &name_ref)?.referenced(sema.db)
|
||||
let class = NameRefClass::classify(sema, &name_ref)?;
|
||||
Some(class.referenced(sema.db))
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
Some(def)
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> {
|
||||
|
@ -235,6 +188,43 @@ fn get_enum_def_name_for_struct_literal_search(
|
|||
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)
|
||||
}
|
||||
|
||||
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_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)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use expect_test::{expect, Expect};
|
||||
|
@ -259,9 +249,9 @@ fn main() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
Foo Struct FileId(0) 0..26 7..10 Other
|
||||
Foo Struct FileId(0) 0..26 7..10
|
||||
|
||||
FileId(0) 101..104 StructLiteral
|
||||
FileId(0) 101..104
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -277,10 +267,10 @@ struct Foo$0 {}
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
Foo Struct FileId(0) 0..13 7..10 Other
|
||||
Foo Struct FileId(0) 0..13 7..10
|
||||
|
||||
FileId(0) 41..44 Other
|
||||
FileId(0) 54..57 StructLiteral
|
||||
FileId(0) 41..44
|
||||
FileId(0) 54..57
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -296,9 +286,9 @@ struct Foo<T> $0{}
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
Foo Struct FileId(0) 0..16 7..10 Other
|
||||
Foo Struct FileId(0) 0..16 7..10
|
||||
|
||||
FileId(0) 64..67 StructLiteral
|
||||
FileId(0) 64..67
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -315,9 +305,9 @@ fn main() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
Foo Struct FileId(0) 0..16 7..10 Other
|
||||
Foo Struct FileId(0) 0..16 7..10
|
||||
|
||||
FileId(0) 54..57 StructLiteral
|
||||
FileId(0) 54..57
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -336,9 +326,9 @@ fn main() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
Foo Enum FileId(0) 0..26 5..8 Other
|
||||
Foo Enum FileId(0) 0..26 5..8
|
||||
|
||||
FileId(0) 63..66 EnumLiteral
|
||||
FileId(0) 63..66
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -357,10 +347,10 @@ fn main() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
Foo Enum FileId(0) 0..26 5..8 Other
|
||||
Foo Enum FileId(0) 0..26 5..8
|
||||
|
||||
FileId(0) 50..53 Other
|
||||
FileId(0) 63..66 EnumLiteral
|
||||
FileId(0) 50..53
|
||||
FileId(0) 63..66
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -379,9 +369,9 @@ fn main() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
Foo Enum FileId(0) 0..32 5..8 Other
|
||||
Foo Enum FileId(0) 0..32 5..8
|
||||
|
||||
FileId(0) 73..76 EnumLiteral
|
||||
FileId(0) 73..76
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -400,9 +390,9 @@ fn main() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
Foo Enum FileId(0) 0..33 5..8 Other
|
||||
Foo Enum FileId(0) 0..33 5..8
|
||||
|
||||
FileId(0) 70..73 EnumLiteral
|
||||
FileId(0) 70..73
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -423,12 +413,12 @@ fn main() {
|
|||
i = 5;
|
||||
}"#,
|
||||
expect![[r#"
|
||||
i Local FileId(0) 20..25 24..25 Other Write
|
||||
i Local FileId(0) 20..25 24..25 Write
|
||||
|
||||
FileId(0) 50..51 Other Write
|
||||
FileId(0) 54..55 Other Read
|
||||
FileId(0) 76..77 Other Write
|
||||
FileId(0) 94..95 Other Write
|
||||
FileId(0) 50..51 Write
|
||||
FileId(0) 54..55 Read
|
||||
FileId(0) 76..77 Write
|
||||
FileId(0) 94..95 Write
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -447,10 +437,10 @@ fn bar() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
spam Local FileId(0) 19..23 19..23 Other
|
||||
spam Local FileId(0) 19..23 19..23
|
||||
|
||||
FileId(0) 34..38 Other Read
|
||||
FileId(0) 41..45 Other Read
|
||||
FileId(0) 34..38 Read
|
||||
FileId(0) 41..45 Read
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -462,9 +452,9 @@ fn bar() {
|
|||
fn foo(i : u32) -> u32 { i$0 }
|
||||
"#,
|
||||
expect![[r#"
|
||||
i ValueParam FileId(0) 7..8 7..8 Other
|
||||
i ValueParam FileId(0) 7..8 7..8
|
||||
|
||||
FileId(0) 25..26 Other Read
|
||||
FileId(0) 25..26 Read
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -476,9 +466,9 @@ fn foo(i : u32) -> u32 { i$0 }
|
|||
fn foo(i$0 : u32) -> u32 { i }
|
||||
"#,
|
||||
expect![[r#"
|
||||
i ValueParam FileId(0) 7..8 7..8 Other
|
||||
i ValueParam FileId(0) 7..8 7..8
|
||||
|
||||
FileId(0) 25..26 Other Read
|
||||
FileId(0) 25..26 Read
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -497,9 +487,9 @@ fn main(s: Foo) {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
spam Field FileId(0) 17..30 21..25 Other
|
||||
spam Field FileId(0) 17..30 21..25
|
||||
|
||||
FileId(0) 67..71 Other Read
|
||||
FileId(0) 67..71 Read
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -514,7 +504,7 @@ impl Foo {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
f Function FileId(0) 27..43 30..31 Other
|
||||
f Function FileId(0) 27..43 30..31
|
||||
|
||||
"#]],
|
||||
);
|
||||
|
@ -531,7 +521,7 @@ enum Foo {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
B Variant FileId(0) 22..23 22..23 Other
|
||||
B Variant FileId(0) 22..23 22..23
|
||||
|
||||
"#]],
|
||||
);
|
||||
|
@ -548,7 +538,7 @@ enum Foo {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
field Field FileId(0) 26..35 26..31 Other
|
||||
field Field FileId(0) 26..35 26..31
|
||||
|
||||
"#]],
|
||||
);
|
||||
|
@ -589,10 +579,10 @@ fn f() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
Foo Struct FileId(1) 17..51 28..31 Other
|
||||
Foo Struct FileId(1) 17..51 28..31
|
||||
|
||||
FileId(0) 53..56 StructLiteral
|
||||
FileId(2) 79..82 StructLiteral
|
||||
FileId(0) 53..56
|
||||
FileId(2) 79..82
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -619,9 +609,9 @@ pub struct Foo {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
foo Module FileId(1) 0..35 Other
|
||||
foo Module FileId(1) 0..35
|
||||
|
||||
FileId(0) 14..17 Other
|
||||
FileId(0) 14..17
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -647,10 +637,10 @@ pub(super) struct Foo$0 {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
Foo Struct FileId(2) 0..41 18..21 Other
|
||||
Foo Struct FileId(2) 0..41 18..21
|
||||
|
||||
FileId(1) 20..23 Other
|
||||
FileId(1) 47..50 StructLiteral
|
||||
FileId(1) 20..23
|
||||
FileId(1) 47..50
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -675,10 +665,10 @@ pub(super) struct Foo$0 {
|
|||
code,
|
||||
None,
|
||||
expect![[r#"
|
||||
quux Function FileId(0) 19..35 26..30 Other
|
||||
quux Function FileId(0) 19..35 26..30
|
||||
|
||||
FileId(1) 16..20 StructLiteral
|
||||
FileId(2) 16..20 StructLiteral
|
||||
FileId(1) 16..20
|
||||
FileId(2) 16..20
|
||||
"#]],
|
||||
);
|
||||
|
||||
|
@ -686,9 +676,9 @@ pub(super) struct Foo$0 {
|
|||
code,
|
||||
Some(SearchScope::single_file(FileId(2))),
|
||||
expect![[r#"
|
||||
quux Function FileId(0) 19..35 26..30 Other
|
||||
quux Function FileId(0) 19..35 26..30
|
||||
|
||||
FileId(2) 16..20 StructLiteral
|
||||
FileId(2) 16..20
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -706,10 +696,10 @@ fn foo() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
m1 Macro FileId(0) 0..46 29..31 Other
|
||||
m1 Macro FileId(0) 0..46 29..31
|
||||
|
||||
FileId(0) 63..65 StructLiteral
|
||||
FileId(0) 73..75 StructLiteral
|
||||
FileId(0) 63..65
|
||||
FileId(0) 73..75
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -724,10 +714,10 @@ fn foo() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
i Local FileId(0) 19..24 23..24 Other Write
|
||||
i Local FileId(0) 19..24 23..24 Write
|
||||
|
||||
FileId(0) 34..35 Other Write
|
||||
FileId(0) 38..39 Other Read
|
||||
FileId(0) 34..35 Write
|
||||
FileId(0) 38..39 Read
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -746,10 +736,10 @@ fn foo() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
f Field FileId(0) 15..21 15..16 Other
|
||||
f Field FileId(0) 15..21 15..16
|
||||
|
||||
FileId(0) 55..56 RecordFieldExprOrPat Read
|
||||
FileId(0) 68..69 Other Write
|
||||
FileId(0) 55..56 Read
|
||||
FileId(0) 68..69 Write
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -764,9 +754,9 @@ fn foo() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
i Local FileId(0) 19..20 19..20 Other
|
||||
i Local FileId(0) 19..20 19..20
|
||||
|
||||
FileId(0) 26..27 Other Write
|
||||
FileId(0) 26..27 Write
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -788,9 +778,9 @@ fn main() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
new Function FileId(0) 54..81 61..64 Other
|
||||
new Function FileId(0) 54..81 61..64
|
||||
|
||||
FileId(0) 126..129 StructLiteral
|
||||
FileId(0) 126..129
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -810,10 +800,10 @@ use crate::f;
|
|||
fn g() { f(); }
|
||||
"#,
|
||||
expect![[r#"
|
||||
f Function FileId(0) 22..31 25..26 Other
|
||||
f Function FileId(0) 22..31 25..26
|
||||
|
||||
FileId(1) 11..12 Other
|
||||
FileId(1) 24..25 StructLiteral
|
||||
FileId(1) 11..12
|
||||
FileId(1) 24..25
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -833,9 +823,9 @@ fn f(s: S) {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
field Field FileId(0) 15..24 15..20 Other
|
||||
field Field FileId(0) 15..24 15..20
|
||||
|
||||
FileId(0) 68..73 FieldShorthandForField Read
|
||||
FileId(0) 68..73 Read
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -857,9 +847,9 @@ fn f(e: En) {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
field Field FileId(0) 32..41 32..37 Other
|
||||
field Field FileId(0) 32..41 32..37
|
||||
|
||||
FileId(0) 102..107 FieldShorthandForField Read
|
||||
FileId(0) 102..107 Read
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -881,9 +871,9 @@ fn f() -> m::En {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
field Field FileId(0) 56..65 56..61 Other
|
||||
field Field FileId(0) 56..65 56..61
|
||||
|
||||
FileId(0) 125..130 RecordFieldExprOrPat Read
|
||||
FileId(0) 125..130 Read
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -906,10 +896,10 @@ impl Foo {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
self SelfParam FileId(0) 47..51 47..51 SelfParam
|
||||
self SelfParam FileId(0) 47..51 47..51
|
||||
|
||||
FileId(0) 71..75 Other Read
|
||||
FileId(0) 152..156 Other Read
|
||||
FileId(0) 71..75 Read
|
||||
FileId(0) 152..156 Read
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -927,9 +917,9 @@ impl Foo {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
self SelfParam FileId(0) 47..51 47..51 SelfParam
|
||||
self SelfParam FileId(0) 47..51 47..51
|
||||
|
||||
FileId(0) 63..67 Other Read
|
||||
FileId(0) 63..67 Read
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -945,7 +935,7 @@ impl Foo {
|
|||
let mut actual = String::new();
|
||||
{
|
||||
let decl = refs.declaration;
|
||||
format_to!(actual, "{} {:?}", decl.nav.debug_render(), decl.kind);
|
||||
format_to!(actual, "{}", decl.nav.debug_render());
|
||||
if let Some(access) = decl.access {
|
||||
format_to!(actual, " {:?}", access)
|
||||
}
|
||||
|
@ -953,9 +943,9 @@ impl Foo {
|
|||
}
|
||||
|
||||
for (file_id, references) in refs.references {
|
||||
for r in references {
|
||||
format_to!(actual, "{:?} {:?} {:?}", file_id, r.range, r.kind);
|
||||
if let Some(access) = r.access {
|
||||
for (range, access) in references {
|
||||
format_to!(actual, "{:?} {:?}", file_id, range);
|
||||
if let Some(access) = access {
|
||||
format_to!(actual, " {:?}", access);
|
||||
}
|
||||
actual += "\n";
|
||||
|
@ -976,13 +966,13 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
'a LifetimeParam FileId(0) 55..57 55..57 Lifetime
|
||||
'a LifetimeParam FileId(0) 55..57 55..57
|
||||
|
||||
FileId(0) 63..65 Lifetime
|
||||
FileId(0) 71..73 Lifetime
|
||||
FileId(0) 82..84 Lifetime
|
||||
FileId(0) 95..97 Lifetime
|
||||
FileId(0) 106..108 Lifetime
|
||||
FileId(0) 63..65
|
||||
FileId(0) 71..73
|
||||
FileId(0) 82..84
|
||||
FileId(0) 95..97
|
||||
FileId(0) 106..108
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -994,10 +984,10 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> {
|
|||
type Foo<'a, T> where T: 'a$0 = &'a T;
|
||||
"#,
|
||||
expect![[r#"
|
||||
'a LifetimeParam FileId(0) 9..11 9..11 Lifetime
|
||||
'a LifetimeParam FileId(0) 9..11 9..11
|
||||
|
||||
FileId(0) 25..27 Lifetime
|
||||
FileId(0) 31..33 Lifetime
|
||||
FileId(0) 25..27
|
||||
FileId(0) 31..33
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -1016,11 +1006,11 @@ impl<'a> Foo<'a> for &'a () {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
'a LifetimeParam FileId(0) 47..49 47..49 Lifetime
|
||||
'a LifetimeParam FileId(0) 47..49 47..49
|
||||
|
||||
FileId(0) 55..57 Lifetime
|
||||
FileId(0) 64..66 Lifetime
|
||||
FileId(0) 89..91 Lifetime
|
||||
FileId(0) 55..57
|
||||
FileId(0) 64..66
|
||||
FileId(0) 89..91
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -1036,9 +1026,9 @@ fn main() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
a Local FileId(0) 59..60 59..60 Other
|
||||
a Local FileId(0) 59..60 59..60
|
||||
|
||||
FileId(0) 80..81 Other Read
|
||||
FileId(0) 80..81 Read
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -1054,9 +1044,9 @@ fn main() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
a Local FileId(0) 59..60 59..60 Other
|
||||
a Local FileId(0) 59..60 59..60
|
||||
|
||||
FileId(0) 80..81 Other Read
|
||||
FileId(0) 80..81 Read
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -1075,10 +1065,10 @@ fn foo<'a>() -> &'a () {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
'a Label FileId(0) 29..32 29..31 Lifetime
|
||||
'a Label FileId(0) 29..32 29..31
|
||||
|
||||
FileId(0) 80..82 Lifetime
|
||||
FileId(0) 108..110 Lifetime
|
||||
FileId(0) 80..82
|
||||
FileId(0) 108..110
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -1092,9 +1082,9 @@ fn foo<const FOO$0: usize>() -> usize {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
FOO ConstParam FileId(0) 7..23 13..16 Other
|
||||
FOO ConstParam FileId(0) 7..23 13..16
|
||||
|
||||
FileId(0) 42..45 Other
|
||||
FileId(0) 42..45
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -1108,9 +1098,9 @@ trait Foo {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
Self TypeParam FileId(0) 6..9 6..9 Other
|
||||
Self TypeParam FileId(0) 6..9 6..9
|
||||
|
||||
FileId(0) 26..30 Other
|
||||
FileId(0) 26..30
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
@ -1131,9 +1121,9 @@ impl Foo {
|
|||
|
||||
"#,
|
||||
expect![[r#"
|
||||
Bar Variant FileId(0) 11..16 11..14 Other
|
||||
Bar Variant FileId(0) 11..16 11..14
|
||||
|
||||
FileId(0) 89..92 Other
|
||||
FileId(0) 89..92
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@ use std::fmt::{self, Display};
|
|||
use either::Either;
|
||||
use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics};
|
||||
use ide_db::{
|
||||
base_db::{AnchoredPathBuf, FileId, FileRange},
|
||||
base_db::{AnchoredPathBuf, FileId},
|
||||
defs::{Definition, NameClass, NameRefClass},
|
||||
search::FileReference,
|
||||
search::{FileReference, NameLike},
|
||||
RootDatabase,
|
||||
};
|
||||
use stdx::never;
|
||||
|
@ -17,10 +17,7 @@ use syntax::{
|
|||
use test_utils::mark;
|
||||
use text_edit::TextEdit;
|
||||
|
||||
use crate::{
|
||||
display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, SourceChange,
|
||||
TextRange,
|
||||
};
|
||||
use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange};
|
||||
|
||||
type RenameResult<T> = Result<T, RenameError>;
|
||||
#[derive(Debug)]
|
||||
|
@ -41,6 +38,8 @@ macro_rules! bail {
|
|||
($($tokens:tt)*) => {return Err(format_err!($($tokens)*))}
|
||||
}
|
||||
|
||||
/// Prepares a rename. The sole job of this function is to return the TextRange of the thing that is
|
||||
/// being targeted for a rename.
|
||||
pub(crate) fn prepare_rename(
|
||||
db: &RootDatabase,
|
||||
position: FilePosition,
|
||||
|
@ -123,12 +122,6 @@ fn check_identifier(new_name: &str) -> RenameResult<IdentifierKind> {
|
|||
}
|
||||
}
|
||||
|
||||
enum NameLike {
|
||||
Name(ast::Name),
|
||||
NameRef(ast::NameRef),
|
||||
Lifetime(ast::Lifetime),
|
||||
}
|
||||
|
||||
fn find_name_like(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
syntax: &SyntaxNode,
|
||||
|
@ -171,72 +164,97 @@ fn find_definition(
|
|||
}
|
||||
|
||||
fn source_edit_from_references(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
_sema: &Semantics<RootDatabase>,
|
||||
file_id: FileId,
|
||||
references: &[FileReference],
|
||||
def: Definition,
|
||||
new_name: &str,
|
||||
) -> (FileId, TextEdit) {
|
||||
let mut edit = TextEdit::builder();
|
||||
for reference in references {
|
||||
let mut replacement_text = String::new();
|
||||
let range = match reference.kind {
|
||||
ReferenceKind::FieldShorthandForField => {
|
||||
mark::hit!(test_rename_struct_field_for_shorthand);
|
||||
replacement_text.push_str(new_name);
|
||||
replacement_text.push_str(": ");
|
||||
TextRange::new(reference.range.start(), reference.range.start())
|
||||
}
|
||||
ReferenceKind::FieldShorthandForLocal => {
|
||||
mark::hit!(test_rename_local_for_field_shorthand);
|
||||
replacement_text.push_str(": ");
|
||||
replacement_text.push_str(new_name);
|
||||
TextRange::new(reference.range.end(), reference.range.end())
|
||||
}
|
||||
ReferenceKind::RecordFieldExprOrPat => {
|
||||
mark::hit!(test_rename_field_expr_pat);
|
||||
replacement_text.push_str(new_name);
|
||||
edit_text_range_for_record_field_expr_or_pat(
|
||||
sema,
|
||||
FileRange { file_id, range: reference.range },
|
||||
new_name,
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
replacement_text.push_str(new_name);
|
||||
reference.range
|
||||
}
|
||||
let (range, replacement) = match &reference.name {
|
||||
NameLike::Name(_) => (None, format!("{}", new_name)),
|
||||
NameLike::NameRef(name_ref) => source_edit_from_name_ref(name_ref, new_name, def),
|
||||
NameLike::Lifetime(_) => (None, format!("{}", new_name)),
|
||||
};
|
||||
edit.replace(range, replacement_text);
|
||||
// FIXME: Some(range) will be incorrect when we are inside macros
|
||||
edit.replace(range.unwrap_or(reference.range), replacement);
|
||||
}
|
||||
(file_id, edit.finish())
|
||||
}
|
||||
|
||||
fn edit_text_range_for_record_field_expr_or_pat(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
file_range: FileRange,
|
||||
fn source_edit_from_name_ref(
|
||||
name_ref: &ast::NameRef,
|
||||
new_name: &str,
|
||||
) -> TextRange {
|
||||
let source_file = sema.parse(file_range.file_id);
|
||||
let file_syntax = source_file.syntax();
|
||||
let original_range = file_range.range;
|
||||
|
||||
syntax::algo::find_node_at_range::<ast::RecordExprField>(file_syntax, original_range)
|
||||
.and_then(|field_expr| match field_expr.expr().and_then(|e| e.name_ref()) {
|
||||
Some(name) if &name.to_string() == new_name => Some(field_expr.syntax().text_range()),
|
||||
_ => None,
|
||||
})
|
||||
.or_else(|| {
|
||||
syntax::algo::find_node_at_range::<ast::RecordPatField>(file_syntax, original_range)
|
||||
.and_then(|field_pat| match field_pat.pat() {
|
||||
Some(ast::Pat::IdentPat(pat))
|
||||
if pat.name().map(|n| n.to_string()).as_deref() == Some(new_name) =>
|
||||
{
|
||||
Some(field_pat.syntax().text_range())
|
||||
def: Definition,
|
||||
) -> (Option<TextRange>, String) {
|
||||
if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) {
|
||||
let rcf_name_ref = record_field.name_ref();
|
||||
let rcf_expr = record_field.expr();
|
||||
match (rcf_name_ref, rcf_expr.and_then(|it| it.name_ref())) {
|
||||
// field: init-expr, check if we can use a field init shorthand
|
||||
(Some(field_name), Some(init)) => {
|
||||
if field_name == *name_ref {
|
||||
if init.text() == new_name {
|
||||
mark::hit!(test_rename_field_put_init_shorthand);
|
||||
// same names, we can use a shorthand here instead
|
||||
// we do not want to erase attributes hence this range start
|
||||
let s = field_name.syntax().text_range().start();
|
||||
let e = record_field.syntax().text_range().end();
|
||||
return (Some(TextRange::new(s, e)), format!("{}", new_name));
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
})
|
||||
.unwrap_or(original_range)
|
||||
} else if init == *name_ref {
|
||||
if field_name.text() == new_name {
|
||||
mark::hit!(test_rename_local_put_init_shorthand);
|
||||
// same names, we can use a shorthand here instead
|
||||
// we do not want to erase attributes hence this range start
|
||||
let s = field_name.syntax().text_range().start();
|
||||
let e = record_field.syntax().text_range().end();
|
||||
return (Some(TextRange::new(s, e)), format!("{}", new_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
// init shorthand
|
||||
(None, Some(_)) => {
|
||||
// FIXME: instead of splitting the shorthand, recursively trigger a rename of the
|
||||
// other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
|
||||
match def {
|
||||
Definition::Field(_) => {
|
||||
mark::hit!(test_rename_field_in_field_shorthand);
|
||||
let s = name_ref.syntax().text_range().start();
|
||||
return (Some(TextRange::empty(s)), format!("{}: ", new_name));
|
||||
}
|
||||
Definition::Local(_) => {
|
||||
mark::hit!(test_rename_local_in_field_shorthand);
|
||||
let s = name_ref.syntax().text_range().end();
|
||||
return (Some(TextRange::empty(s)), format!(": {}", new_name));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) {
|
||||
let rcf_name_ref = record_field.name_ref();
|
||||
let rcf_pat = record_field.pat();
|
||||
match (rcf_name_ref, rcf_pat) {
|
||||
// field: rename
|
||||
(Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => {
|
||||
// field name is being renamed
|
||||
if pat.name().map_or(false, |it| it.text() == new_name) {
|
||||
mark::hit!(test_rename_field_put_init_shorthand_pat);
|
||||
// same names, we can use a shorthand here instead
|
||||
// we do not want to erase attributes hence this range start
|
||||
let s = field_name.syntax().text_range().start();
|
||||
let e = record_field.syntax().text_range().end();
|
||||
return (Some(TextRange::new(s, e)), format!("{}", new_name));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
(None, format!("{}", new_name))
|
||||
}
|
||||
|
||||
fn rename_mod(
|
||||
|
@ -277,7 +295,7 @@ fn rename_mod(
|
|||
let def = Definition::ModuleDef(ModuleDef::Module(module));
|
||||
let usages = def.usages(sema).all();
|
||||
let ref_edits = usages.iter().map(|(&file_id, references)| {
|
||||
source_edit_from_references(sema, file_id, references, new_name)
|
||||
source_edit_from_references(sema, file_id, references, def, new_name)
|
||||
});
|
||||
source_change.extend(ref_edits);
|
||||
|
||||
|
@ -346,7 +364,7 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe
|
|||
let usages = def.usages(sema).all();
|
||||
let mut source_change = SourceChange::default();
|
||||
source_change.extend(usages.iter().map(|(&file_id, references)| {
|
||||
source_edit_from_references(sema, file_id, references, "self")
|
||||
source_edit_from_references(sema, file_id, references, def, "self")
|
||||
}));
|
||||
source_change.insert_source_edit(
|
||||
file_id.original_file(sema.db),
|
||||
|
@ -403,7 +421,7 @@ fn rename_self_to_param(
|
|||
let mut source_change = SourceChange::default();
|
||||
source_change.insert_source_edit(file_id.original_file(sema.db), edit);
|
||||
source_change.extend(usages.iter().map(|(&file_id, references)| {
|
||||
source_edit_from_references(sema, file_id, &references, new_name)
|
||||
source_edit_from_references(sema, file_id, &references, def, new_name)
|
||||
}));
|
||||
Ok(source_change)
|
||||
}
|
||||
|
@ -457,7 +475,7 @@ fn rename_reference(
|
|||
}
|
||||
let mut source_change = SourceChange::default();
|
||||
source_change.extend(usages.iter().map(|(&file_id, references)| {
|
||||
source_edit_from_references(sema, file_id, &references, new_name)
|
||||
source_edit_from_references(sema, file_id, &references, def, new_name)
|
||||
}));
|
||||
|
||||
let (file_id, edit) = source_edit_from_def(sema, def, new_name)?;
|
||||
|
@ -545,10 +563,8 @@ mod tests {
|
|||
|
||||
fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) {
|
||||
let (analysis, position) = fixture::position(ra_fixture);
|
||||
let source_change = analysis
|
||||
.rename(position, new_name)
|
||||
.unwrap()
|
||||
.expect("Expect returned RangeInfo to be Some, but was None");
|
||||
let source_change =
|
||||
analysis.rename(position, new_name).unwrap().expect("Expect returned a RenameError");
|
||||
expect.assert_debug_eq(&source_change)
|
||||
}
|
||||
|
||||
|
@ -792,8 +808,8 @@ impl Foo {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_struct_field_for_shorthand() {
|
||||
mark::check!(test_rename_struct_field_for_shorthand);
|
||||
fn test_rename_field_in_field_shorthand() {
|
||||
mark::check!(test_rename_field_in_field_shorthand);
|
||||
check(
|
||||
"j",
|
||||
r#"
|
||||
|
@ -818,8 +834,8 @@ impl Foo {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_local_for_field_shorthand() {
|
||||
mark::check!(test_rename_local_for_field_shorthand);
|
||||
fn test_rename_local_in_field_shorthand() {
|
||||
mark::check!(test_rename_local_in_field_shorthand);
|
||||
check(
|
||||
"j",
|
||||
r#"
|
||||
|
@ -1417,8 +1433,8 @@ impl Foo {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_initializer_use_field_init_shorthand() {
|
||||
mark::check!(test_rename_field_expr_pat);
|
||||
fn test_rename_field_put_init_shorthand() {
|
||||
mark::check!(test_rename_field_put_init_shorthand);
|
||||
check(
|
||||
"bar",
|
||||
r#"
|
||||
|
@ -1438,8 +1454,31 @@ fn foo(bar: i32) -> Foo {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_local_put_init_shorthand() {
|
||||
mark::check!(test_rename_local_put_init_shorthand);
|
||||
check(
|
||||
"i",
|
||||
r#"
|
||||
struct Foo { i: i32 }
|
||||
|
||||
fn foo(bar$0: i32) -> Foo {
|
||||
Foo { i: bar }
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct Foo { i: i32 }
|
||||
|
||||
fn foo(i: i32) -> Foo {
|
||||
Foo { i }
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_struct_field_destructure_into_shorthand() {
|
||||
mark::check!(test_rename_field_put_init_shorthand_pat);
|
||||
check(
|
||||
"baz",
|
||||
r#"
|
||||
|
|
|
@ -53,22 +53,34 @@ impl IntoIterator for UsageSearchResult {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FileReference {
|
||||
pub range: TextRange,
|
||||
pub kind: ReferenceKind,
|
||||
pub access: Option<ReferenceAccess>,
|
||||
pub enum NameLike {
|
||||
NameRef(ast::NameRef),
|
||||
Name(ast::Name),
|
||||
Lifetime(ast::Lifetime),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ReferenceKind {
|
||||
FieldShorthandForField,
|
||||
FieldShorthandForLocal,
|
||||
StructLiteral,
|
||||
RecordFieldExprOrPat,
|
||||
SelfParam,
|
||||
EnumLiteral,
|
||||
Lifetime,
|
||||
Other,
|
||||
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 access: Option<ReferenceAccess>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
|
@ -369,8 +381,11 @@ 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: NameLike::Lifetime(lifetime.clone()),
|
||||
access: None,
|
||||
};
|
||||
sink(file_id, reference)
|
||||
}
|
||||
_ => false, // not a usage
|
||||
|
@ -384,19 +399,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: NameLike::NameRef(name_ref.clone()),
|
||||
access: reference_access(&def, &name_ref),
|
||||
};
|
||||
sink(file_id, reference)
|
||||
}
|
||||
Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
|
||||
|
@ -404,12 +412,12 @@ impl<'a> FindUsages<'a> {
|
|||
let reference = match self.def {
|
||||
Definition::Field(_) if &field == self.def => FileReference {
|
||||
range,
|
||||
kind: ReferenceKind::FieldShorthandForField,
|
||||
name: NameLike::NameRef(name_ref.clone()),
|
||||
access: reference_access(&field, &name_ref),
|
||||
},
|
||||
Definition::Local(l) if &local == l => FileReference {
|
||||
range,
|
||||
kind: ReferenceKind::FieldShorthandForLocal,
|
||||
name: NameLike::NameRef(name_ref.clone()),
|
||||
access: reference_access(&Definition::Local(local), &name_ref),
|
||||
},
|
||||
_ => return false, // not a usage
|
||||
|
@ -433,7 +441,7 @@ impl<'a> FindUsages<'a> {
|
|||
let FileRange { file_id, range } = self.sema.original_range(name.syntax());
|
||||
let reference = FileReference {
|
||||
range,
|
||||
kind: ReferenceKind::FieldShorthandForField,
|
||||
name: NameLike::Name(name.clone()),
|
||||
// FIXME: mutable patterns should have `Write` access
|
||||
access: Some(ReferenceAccess::Read),
|
||||
};
|
||||
|
@ -473,54 +481,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)
|
||||
}
|
||||
|
|
|
@ -827,18 +827,23 @@ pub(crate) fn handle_references(
|
|||
Some(refs) => refs,
|
||||
};
|
||||
|
||||
let locations = if params.context.include_declaration {
|
||||
refs.references_with_declaration()
|
||||
.file_ranges()
|
||||
.filter_map(|frange| to_proto::location(&snap, frange).ok())
|
||||
.collect()
|
||||
let decl = if params.context.include_declaration {
|
||||
Some(FileRange {
|
||||
file_id: refs.declaration.nav.file_id,
|
||||
range: refs.declaration.nav.focus_or_full_range(),
|
||||
})
|
||||
} else {
|
||||
// Only iterate over the references if include_declaration was false
|
||||
refs.references()
|
||||
.file_ranges()
|
||||
.filter_map(|frange| to_proto::location(&snap, frange).ok())
|
||||
.collect()
|
||||
None
|
||||
};
|
||||
let locations = refs
|
||||
.references
|
||||
.into_iter()
|
||||
.flat_map(|(file_id, refs)| {
|
||||
refs.into_iter().map(move |(range, _)| FileRange { file_id, range })
|
||||
})
|
||||
.chain(decl)
|
||||
.filter_map(|frange| to_proto::location(&snap, frange).ok())
|
||||
.collect();
|
||||
|
||||
Ok(Some(locations))
|
||||
}
|
||||
|
@ -1214,8 +1219,11 @@ pub(crate) fn handle_code_lens_resolve(
|
|||
.find_all_refs(position, None)
|
||||
.unwrap_or(None)
|
||||
.map(|r| {
|
||||
r.references()
|
||||
.file_ranges()
|
||||
r.references
|
||||
.into_iter()
|
||||
.flat_map(|(file_id, ranges)| {
|
||||
ranges.into_iter().map(move |(range, _)| FileRange { file_id, range })
|
||||
})
|
||||
.filter_map(|frange| to_proto::location(&snap, frange).ok())
|
||||
.collect_vec()
|
||||
})
|
||||
|
@ -1259,17 +1267,26 @@ pub(crate) fn handle_document_highlight(
|
|||
Some(refs) => refs,
|
||||
};
|
||||
|
||||
let decl = if refs.declaration.nav.file_id == position.file_id {
|
||||
Some(DocumentHighlight {
|
||||
range: to_proto::range(&line_index, refs.declaration.nav.focus_or_full_range()),
|
||||
kind: refs.declaration.access.map(to_proto::document_highlight_kind),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let res = refs
|
||||
.references_with_declaration()
|
||||
.references
|
||||
.get(&position.file_id)
|
||||
.map(|file_refs| {
|
||||
file_refs
|
||||
.into_iter()
|
||||
.map(|r| DocumentHighlight {
|
||||
range: to_proto::range(&line_index, r.range),
|
||||
kind: r.access.map(to_proto::document_highlight_kind),
|
||||
.map(|&(range, access)| DocumentHighlight {
|
||||
range: to_proto::range(&line_index, range),
|
||||
kind: access.map(to_proto::document_highlight_kind),
|
||||
})
|
||||
.chain(decl)
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
|
|
@ -274,10 +274,7 @@ impl ast::Struct {
|
|||
|
||||
impl ast::RecordExprField {
|
||||
pub fn for_field_name(field_name: &ast::NameRef) -> Option<ast::RecordExprField> {
|
||||
let candidate =
|
||||
field_name.syntax().parent().and_then(ast::RecordExprField::cast).or_else(|| {
|
||||
field_name.syntax().ancestors().nth(4).and_then(ast::RecordExprField::cast)
|
||||
})?;
|
||||
let candidate = Self::for_name_ref(field_name)?;
|
||||
if candidate.field_name().as_ref() == Some(field_name) {
|
||||
Some(candidate)
|
||||
} else {
|
||||
|
@ -285,6 +282,13 @@ impl ast::RecordExprField {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn for_name_ref(name_ref: &ast::NameRef) -> Option<ast::RecordExprField> {
|
||||
let syn = name_ref.syntax();
|
||||
syn.parent()
|
||||
.and_then(ast::RecordExprField::cast)
|
||||
.or_else(|| syn.ancestors().nth(4).and_then(ast::RecordExprField::cast))
|
||||
}
|
||||
|
||||
/// Deals with field init shorthand
|
||||
pub fn field_name(&self) -> Option<ast::NameRef> {
|
||||
if let Some(name_ref) = self.name_ref() {
|
||||
|
@ -294,6 +298,7 @@ impl ast::RecordExprField {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum NameOrNameRef {
|
||||
Name(ast::Name),
|
||||
NameRef(ast::NameRef),
|
||||
|
@ -309,6 +314,23 @@ impl fmt::Display for NameOrNameRef {
|
|||
}
|
||||
|
||||
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),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_field_name(field_name: &ast::Name) -> Option<ast::RecordPatField> {
|
||||
let candidate =
|
||||
field_name.syntax().ancestors().nth(3).and_then(ast::RecordPatField::cast)?;
|
||||
match candidate.field_name()? {
|
||||
NameOrNameRef::Name(name) if name == *field_name => Some(candidate),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Deals with field init shorthand
|
||||
pub fn field_name(&self) -> Option<NameOrNameRef> {
|
||||
if let Some(name_ref) = self.name_ref() {
|
||||
|
|
Loading…
Reference in a new issue