Refactor reference searching to work with the ast

This commit is contained in:
Lukas Wirth
2021-02-07 18:38:12 +01:00
parent 43ccbf4360
commit d644728d82
8 changed files with 488 additions and 405 deletions

View File

@@ -1,7 +1,6 @@
use ide_db::{ use std::collections::HashMap;
defs::Definition,
search::{FileReference, ReferenceKind}, use ide_db::{defs::Definition, search::FileReference};
};
use syntax::{ use syntax::{
ast::{self, AstNode, AstToken}, ast::{self, AstNode, AstToken},
TextRange, TextRange,
@@ -68,12 +67,16 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
let wrap_in_parens = usages let wrap_in_parens = usages
.references .references
.values() .iter()
.flatten() .map(|(&file_id, refs)| {
refs.iter()
.map(|&FileReference { range, .. }| { .map(|&FileReference { range, .. }| {
let usage_node = let usage_node = ctx
ctx.covering_node_for_range(range).ancestors().find_map(ast::PathExpr::cast)?; .covering_node_for_range(range)
let usage_parent_option = usage_node.syntax().parent().and_then(ast::Expr::cast); .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 { let usage_parent = match usage_parent_option {
Some(u) => u, Some(u) => u,
None => return Ok(false), None => return Ok(false),
@@ -105,7 +108,10 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
| (_, ast::Expr::MatchExpr(_)) | (_, ast::Expr::MatchExpr(_))
)) ))
}) })
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<_, _>>()
.map(|b| (file_id, b))
})
.collect::<Result<HashMap<_, Vec<_>>, _>>()?;
let init_str = initializer_expr.syntax().text().to_string(); let init_str = initializer_expr.syntax().text().to_string();
let init_in_paren = format!("({})", &init_str); let init_in_paren = format!("({})", &init_str);
@@ -117,18 +123,22 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
target, target,
move |builder| { move |builder| {
builder.delete(delete_range); builder.delete(delete_range);
for (reference, should_wrap) in usages.references.values().flatten().zip(wrap_in_parens) for (file_id, references) in usages.references {
{ let root = ctx.sema.parse(file_id);
for (&should_wrap, reference) in wrap_in_parens[&file_id].iter().zip(references) {
let replacement = let replacement =
if should_wrap { init_in_paren.clone() } else { init_str.clone() }; if should_wrap { init_in_paren.clone() } else { init_str.clone() };
match reference.kind { match &reference.as_name_ref(root.syntax()) {
ReferenceKind::FieldShorthandForLocal => { Some(name_ref)
if ast::RecordExprField::for_field_name(name_ref).is_some() =>
{
mark::hit!(inline_field_shorthand); mark::hit!(inline_field_shorthand);
builder.insert(reference.range.end(), format!(": {}", replacement)) builder.insert(reference.range.end(), format!(": {}", replacement));
} }
_ => builder.replace(reference.range, replacement), _ => builder.replace(reference.range, replacement),
} }
} }
}
}, },
) )
} }

View File

@@ -47,11 +47,11 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
let mut calls = CallLocations::default(); 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 = sema.parse(file_id);
let file = file.syntax(); let file = file.syntax();
for reference in references { for (r_range, _) in references {
let token = file.token_at_offset(reference.range.start()).next()?; let token = file.token_at_offset(r_range.start()).next()?;
let token = sema.descend_into_macros(token); let token = sema.descend_into_macros(token);
let syntax = token.parent(); let syntax = token.parent();
@@ -61,7 +61,7 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
let def = sema.to_def(&fn_)?; let def = sema.to_def(&fn_)?;
def.try_to_nav(sema.db) def.try_to_nav(sema.db)
}) { }) {
let relative_range = reference.range; let relative_range = r_range;
calls.add(&nav, relative_range); calls.add(&nav, relative_range);
} }
} }

View File

