Lifetime reference search
This commit is contained in:
parent
067067a6c1
commit
55faa2daa3
crates
ide/src
ide_db/src
rust-analyzer/src
@ -9,7 +9,7 @@ use ide_db::{defs::Definition, RootDatabase};
|
||||
use syntax::{
|
||||
ast::{self, NameOwner},
|
||||
match_ast, AstNode, SmolStr,
|
||||
SyntaxKind::{self, IDENT_PAT, TYPE_PARAM},
|
||||
SyntaxKind::{self, IDENT_PAT, LIFETIME_PARAM, TYPE_PARAM},
|
||||
TextRange,
|
||||
};
|
||||
|
||||
@ -182,6 +182,7 @@ impl TryToNav for Definition {
|
||||
Definition::SelfType(it) => Some(it.to_nav(db)),
|
||||
Definition::Local(it) => Some(it.to_nav(db)),
|
||||
Definition::TypeParam(it) => Some(it.to_nav(db)),
|
||||
Definition::LifetimeParam(it) => Some(it.to_nav(db)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -376,6 +377,23 @@ impl ToNav for hir::TypeParam {
|
||||
}
|
||||
}
|
||||
|
||||
impl ToNav for hir::LifetimeParam {
|
||||
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
|
||||
let src = self.source(db);
|
||||
let full_range = src.value.syntax().text_range();
|
||||
NavigationTarget {
|
||||
file_id: src.file_id.original_file(db),
|
||||
name: self.name(db).to_string().into(),
|
||||
kind: LIFETIME_PARAM,
|
||||
full_range,
|
||||
focus_range: Some(full_range),
|
||||
container_name: None,
|
||||
description: None,
|
||||
docs: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<Documentation> {
|
||||
let parse = db.parse(symbol.file_id);
|
||||
let node = symbol.ptr.to_node(parse.tree().syntax());
|
||||
|
@ -190,7 +190,10 @@ fn rewrite_intra_doc_link(
|
||||
},
|
||||
Definition::Macro(it) => it.resolve_doc_path(db, link, ns),
|
||||
Definition::Field(it) => it.resolve_doc_path(db, link, ns),
|
||||
Definition::SelfType(_) | Definition::Local(_) | Definition::TypeParam(_) => return None,
|
||||
Definition::SelfType(_)
|
||||
| Definition::Local(_)
|
||||
| Definition::TypeParam(_)
|
||||
| Definition::LifetimeParam(_) => return None,
|
||||
}?;
|
||||
let krate = resolved.module(db)?.krate();
|
||||
let canonical_path = resolved.canonical_path(db)?;
|
||||
|
@ -1,3 +1,4 @@
|
||||
use either::Either;
|
||||
use hir::Semantics;
|
||||
use ide_db::{
|
||||
base_db::FileId,
|
||||
@ -33,7 +34,7 @@ pub(crate) fn goto_definition(
|
||||
let nav_targets = match_ast! {
|
||||
match parent {
|
||||
ast::NameRef(name_ref) => {
|
||||
reference_definition(&sema, &name_ref).to_vec()
|
||||
reference_definition(&sema, Either::Right(&name_ref)).to_vec()
|
||||
},
|
||||
ast::Name(name) => {
|
||||
let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db);
|
||||
@ -53,6 +54,13 @@ pub(crate) fn goto_definition(
|
||||
let self_param = func.param_list()?.self_param()?;
|
||||
vec![self_to_nav_target(self_param, position.file_id)?]
|
||||
},
|
||||
ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) {
|
||||
let def = name_class.referenced_or_defined(sema.db);
|
||||
let nav = def.try_to_nav(sema.db)?;
|
||||
vec![nav]
|
||||
} else {
|
||||
reference_definition(&sema, Either::Left(<)).to_vec()
|
||||
},
|
||||
_ => return None,
|
||||
}
|
||||
};
|
||||
@ -64,7 +72,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
|
||||
return tokens.max_by_key(priority);
|
||||
fn priority(n: &SyntaxToken) -> usize {
|
||||
match n.kind() {
|
||||
IDENT | INT_NUMBER | T![self] => 2,
|
||||
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 2,
|
||||
kind if kind.is_trivia() => 0,
|
||||
_ => 1,
|
||||
}
|
||||
@ -102,9 +110,12 @@ impl ReferenceResult {
|
||||
|
||||
pub(crate) fn reference_definition(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
name_ref: &ast::NameRef,
|
||||
name_ref: Either<&ast::Lifetime, &ast::NameRef>,
|
||||
) -> ReferenceResult {
|
||||
let name_kind = NameRefClass::classify(sema, name_ref);
|
||||
let name_kind = name_ref.either(
|
||||
|lifetime| NameRefClass::classify_lifetime(sema, lifetime),
|
||||
|name_ref| NameRefClass::classify(sema, name_ref),
|
||||
);
|
||||
if let Some(def) = name_kind {
|
||||
let def = def.referenced(sema.db);
|
||||
return match def.try_to_nav(sema.db) {
|
||||
@ -114,10 +125,9 @@ pub(crate) fn reference_definition(
|
||||
}
|
||||
|
||||
// Fallback index based approach:
|
||||
let navs = symbol_index::index_resolve(sema.db, name_ref)
|
||||
.into_iter()
|
||||
.map(|s| s.to_nav(sema.db))
|
||||
.collect();
|
||||
let name = name_ref.either(ast::Lifetime::text, ast::NameRef::text);
|
||||
let navs =
|
||||
symbol_index::index_resolve(sema.db, name).into_iter().map(|s| s.to_nav(sema.db)).collect();
|
||||
ReferenceResult::Approximate(navs)
|
||||
}
|
||||
|
||||
@ -1033,6 +1043,37 @@ impl Foo {
|
||||
fn bar(&self<|>) {
|
||||
//^^^^
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_lifetime_param_on_decl() {
|
||||
check(
|
||||
r#"
|
||||
fn foo<'foobar<|>>(_: &'foobar ()) {
|
||||
//^^^^^^^
|
||||
}"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_lifetime_param_decl() {
|
||||
check(
|
||||
r#"
|
||||
fn foo<'foobar>(_: &'foobar<|> ()) {
|
||||
//^^^^^^^
|
||||
}"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn goto_lifetime_param_decl_nested() {
|
||||
check(
|
||||
r#"
|
||||
fn foo<'foobar>(_: &'foobar ()) {
|
||||
fn foo<'foobar>(_: &'foobar<|> ()) {}
|
||||
//^^^^^^^
|
||||
}"#,
|
||||
)
|
||||
}
|
||||
|
@ -364,7 +364,7 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
|
||||
Adt::Enum(it) => from_def_source(db, it, mod_path),
|
||||
})
|
||||
}
|
||||
Definition::TypeParam(_) => {
|
||||
Definition::TypeParam(_) | Definition::LifetimeParam(_) => {
|
||||
// FIXME: Hover for generic param
|
||||
None
|
||||
}
|
||||
|
@ -528,6 +528,13 @@ impl Analysis {
|
||||
self.with_db(|db| references::rename::rename(db, position, new_name))
|
||||
}
|
||||
|
||||
pub fn prepare_rename(
|
||||
&self,
|
||||
position: FilePosition,
|
||||
) -> Cancelable<Result<RangeInfo<()>, RenameError>> {
|
||||
self.with_db(|db| references::rename::prepare_rename(db, position))
|
||||
}
|
||||
|
||||
pub fn structural_search_replace(
|
||||
&self,
|
||||
query: &str,
|
||||
|
@ -130,6 +130,8 @@ pub(crate) fn find_all_refs(
|
||||
kind = ReferenceKind::FieldShorthandForLocal;
|
||||
}
|
||||
}
|
||||
} else if let Definition::LifetimeParam(_) = def {
|
||||
kind = ReferenceKind::Lifetime;
|
||||
};
|
||||
|
||||
let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) };
|
||||
@ -148,11 +150,29 @@ fn find_name(
|
||||
let range = name.syntax().text_range();
|
||||
return Some(RangeInfo::new(range, def));
|
||||
}
|
||||
let name_ref =
|
||||
sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?;
|
||||
let def = NameRefClass::classify(sema, &name_ref)?.referenced(sema.db);
|
||||
let range = name_ref.syntax().text_range();
|
||||
Some(RangeInfo::new(range, def))
|
||||
|
||||
let (text_range, def) = if let Some(lifetime) =
|
||||
sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset)
|
||||
{
|
||||
if let Some(def) = NameRefClass::classify_lifetime(sema, &lifetime)
|
||||
.map(|class| NameRefClass::referenced(class, sema.db))
|
||||
{
|
||||
(lifetime.syntax().text_range(), def)
|
||||
} else {
|
||||
(
|
||||
lifetime.syntax().text_range(),
|
||||
NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
let name_ref =
|
||||
sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?;
|
||||
(
|
||||
name_ref.syntax().text_range(),
|
||||
NameRefClass::classify(sema, &name_ref)?.referenced(sema.db),
|
||||
)
|
||||
};
|
||||
Some(RangeInfo::new(text_range, def))
|
||||
}
|
||||
|
||||
fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> {
|
||||
@ -1005,4 +1025,65 @@ impl Foo {
|
||||
}
|
||||
expect.assert_eq(&actual)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_lifetimes_function() {
|
||||
check(
|
||||
r#"
|
||||
trait Foo<'a> {}
|
||||
impl<'a> Foo<'a> for &'a () {}
|
||||
fn foo<'a, 'b: 'a>(x: &'a<|> ()) -> &'a () where &'a (): Foo<'a> {
|
||||
fn bar<'a>(_: &'a ()) {}
|
||||
x
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
'a LIFETIME_PARAM FileId(0) 55..57 55..57 Lifetime
|
||||
|
||||
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
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_lifetimes_type_alias() {
|
||||
check(
|
||||
r#"
|
||||
type Foo<'a, T> where T: 'a<|> = &'a T;
|
||||
"#,
|
||||
expect![[r#"
|
||||
'a LIFETIME_PARAM FileId(0) 9..11 9..11 Lifetime
|
||||
|
||||
FileId(0) 25..27 Lifetime
|
||||
FileId(0) 31..33 Lifetime
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_lifetimes_trait_impl() {
|
||||
check(
|
||||
r#"
|
||||
trait Foo<'a> {
|
||||
fn foo() -> &'a ();
|
||||
}
|
||||
impl<'a> Foo<'a> for &'a () {
|
||||
fn foo() -> &'a<|> () {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
'a LIFETIME_PARAM FileId(0) 47..49 47..49 Lifetime
|
||||
|
||||
FileId(0) 55..57 Lifetime
|
||||
FileId(0) 64..66 Lifetime
|
||||
FileId(0) 89..91 Lifetime
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,29 @@ impl fmt::Display for RenameError {
|
||||
|
||||
impl Error for RenameError {}
|
||||
|
||||
pub(crate) fn prepare_rename(
|
||||
db: &RootDatabase,
|
||||
position: FilePosition,
|
||||
) -> Result<RangeInfo<()>, RenameError> {
|
||||
let sema = Semantics::new(db);
|
||||
let source_file = sema.parse(position.file_id);
|
||||
let syntax = source_file.syntax();
|
||||
if let Some(module) = find_module_at_offset(&sema, position, syntax) {
|
||||
rename_mod(&sema, position, module, "dummy")
|
||||
} else if let Some(self_token) =
|
||||
syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
|
||||
{
|
||||
rename_self_to_param(&sema, position, self_token, "dummy")
|
||||
} else {
|
||||
let range = match find_all_refs(&sema, position, None) {
|
||||
Some(RangeInfo { range, .. }) => range,
|
||||
None => return Err(RenameError("No references found at position".to_string())),
|
||||
};
|
||||
Ok(RangeInfo::new(range, SourceChange::from(vec![])))
|
||||
}
|
||||
.map(|info| RangeInfo::new(info.range, ()))
|
||||
}
|
||||
|
||||
pub(crate) fn rename(
|
||||
db: &RootDatabase,
|
||||
position: FilePosition,
|
||||
@ -49,11 +72,18 @@ pub(crate) fn rename_with_semantics(
|
||||
position: FilePosition,
|
||||
new_name: &str,
|
||||
) -> Result<RangeInfo<SourceChange>, RenameError> {
|
||||
match lex_single_syntax_kind(new_name) {
|
||||
let is_lifetime_name = match lex_single_syntax_kind(new_name) {
|
||||
Some(res) => match res {
|
||||
(SyntaxKind::IDENT, _) => (),
|
||||
(SyntaxKind::UNDERSCORE, _) => (),
|
||||
(SyntaxKind::IDENT, _) => false,
|
||||
(SyntaxKind::UNDERSCORE, _) => false,
|
||||
(SyntaxKind::SELF_KW, _) => return rename_to_self(&sema, position),
|
||||
(SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => true,
|
||||
(SyntaxKind::LIFETIME_IDENT, _) => {
|
||||
return Err(RenameError(format!(
|
||||
"Invalid name `{0}`: Cannot rename lifetime to {0}",
|
||||
new_name
|
||||
)))
|
||||
}
|
||||
(_, Some(syntax_error)) => {
|
||||
return Err(RenameError(format!("Invalid name `{}`: {}", new_name, syntax_error)))
|
||||
}
|
||||
@ -62,18 +92,21 @@ pub(crate) fn rename_with_semantics(
|
||||
}
|
||||
},
|
||||
None => return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))),
|
||||
}
|
||||
};
|
||||
|
||||
let source_file = sema.parse(position.file_id);
|
||||
let syntax = source_file.syntax();
|
||||
if let Some(module) = find_module_at_offset(&sema, position, syntax) {
|
||||
// this is here to prevent lifetime renames from happening on modules and self
|
||||
if is_lifetime_name {
|
||||
rename_reference(&sema, position, new_name, is_lifetime_name)
|
||||
} else if let Some(module) = find_module_at_offset(&sema, position, syntax) {
|
||||
rename_mod(&sema, position, module, new_name)
|
||||
} else if let Some(self_token) =
|
||||
syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
|
||||
{
|
||||
rename_self_to_param(&sema, position, self_token, new_name)
|
||||
} else {
|
||||
rename_reference(&sema, position, new_name)
|
||||
rename_reference(&sema, position, new_name, is_lifetime_name)
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,12 +388,26 @@ fn rename_reference(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
position: FilePosition,
|
||||
new_name: &str,
|
||||
is_lifetime_name: bool,
|
||||
) -> Result<RangeInfo<SourceChange>, RenameError> {
|
||||
let RangeInfo { range, info: refs } = match find_all_refs(sema, position, None) {
|
||||
Some(range_info) => range_info,
|
||||
None => return Err(RenameError("No references found at position".to_string())),
|
||||
};
|
||||
|
||||
match (refs.declaration.kind == ReferenceKind::Lifetime, is_lifetime_name) {
|
||||
(true, false) => {
|
||||
return Err(RenameError(format!(
|
||||
"Invalid name `{}`: not a lifetime identifier",
|
||||
new_name
|
||||
)))
|
||||
}
|
||||
(false, true) => {
|
||||
return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name)))
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let edit = refs
|
||||
.into_iter()
|
||||
.map(|reference| source_edit_from_reference(sema, reference, new_name))
|
||||
@ -464,6 +511,24 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_to_invalid_identifier_lifetime() {
|
||||
check(
|
||||
"'foo",
|
||||
r#"fn main() { let i<|> = 1; }"#,
|
||||
"error: Invalid name `'foo`: not an identifier",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_to_invalid_identifier_lifetime2() {
|
||||
check(
|
||||
"foo",
|
||||
r#"fn main<'a>(_: &'a<|> ()) {}"#,
|
||||
"error: Invalid name `foo`: not a lifetime identifier",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_for_local() {
|
||||
check(
|
||||
@ -1393,6 +1458,33 @@ struct Foo {
|
||||
fn foo(Foo { i: bar }: foo) -> i32 {
|
||||
bar
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rename_lifetimes() {
|
||||
check(
|
||||
"'yeeee",
|
||||
r#"
|
||||
trait Foo<'a> {
|
||||
fn foo() -> &'a ();
|
||||
}
|
||||
impl<'a> Foo<'a> for &'a () {
|
||||
fn foo() -> &'a<|> () {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
trait Foo<'a> {
|
||||
fn foo() -> &'a ();
|
||||
}
|
||||
impl<'yeeee> Foo<'yeeee> for &'yeeee () {
|
||||
fn foo() -> &'yeeee () {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
"#,
|
||||
)
|
||||
}
|
||||
|
@ -806,6 +806,7 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
|
||||
}
|
||||
return h;
|
||||
}
|
||||
Definition::LifetimeParam(_) => HighlightTag::Lifetime,
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
@ -6,12 +6,12 @@
|
||||
// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
|
||||
|
||||
use hir::{
|
||||
db::HirDatabase, Crate, Field, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef,
|
||||
Name, PathResolution, Semantics, TypeParam, Visibility,
|
||||
db::HirDatabase, Crate, Field, HasVisibility, ImplDef, LifetimeParam, Local, MacroDef, Module,
|
||||
ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility,
|
||||
};
|
||||
use syntax::{
|
||||
ast::{self, AstNode},
|
||||
match_ast, SyntaxNode,
|
||||
match_ast, SyntaxKind, SyntaxNode,
|
||||
};
|
||||
|
||||
use crate::RootDatabase;
|
||||
@ -25,6 +25,8 @@ pub enum Definition {
|
||||
SelfType(ImplDef),
|
||||
Local(Local),
|
||||
TypeParam(TypeParam),
|
||||
LifetimeParam(LifetimeParam),
|
||||
// FIXME: Label
|
||||
}
|
||||
|
||||
impl Definition {
|
||||
@ -36,6 +38,7 @@ impl Definition {
|
||||
Definition::SelfType(it) => Some(it.module(db)),
|
||||
Definition::Local(it) => Some(it.module(db)),
|
||||
Definition::TypeParam(it) => Some(it.module(db)),
|
||||
Definition::LifetimeParam(it) => Some(it.module(db)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,6 +50,7 @@ impl Definition {
|
||||
Definition::SelfType(_) => None,
|
||||
Definition::Local(_) => None,
|
||||
Definition::TypeParam(_) => None,
|
||||
Definition::LifetimeParam(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,6 +76,7 @@ impl Definition {
|
||||
Definition::SelfType(_) => return None,
|
||||
Definition::Local(it) => it.name(db)?,
|
||||
Definition::TypeParam(it) => it.name(db),
|
||||
Definition::LifetimeParam(it) => it.name(db),
|
||||
};
|
||||
Some(name)
|
||||
}
|
||||
@ -229,6 +234,25 @@ impl NameClass {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn classify_lifetime(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
lifetime: &ast::Lifetime,
|
||||
) -> Option<NameClass> {
|
||||
let _p = profile::span("classify_lifetime").detail(|| lifetime.to_string());
|
||||
let parent = lifetime.syntax().parent()?;
|
||||
|
||||
match_ast! {
|
||||
match parent {
|
||||
ast::LifetimeParam(it) => {
|
||||
let def = sema.to_def(&it)?;
|
||||
Some(NameClass::Definition(Definition::LifetimeParam(def)))
|
||||
},
|
||||
ast::Label(_it) => None,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -338,6 +362,35 @@ impl NameRefClass {
|
||||
let resolved = sema.resolve_extern_crate(&extern_crate)?;
|
||||
Some(NameRefClass::ExternCrate(resolved))
|
||||
}
|
||||
|
||||
pub fn classify_lifetime(
|
||||
sema: &Semantics<RootDatabase>,
|
||||
lifetime: &ast::Lifetime,
|
||||
) -> Option<NameRefClass> {
|
||||
let _p = profile::span("classify_lifetime_ref").detail(|| lifetime.to_string());
|
||||
let parent = lifetime.syntax().parent()?;
|
||||
match parent.kind() {
|
||||
SyntaxKind::LIFETIME_ARG
|
||||
| SyntaxKind::SELF_PARAM
|
||||
| SyntaxKind::TYPE_BOUND
|
||||
| SyntaxKind::WHERE_PRED
|
||||
| SyntaxKind::REF_TYPE => sema
|
||||
.resolve_lifetime_param(lifetime)
|
||||
.map(Definition::LifetimeParam)
|
||||
.map(NameRefClass::Definition),
|
||||
// lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
|
||||
// if our lifetime is in a LifetimeParam without being the constrained lifetime
|
||||
_ if ast::LifetimeParam::cast(parent).and_then(|param| param.lifetime()).as_ref()
|
||||
!= Some(lifetime) =>
|
||||
{
|
||||
sema.resolve_lifetime_param(lifetime)
|
||||
.map(Definition::LifetimeParam)
|
||||
.map(NameRefClass::Definition)
|
||||
}
|
||||
SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => None,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PathResolution> for Definition {
|
||||
|
@ -33,6 +33,7 @@ pub enum ReferenceKind {
|
||||
RecordFieldExprOrPat,
|
||||
SelfKw,
|
||||
EnumLiteral,
|
||||
Lifetime,
|
||||
Other,
|
||||
}
|
||||
|
||||
@ -129,6 +130,25 @@ impl Definition {
|
||||
return SearchScope::new(res);
|
||||
}
|
||||
|
||||
if let Definition::LifetimeParam(param) = self {
|
||||
let range = match param.parent(db) {
|
||||
hir::GenericDef::Function(it) => it.source(db).value.syntax().text_range(),
|
||||
hir::GenericDef::Adt(it) => match it {
|
||||
hir::Adt::Struct(it) => it.source(db).value.syntax().text_range(),
|
||||
hir::Adt::Union(it) => it.source(db).value.syntax().text_range(),
|
||||
hir::Adt::Enum(it) => it.source(db).value.syntax().text_range(),
|
||||
},
|
||||
hir::GenericDef::Trait(it) => it.source(db).value.syntax().text_range(),
|
||||
hir::GenericDef::TypeAlias(it) => it.source(db).value.syntax().text_range(),
|
||||
hir::GenericDef::ImplDef(it) => it.source(db).value.syntax().text_range(),
|
||||
hir::GenericDef::EnumVariant(it) => it.source(db).value.syntax().text_range(),
|
||||
hir::GenericDef::Const(it) => it.source(db).value.syntax().text_range(),
|
||||
};
|
||||
let mut res = FxHashMap::default();
|
||||
res.insert(file_id, Some(range));
|
||||
return SearchScope::new(res);
|
||||
}
|
||||
|
||||
let vis = self.visibility(db);
|
||||
|
||||
if let Some(Visibility::Module(module)) = vis.and_then(|it| it.into()) {
|
||||
@ -255,25 +275,42 @@ impl<'a> FindUsages<'a> {
|
||||
continue;
|
||||
}
|
||||
|
||||
match sema.find_node_at_offset_with_descend(&tree, offset) {
|
||||
Some(name_ref) => {
|
||||
if self.found_name_ref(&name_ref, sink) {
|
||||
return;
|
||||
}
|
||||
if let Some(name_ref) = sema.find_node_at_offset_with_descend(&tree, offset) {
|
||||
if self.found_name_ref(&name_ref, sink) {
|
||||
return;
|
||||
}
|
||||
} else if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) {
|
||||
if self.found_name(&name, sink) {
|
||||
return;
|
||||
}
|
||||
} else if let Some(lifetime) = sema.find_node_at_offset_with_descend(&tree, offset)
|
||||
{
|
||||
if self.found_lifetime(&lifetime, sink) {
|
||||
return;
|
||||
}
|
||||
None => match sema.find_node_at_offset_with_descend(&tree, offset) {
|
||||
Some(name) => {
|
||||
if self.found_name(&name, sink) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn found_lifetime(
|
||||
&self,
|
||||
lifetime: &ast::Lifetime,
|
||||
sink: &mut dyn FnMut(Reference) -> bool,
|
||||
) -> bool {
|
||||
match NameRefClass::classify_lifetime(self.sema, lifetime) {
|
||||
Some(NameRefClass::Definition(def)) if &def == self.def => {
|
||||
let reference = Reference {
|
||||
file_range: self.sema.original_range(lifetime.syntax()),
|
||||
kind: ReferenceKind::Lifetime,
|
||||
access: None,
|
||||
};
|
||||
sink(reference)
|
||||
}
|
||||
_ => false, // not a usage
|
||||
}
|
||||
}
|
||||
|
||||
fn found_name_ref(
|
||||
&self,
|
||||
name_ref: &ast::NameRef,
|
||||
|
@ -209,8 +209,7 @@ pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<Fil
|
||||
query.search(&buf)
|
||||
}
|
||||
|
||||
pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymbol> {
|
||||
let name = name_ref.text();
|
||||
pub fn index_resolve(db: &RootDatabase, name: &SmolStr) -> Vec<FileSymbol> {
|
||||
let mut query = Query::new(name.to_string());
|
||||
query.exact();
|
||||
query.limit(4);
|
||||
|
@ -733,7 +733,7 @@ pub(crate) fn handle_prepare_rename(
|
||||
let _p = profile::span("handle_prepare_rename");
|
||||
let position = from_proto::file_position(&snap, params)?;
|
||||
|
||||
let change = snap.analysis.rename(position, "dummy")??;
|
||||
let change = snap.analysis.prepare_rename(position)??;
|
||||
let line_index = snap.analysis.file_line_index(position.file_id)?;
|
||||
let range = to_proto::range(&line_index, change.range);
|
||||
Ok(Some(PrepareRenameResponse::Range(range)))
|
||||
|
Loading…
x
Reference in New Issue
Block a user