@@ -73,7 +73,7 @@ pub use crate::{
inlay_hints::{InlayHint, InlayHintsConfig, InlayKind}, inlay_hints::{InlayHint, InlayHintsConfig, InlayKind},
markup::Markup, markup::Markup,
prime_caches::PrimeCachesProgress, prime_caches::PrimeCachesProgress,
references::{rename::RenameError, Declaration, ReferenceSearchResult}, references::{rename::RenameError, ReferenceSearchResult},
runnables::{Runnable, RunnableKind, TestId}, runnables::{Runnable, RunnableKind, TestId},
syntax_highlighting::{ syntax_highlighting::{
tags::{Highlight, HlMod, HlMods, HlPunct, HlTag}, tags::{Highlight, HlMod, HlMods, HlPunct, HlTag},
@@ -94,7 +94,7 @@ pub use ide_db::{
call_info::CallInfo, call_info::CallInfo,
label::Label, label::Label,
line_index::{LineCol, LineIndex}, line_index::{LineCol, LineIndex},
search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope}, search::{FileReference, ReferenceAccess, SearchScope},
source_change::{FileSystemEdit, SourceChange}, source_change::{FileSystemEdit, SourceChange},
symbol_index::Query, symbol_index::Query,
RootDatabase, RootDatabase,

View File

@@ -11,13 +11,14 @@
pub(crate) mod rename; pub(crate) mod rename;
use either::Either;
use hir::Semantics; use hir::Semantics;
use ide_db::{ use ide_db::{
base_db::FileId,
defs::{Definition, NameClass, NameRefClass}, defs::{Definition, NameClass, NameRefClass},
search::{FileReference, ReferenceAccess, ReferenceKind, SearchScope, UsageSearchResult}, search::{ReferenceAccess, SearchScope},
RootDatabase, RootDatabase,
}; };
use rustc_hash::FxHashMap;
use syntax::{ use syntax::{
algo::find_node_at_offset, algo::find_node_at_offset,
ast::{self, NameOwner}, ast::{self, NameOwner},
@@ -28,32 +29,14 @@ use crate::{display::TryToNav, FilePosition, NavigationTarget};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ReferenceSearchResult { pub struct ReferenceSearchResult {
declaration: Declaration, pub declaration: Declaration,
references: UsageSearchResult, pub references: FxHashMap<FileId, Vec<(TextRange, Option<ReferenceAccess>)>>,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Declaration { pub struct Declaration {
nav: NavigationTarget, pub nav: NavigationTarget,
kind: ReferenceKind, pub access: Option<ReferenceAccess>,
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
}
} }
pub(crate) fn find_all_refs( pub(crate) fn find_all_refs(
@@ -64,83 +47,76 @@ 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();
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) 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) } 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 { } else {
( (sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset), None)
sema.find_node_at_offset_with_descend::<ast::Name>(&syntax, position.offset),
ReferenceKind::Other,
)
}; };
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(); let mut usages = def.usages(sema).set_scope(search_scope).all();
usages if let Some(ctor_filter) = ctor_filter {
.references // filter for constructor-literals
.values_mut() usages.references.iter_mut().for_each(|(&file_id, it)| {
.for_each(|it| it.retain(|r| search_kind == ReferenceKind::Other || search_kind == r.kind)); let root = sema.parse(file_id);
let root = root.syntax();
it.retain(|reference| {
reference.as_name_ref(root).map_or(false, |name_ref| ctor_filter(&name_ref))
})
});
usages.references.retain(|_, it| !it.is_empty()); usages.references.retain(|_, it| !it.is_empty());
}
let nav = def.try_to_nav(sema.db)?; let nav = def.try_to_nav(sema.db)?;
let decl_range = nav.focus_or_full_range(); let decl_range = nav.focus_or_full_range();
let mut kind = ReferenceKind::Other; let declaration = Declaration { nav, access: decl_access(&def, &syntax, decl_range) };
if let Definition::Local(local) = def { let references = usages
match local.source(sema.db).value { .into_iter()
Either::Left(pat) => { .map(|(file_id, refs)| {
if matches!( (file_id, refs.into_iter().map(|file_ref| (file_ref.range, file_ref.access)).collect())
pat.syntax().parent().and_then(ast::RecordPatField::cast), })
Some(pat_field) if pat_field.name_ref().is_none() .collect();
) {
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, kind, access: decl_access(&def, &syntax, decl_range) }; Some(ReferenceSearchResult { declaration, references })
Some(ReferenceSearchResult { declaration, references: usages })
} }
fn find_name( fn find_def(
sema: &Semantics<RootDatabase>, sema: &Semantics<RootDatabase>,
syntax: &SyntaxNode, syntax: &SyntaxNode,
position: FilePosition, position: FilePosition,
opt_name: Option<ast::Name>, opt_name: Option<ast::Name>,
) -> Option<Definition> { ) -> Option<Definition> {
let def = if let Some(name) = opt_name { if let Some(name) = opt_name {
NameClass::classify(sema, &name)?.referenced_or_defined(sema.db) let class = NameClass::classify(sema, &name)?;
Some(class.referenced_or_defined(sema.db))
} else if let Some(lifetime) = } else if let Some(lifetime) =
sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset) 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)) NameRefClass::classify_lifetime(sema, &lifetime).map(|class| class.referenced(sema.db))
{ {
def def
} else { } else {
NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db) NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db)
} };
Some(def)
} else if let Some(name_ref) = } else if let Some(name_ref) =
sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset) 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 { } else {
return None; None
}; }
Some(def)
} }
fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> {
@@ -216,6 +192,43 @@ fn get_enum_def_name_for_struct_literal_search(
None 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)] #[cfg(test)]
mod tests { mod tests {
use expect_test::{expect, Expect}; use expect_test::{expect, Expect};
@@ -240,9 +253,9 @@ fn main() {
} }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -258,10 +271,10 @@ struct Foo$0 {}
} }
"#, "#,
expect![[r#" 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) 41..44
FileId(0) 54..57 StructLiteral FileId(0) 54..57
"#]], "#]],
); );
} }
@@ -277,9 +290,9 @@ struct Foo<T> $0{}
} }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -296,9 +309,9 @@ fn main() {
} }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -317,9 +330,9 @@ fn main() {
} }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -338,10 +351,10 @@ fn main() {
} }
"#, "#,
expect![[r#" 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) 50..53
FileId(0) 63..66 EnumLiteral FileId(0) 63..66
"#]], "#]],
); );
} }
@@ -360,9 +373,9 @@ fn main() {
} }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -381,9 +394,9 @@ fn main() {
} }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -404,12 +417,12 @@ fn main() {
i = 5; i = 5;
}"#, }"#,
expect![[r#" 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) 50..51 Write
FileId(0) 54..55 Other Read FileId(0) 54..55 Read
FileId(0) 76..77 Other Write FileId(0) 76..77 Write
FileId(0) 94..95 Other Write FileId(0) 94..95 Write
"#]], "#]],
); );
} }
@@ -428,10 +441,10 @@ fn bar() {
} }
"#, "#,
expect![[r#" 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) 34..38 Read
FileId(0) 41..45 Other Read FileId(0) 41..45 Read
"#]], "#]],
); );
} }
@@ -443,9 +456,9 @@ fn bar() {
fn foo(i : u32) -> u32 { i$0 } fn foo(i : u32) -> u32 { i$0 }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -457,9 +470,9 @@ fn foo(i : u32) -> u32 { i$0 }
fn foo(i$0 : u32) -> u32 { i } fn foo(i$0 : u32) -> u32 { i }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -478,9 +491,9 @@ fn main(s: Foo) {
} }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -495,7 +508,7 @@ impl Foo {
} }
"#, "#,
expect![[r#" expect![[r#"
f Function FileId(0) 27..43 30..31 Other f Function FileId(0) 27..43 30..31
"#]], "#]],
); );
@@ -512,7 +525,7 @@ enum Foo {
} }
"#, "#,
expect![[r#" expect![[r#"
B Variant FileId(0) 22..23 22..23 Other B Variant FileId(0) 22..23 22..23
"#]], "#]],
); );
@@ -529,7 +542,7 @@ enum Foo {
} }
"#, "#,
expect![[r#" expect![[r#"
field Field FileId(0) 26..35 26..31 Other field Field FileId(0) 26..35 26..31
"#]], "#]],
); );
@@ -570,10 +583,10 @@ fn f() {
} }
"#, "#,
expect![[r#" 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(0) 53..56
FileId(2) 79..82 StructLiteral FileId(2) 79..82
"#]], "#]],
); );
} }
@@ -600,9 +613,9 @@ pub struct Foo {
} }
"#, "#,
expect![[r#" expect![[r#"
foo Module FileId(1) 0..35 Other foo Module FileId(1) 0..35
FileId(0) 14..17 Other FileId(0) 14..17
"#]], "#]],
); );
} }
@@ -628,10 +641,10 @@ pub(super) struct Foo$0 {
} }
"#, "#,
expect![[r#" 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) 20..23
FileId(1) 47..50 StructLiteral FileId(1) 47..50
"#]], "#]],
); );
} }
@@ -656,10 +669,10 @@ pub(super) struct Foo$0 {
code, code,
None, None,
expect![[r#" 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(1) 16..20
FileId(2) 16..20 StructLiteral FileId(2) 16..20
"#]], "#]],
); );
@@ -667,9 +680,9 @@ pub(super) struct Foo$0 {
code, code,
Some(SearchScope::single_file(FileId(2))), Some(SearchScope::single_file(FileId(2))),
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -687,10 +700,10 @@ fn foo() {
} }
"#, "#,
expect![[r#" 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) 63..65
FileId(0) 73..75 StructLiteral FileId(0) 73..75
"#]], "#]],
); );
} }
@@ -705,10 +718,10 @@ fn foo() {
} }
"#, "#,
expect![[r#" 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) 34..35 Write
FileId(0) 38..39 Other Read FileId(0) 38..39 Read
"#]], "#]],
); );
} }
@@ -727,10 +740,10 @@ fn foo() {
} }
"#, "#,
expect![[r#" 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) 55..56 Read
FileId(0) 68..69 Other Write FileId(0) 68..69 Write
"#]], "#]],
); );
} }
@@ -745,9 +758,9 @@ fn foo() {
} }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -769,9 +782,9 @@ fn main() {
} }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -791,10 +804,10 @@ use crate::f;
fn g() { f(); } fn g() { f(); }
"#, "#,
expect![[r#" 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) 11..12
FileId(1) 24..25 StructLiteral FileId(1) 24..25
"#]], "#]],
); );
} }
@@ -814,9 +827,9 @@ fn f(s: S) {
} }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -838,9 +851,9 @@ fn f(e: En) {
} }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -862,9 +875,9 @@ fn f() -> m::En {
} }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -887,10 +900,10 @@ impl Foo {
} }
"#, "#,
expect![[r#" 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) 71..75 Read
FileId(0) 152..156 Other Read FileId(0) 152..156 Read
"#]], "#]],
); );
} }
@@ -908,9 +921,9 @@ impl Foo {
} }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -926,7 +939,7 @@ impl Foo {
let mut actual = String::new(); let mut actual = String::new();
{ {
let decl = refs.declaration; 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 { if let Some(access) = decl.access {
format_to!(actual, " {:?}", access) format_to!(actual, " {:?}", access)
} }
@@ -934,9 +947,9 @@ impl Foo {
} }
for (file_id, references) in refs.references { for (file_id, references) in refs.references {
for r in references { for (range, access) in references {
format_to!(actual, "{:?} {:?} {:?}", file_id, r.range, r.kind); format_to!(actual, "{:?} {:?}", file_id, range);
if let Some(access) = r.access { if let Some(access) = access {
format_to!(actual, " {:?}", access); format_to!(actual, " {:?}", access);
} }
actual += "\n"; actual += "\n";
@@ -957,13 +970,13 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> {
} }
"#, "#,
expect![[r#" 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) 63..65
FileId(0) 71..73 Lifetime FileId(0) 71..73
FileId(0) 82..84 Lifetime FileId(0) 82..84
FileId(0) 95..97 Lifetime FileId(0) 95..97
FileId(0) 106..108 Lifetime FileId(0) 106..108
"#]], "#]],
); );
} }
@@ -975,10 +988,10 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> {
type Foo<'a, T> where T: 'a$0 = &'a T; type Foo<'a, T> where T: 'a$0 = &'a T;
"#, "#,
expect![[r#" 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) 25..27
FileId(0) 31..33 Lifetime FileId(0) 31..33
"#]], "#]],
); );
} }
@@ -997,11 +1010,11 @@ impl<'a> Foo<'a> for &'a () {
} }
"#, "#,
expect![[r#" 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) 55..57
FileId(0) 64..66 Lifetime FileId(0) 64..66
FileId(0) 89..91 Lifetime FileId(0) 89..91
"#]], "#]],
); );
} }
@@ -1017,9 +1030,9 @@ fn main() {
} }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -1035,9 +1048,9 @@ fn main() {
} }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -1056,10 +1069,10 @@ fn foo<'a>() -> &'a () {
} }
"#, "#,
expect![[r#" 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) 80..82
FileId(0) 108..110 Lifetime FileId(0) 108..110
"#]], "#]],
); );
} }
@@ -1073,9 +1086,9 @@ fn foo<const FOO$0: usize>() -> usize {
} }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }
@@ -1089,9 +1102,9 @@ trait Foo {
} }
"#, "#,
expect![[r#" 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
"#]], "#]],
); );
} }

View File

@@ -4,9 +4,9 @@ use std::fmt::{self, Display};
use either::Either; use either::Either;
use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics}; use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics};
use ide_db::{ use ide_db::{
base_db::{AnchoredPathBuf, FileId, FileRange}, base_db::{AnchoredPathBuf, FileId},
defs::{Definition, NameClass, NameRefClass}, defs::{Definition, NameClass, NameRefClass},
search::FileReference, search::{FileReference, NameLike},
RootDatabase, RootDatabase,
}; };
use stdx::never; use stdx::never;
@@ -17,10 +17,7 @@ use syntax::{
use test_utils::mark; use test_utils::mark;
use text_edit::TextEdit; use text_edit::TextEdit;
use crate::{ use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange};
display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, ReferenceKind, SourceChange,
TextRange,
};
type RenameResult<T> = Result<T, RenameError>; type RenameResult<T> = Result<T, RenameError>;
#[derive(Debug)] #[derive(Debug)]
@@ -41,6 +38,8 @@ macro_rules! bail {
($($tokens:tt)*) => {return Err(format_err!($($tokens)*))} ($($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( pub(crate) fn prepare_rename(
db: &RootDatabase, db: &RootDatabase,
position: FilePosition, 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( fn find_name_like(
sema: &Semantics<RootDatabase>, sema: &Semantics<RootDatabase>,
syntax: &SyntaxNode, syntax: &SyntaxNode,
@@ -174,69 +167,96 @@ fn source_edit_from_references(
sema: &Semantics<RootDatabase>, sema: &Semantics<RootDatabase>,
file_id: FileId, file_id: FileId,
references: &[FileReference], references: &[FileReference],
def: Definition,
new_name: &str, new_name: &str,
) -> (FileId, TextEdit) { ) -> (FileId, TextEdit) {
let root = sema.parse(file_id);
let mut edit = TextEdit::builder(); let mut edit = TextEdit::builder();
for reference in references { for reference in references {
let mut replacement_text = String::new(); let (range, replacement) = match &reference.name_from_syntax(root.syntax()) {
let range = match reference.kind { Some(NameLike::Name(_)) => (None, format!("{}", new_name)),
ReferenceKind::FieldShorthandForField => { Some(NameLike::NameRef(name_ref)) => source_edit_from_name_ref(name_ref, new_name, def),
mark::hit!(test_rename_struct_field_for_shorthand); Some(NameLike::Lifetime(_)) => (None, format!("{}", new_name)),
replacement_text.push_str(new_name); None => (None, new_name.to_owned()),
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
}
}; };
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()) (file_id, edit.finish())
} }
fn edit_text_range_for_record_field_expr_or_pat( fn source_edit_from_name_ref(
sema: &Semantics<RootDatabase>, name_ref: &ast::NameRef,
file_range: FileRange,
new_name: &str, new_name: &str,
) -> TextRange { def: Definition,
let source_file = sema.parse(file_range.file_id); ) -> (Option<TextRange>, String) {
let file_syntax = source_file.syntax(); if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) {
let original_range = file_range.range; let rcf_name_ref = record_field.name_ref();
let rcf_expr = record_field.expr();
syntax::algo::find_node_at_range::<ast::RecordExprField>(file_syntax, original_range) match (rcf_name_ref, rcf_expr.and_then(|it| it.name_ref())) {
.and_then(|field_expr| match field_expr.expr().and_then(|e| e.name_ref()) { // field: init-expr, check if we can use a field init shorthand
Some(name) if &name.to_string() == new_name => Some(field_expr.syntax().text_range()), (Some(field_name), Some(init)) => {
_ => None, if field_name == *name_ref {
}) if init.text() == new_name {
.or_else(|| { mark::hit!(test_rename_field_put_init_shorthand);
syntax::algo::find_node_at_range::<ast::RecordPatField>(file_syntax, original_range) // same names, we can use a shorthand here instead
.and_then(|field_pat| match field_pat.pat() { // we do not want to erase attributes hence this range start
Some(ast::Pat::IdentPat(pat)) let s = field_name.syntax().text_range().start();
if pat.name().map(|n| n.to_string()).as_deref() == Some(new_name) => let e = record_field.syntax().text_range().end();
{ return (Some(TextRange::new(s, e)), format!("{}", new_name));
Some(field_pat.syntax().text_range())
} }
_ => None, } else if init == *name_ref {
}) if field_name.text() == new_name {
}) mark::hit!(test_rename_local_put_init_shorthand);
.unwrap_or(original_range) // 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( fn rename_mod(
@@ -277,7 +297,7 @@ fn rename_mod(
let def = Definition::ModuleDef(ModuleDef::Module(module)); let def = Definition::ModuleDef(ModuleDef::Module(module));
let usages = def.usages(sema).all(); let usages = def.usages(sema).all();
let ref_edits = usages.iter().map(|(&file_id, references)| { 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); source_change.extend(ref_edits);
@@ -346,7 +366,7 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe
let usages = def.usages(sema).all(); let usages = def.usages(sema).all();
let mut source_change = SourceChange::default(); let mut source_change = SourceChange::default();
source_change.extend(usages.iter().map(|(&file_id, references)| { 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( source_change.insert_source_edit(
file_id.original_file(sema.db), file_id.original_file(sema.db),
@@ -403,7 +423,7 @@ fn rename_self_to_param(
let mut source_change = SourceChange::default(); let mut source_change = SourceChange::default();
source_change.insert_source_edit(file_id.original_file(sema.db), edit); source_change.insert_source_edit(file_id.original_file(sema.db), edit);
source_change.extend(usages.iter().map(|(&file_id, references)| { 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) Ok(source_change)
} }
@@ -457,7 +477,7 @@ fn rename_reference(
} }
let mut source_change = SourceChange::default(); let mut source_change = SourceChange::default();
source_change.extend(usages.iter().map(|(&file_id, references)| { 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)?; let (file_id, edit) = source_edit_from_def(sema, def, new_name)?;
@@ -545,10 +565,8 @@ mod tests {
fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) { fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) {
let (analysis, position) = fixture::position(ra_fixture); let (analysis, position) = fixture::position(ra_fixture);
let source_change = analysis let source_change =
.rename(position, new_name) analysis.rename(position, new_name).unwrap().expect("Expect returned a RenameError");
.unwrap()
.expect("Expect returned RangeInfo to be Some, but was None");
expect.assert_debug_eq(&source_change) expect.assert_debug_eq(&source_change)
} }
@@ -792,8 +810,8 @@ impl Foo {
} }
#[test] #[test]
fn test_rename_struct_field_for_shorthand() { fn test_rename_field_in_field_shorthand() {
mark::check!(test_rename_struct_field_for_shorthand); mark::check!(test_rename_field_in_field_shorthand);
check( check(
"j", "j",
r#" r#"
@@ -818,8 +836,8 @@ impl Foo {
} }
#[test] #[test]
fn test_rename_local_for_field_shorthand() { fn test_rename_local_in_field_shorthand() {
mark::check!(test_rename_local_for_field_shorthand); mark::check!(test_rename_local_in_field_shorthand);
check( check(
"j", "j",
r#" r#"
@@ -1417,8 +1435,8 @@ impl Foo {
} }
#[test] #[test]
fn test_initializer_use_field_init_shorthand() { fn test_rename_field_put_init_shorthand() {
mark::check!(test_rename_field_expr_pat); mark::check!(test_rename_field_put_init_shorthand);
check( check(
"bar", "bar",
r#" r#"
@@ -1438,8 +1456,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] #[test]
fn test_struct_field_destructure_into_shorthand() { fn test_struct_field_destructure_into_shorthand() {
mark::check!(test_rename_field_put_init_shorthand_pat);
check( check(
"baz", "baz",
r#" r#"

View File

@@ -10,7 +10,9 @@ use base_db::{FileId, FileRange, SourceDatabaseExt};
use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility}; use hir::{DefWithBody, HasSource, Module, ModuleSource, Semantics, Visibility};
use once_cell::unsync::Lazy; use once_cell::unsync::Lazy;
use rustc_hash::FxHashMap; 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::defs::NameClass;
use crate::{ use crate::{
@@ -18,6 +20,13 @@ use crate::{
RootDatabase, RootDatabase,
}; };
#[derive(Debug, Clone)]
pub enum NameKind {
Name,
NameRef,
Lifetime,
}
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub struct UsageSearchResult { pub struct UsageSearchResult {
pub references: FxHashMap<FileId, Vec<FileReference>>, 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)] #[derive(Debug, Clone)]
pub struct FileReference { pub struct FileReference {
pub range: TextRange, pub range: TextRange,
pub kind: ReferenceKind, pub name: NameKind,
pub access: Option<ReferenceAccess>, pub access: Option<ReferenceAccess>,
} }
#[derive(Debug, Clone, PartialEq)] impl FileReference {
pub enum ReferenceKind { pub fn name_from_syntax(&self, root: &SyntaxNode) -> Option<NameLike> {
FieldShorthandForField, let node = node_or_parent(root.covering_element(self.range));
FieldShorthandForLocal, match self.name {
StructLiteral, NameKind::Name => ast::Name::cast(node).map(Into::into),
RecordFieldExprOrPat, NameKind::NameRef => ast::NameRef::cast(node).map(Into::into),
SelfParam, NameKind::Lifetime => ast::Lifetime::cast(node).map(Into::into),
EnumLiteral, }
Lifetime, }
Other,
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)] #[derive(Debug, Copy, Clone, PartialEq)]
@@ -369,8 +408,7 @@ impl<'a> FindUsages<'a> {
match NameRefClass::classify_lifetime(self.sema, lifetime) { match NameRefClass::classify_lifetime(self.sema, lifetime) {
Some(NameRefClass::Definition(def)) if &def == self.def => { Some(NameRefClass::Definition(def)) if &def == self.def => {
let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax()); let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax());
let reference = let reference = FileReference { range, name: NameKind::Lifetime, access: None };
FileReference { range, kind: ReferenceKind::Lifetime, access: None };
sink(file_id, reference) sink(file_id, reference)
} }
_ => false, // not a usage _ => false, // not a usage
@@ -384,19 +422,12 @@ impl<'a> FindUsages<'a> {
) -> bool { ) -> bool {
match NameRefClass::classify(self.sema, &name_ref) { match NameRefClass::classify(self.sema, &name_ref) {
Some(NameRefClass::Definition(def)) if &def == self.def => { 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 FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
let reference = let reference = FileReference {
FileReference { range, kind, access: reference_access(&def, &name_ref) }; range,
name: NameKind::NameRef,
access: reference_access(&def, &name_ref),
};
sink(file_id, reference) sink(file_id, reference)
} }
Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => { Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
@@ -404,12 +435,12 @@ impl<'a> FindUsages<'a> {
let reference = match self.def { let reference = match self.def {
Definition::Field(_) if &field == self.def => FileReference { Definition::Field(_) if &field == self.def => FileReference {
range, range,
kind: ReferenceKind::FieldShorthandForField, name: NameKind::NameRef,
access: reference_access(&field, &name_ref), access: reference_access(&field, &name_ref),
}, },
Definition::Local(l) if &local == l => FileReference { Definition::Local(l) if &local == l => FileReference {
range, range,
kind: ReferenceKind::FieldShorthandForLocal, name: NameKind::NameRef,
access: reference_access(&Definition::Local(local), &name_ref), access: reference_access(&Definition::Local(local), &name_ref),
}, },
_ => return false, // not a usage _ => 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 FileRange { file_id, range } = self.sema.original_range(name.syntax());
let reference = FileReference { let reference = FileReference {
range, range,
kind: ReferenceKind::FieldShorthandForField, name: NameKind::Name,
// FIXME: mutable patterns should have `Write` access // FIXME: mutable patterns should have `Write` access
access: Some(ReferenceAccess::Read), 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 // Default Locals and Fields to read
mode.or(Some(ReferenceAccess::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)
}

View File

@@ -827,18 +827,23 @@ pub(crate) fn handle_references(
Some(refs) => refs, Some(refs) => refs,
}; };
let locations = if params.context.include_declaration { let decl = if params.context.include_declaration {
refs.references_with_declaration() Some(FileRange {
.file_ranges() file_id: refs.declaration.nav.file_id,
.filter_map(|frange| to_proto::location(&snap, frange).ok()) range: refs.declaration.nav.focus_or_full_range(),
.collect() })
} else { } else {
// Only iterate over the references if include_declaration was false None
refs.references()
.file_ranges()
.filter_map(|frange| to_proto::location(&snap, frange).ok())
.collect()
}; };
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)) Ok(Some(locations))
} }
@@ -1214,8 +1219,11 @@ pub(crate) fn handle_code_lens_resolve(
.find_all_refs(position, None) .find_all_refs(position, None)
.unwrap_or(None) .unwrap_or(None)
.map(|r| { .map(|r| {
r.references() r.references
.file_ranges() .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()) .filter_map(|frange| to_proto::location(&snap, frange).ok())
.collect_vec() .collect_vec()
}) })
@@ -1259,17 +1267,26 @@ pub(crate) fn handle_document_highlight(
Some(refs) => refs, 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 let res = refs
.references_with_declaration()
.references .references
.get(&position.file_id) .get(&position.file_id)
.map(|file_refs| { .map(|file_refs| {
file_refs file_refs
.into_iter() .into_iter()
.map(|r| DocumentHighlight { .map(|&(range, access)| DocumentHighlight {
range: to_proto::range(&line_index, r.range), range: to_proto::range(&line_index, range),
kind: r.access.map(to_proto::document_highlight_kind), kind: access.map(to_proto::document_highlight_kind),
}) })
.chain(decl)
.collect() .collect()
}) })
.unwrap_or_default(); .unwrap_or_default();

View File

@@ -274,10 +274,7 @@ impl ast::Struct {
impl ast::RecordExprField { impl ast::RecordExprField {
pub fn for_field_name(field_name: &ast::NameRef) -> Option<ast::RecordExprField> { pub fn for_field_name(field_name: &ast::NameRef) -> Option<ast::RecordExprField> {
let candidate = let candidate = Self::for_name_ref(field_name)?;
field_name.syntax().parent().and_then(ast::RecordExprField::cast).or_else(|| {
field_name.syntax().ancestors().nth(4).and_then(ast::RecordExprField::cast)
})?;
if candidate.field_name().as_ref() == Some(field_name) { if candidate.field_name().as_ref() == Some(field_name) {
Some(candidate) Some(candidate)
} else { } 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 /// Deals with field init shorthand
pub fn field_name(&self) -> Option<ast::NameRef> { pub fn field_name(&self) -> Option<ast::NameRef> {
if let Some(name_ref) = self.name_ref() { if let Some(name_ref) = self.name_ref() {
@@ -294,6 +298,7 @@ impl ast::RecordExprField {
} }
} }
#[derive(Debug, Clone, PartialEq)]
pub enum NameOrNameRef { pub enum NameOrNameRef {
Name(ast::Name), Name(ast::Name),
NameRef(ast::NameRef), NameRef(ast::NameRef),
@@ -309,6 +314,23 @@ impl fmt::Display for NameOrNameRef {
} }
impl ast::RecordPatField { 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 /// Deals with field init shorthand
pub fn field_name(&self) -> Option<NameOrNameRef> { pub fn field_name(&self) -> Option<NameOrNameRef> {
if let Some(name_ref) = self.name_ref() { if let Some(name_ref) = self.name_ref() {