Skip to content

Commit 55faa2d

Browse files
committed
Lifetime reference search
1 parent 067067a commit 55faa2d

File tree

12 files changed

+373
-41
lines changed

12 files changed

+373
-41
lines changed

crates/ide/src/display/navigation_target.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use ide_db::{defs::Definition, RootDatabase};
99
use syntax::{
1010
ast::{self, NameOwner},
1111
match_ast, AstNode, SmolStr,
12-
SyntaxKind::{self, IDENT_PAT, TYPE_PARAM},
12+
SyntaxKind::{self, IDENT_PAT, LIFETIME_PARAM, TYPE_PARAM},
1313
TextRange,
1414
};
1515

@@ -182,6 +182,7 @@ impl TryToNav for Definition {
182182
Definition::SelfType(it) => Some(it.to_nav(db)),
183183
Definition::Local(it) => Some(it.to_nav(db)),
184184
Definition::TypeParam(it) => Some(it.to_nav(db)),
185+
Definition::LifetimeParam(it) => Some(it.to_nav(db)),
185186
}
186187
}
187188
}
@@ -376,6 +377,23 @@ impl ToNav for hir::TypeParam {
376377
}
377378
}
378379

380+
impl ToNav for hir::LifetimeParam {
381+
fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
382+
let src = self.source(db);
383+
let full_range = src.value.syntax().text_range();
384+
NavigationTarget {
385+
file_id: src.file_id.original_file(db),
386+
name: self.name(db).to_string().into(),
387+
kind: LIFETIME_PARAM,
388+
full_range,
389+
focus_range: Some(full_range),
390+
container_name: None,
391+
description: None,
392+
docs: None,
393+
}
394+
}
395+
}
396+
379397
pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<Documentation> {
380398
let parse = db.parse(symbol.file_id);
381399
let node = symbol.ptr.to_node(parse.tree().syntax());

crates/ide/src/doc_links.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,10 @@ fn rewrite_intra_doc_link(
190190
},
191191
Definition::Macro(it) => it.resolve_doc_path(db, link, ns),
192192
Definition::Field(it) => it.resolve_doc_path(db, link, ns),
193-
Definition::SelfType(_) | Definition::Local(_) | Definition::TypeParam(_) => return None,
193+
Definition::SelfType(_)
194+
| Definition::Local(_)
195+
| Definition::TypeParam(_)
196+
| Definition::LifetimeParam(_) => return None,
194197
}?;
195198
let krate = resolved.module(db)?.krate();
196199
let canonical_path = resolved.canonical_path(db)?;

crates/ide/src/goto_definition.rs

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use either::Either;
12
use hir::Semantics;
23
use ide_db::{
34
base_db::FileId,
@@ -33,7 +34,7 @@ pub(crate) fn goto_definition(
3334
let nav_targets = match_ast! {
3435
match parent {
3536
ast::NameRef(name_ref) => {
36-
reference_definition(&sema, &name_ref).to_vec()
37+
reference_definition(&sema, Either::Right(&name_ref)).to_vec()
3738
},
3839
ast::Name(name) => {
3940
let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db);
@@ -53,6 +54,13 @@ pub(crate) fn goto_definition(
5354
let self_param = func.param_list()?.self_param()?;
5455
vec![self_to_nav_target(self_param, position.file_id)?]
5556
},
57+
ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, &lt) {
58+
let def = name_class.referenced_or_defined(sema.db);
59+
let nav = def.try_to_nav(sema.db)?;
60+
vec![nav]
61+
} else {
62+
reference_definition(&sema, Either::Left(&lt)).to_vec()
63+
},
5664
_ => return None,
5765
}
5866
};
@@ -64,7 +72,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
6472
return tokens.max_by_key(priority);
6573
fn priority(n: &SyntaxToken) -> usize {
6674
match n.kind() {
67-
IDENT | INT_NUMBER | T![self] => 2,
75+
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 2,
6876
kind if kind.is_trivia() => 0,
6977
_ => 1,
7078
}
@@ -102,9 +110,12 @@ impl ReferenceResult {
102110

103111
pub(crate) fn reference_definition(
104112
sema: &Semantics<RootDatabase>,
105-
name_ref: &ast::NameRef,
113+
name_ref: Either<&ast::Lifetime, &ast::NameRef>,
106114
) -> ReferenceResult {
107-
let name_kind = NameRefClass::classify(sema, name_ref);
115+
let name_kind = name_ref.either(
116+
|lifetime| NameRefClass::classify_lifetime(sema, lifetime),
117+
|name_ref| NameRefClass::classify(sema, name_ref),
118+
);
108119
if let Some(def) = name_kind {
109120
let def = def.referenced(sema.db);
110121
return match def.try_to_nav(sema.db) {
@@ -114,10 +125,9 @@ pub(crate) fn reference_definition(
114125
}
115126

116127
// Fallback index based approach:
117-
let navs = symbol_index::index_resolve(sema.db, name_ref)
118-
.into_iter()
119-
.map(|s| s.to_nav(sema.db))
120-
.collect();
128+
let name = name_ref.either(ast::Lifetime::text, ast::NameRef::text);
129+
let navs =
130+
symbol_index::index_resolve(sema.db, name).into_iter().map(|s| s.to_nav(sema.db)).collect();
121131
ReferenceResult::Approximate(navs)
122132
}
123133

@@ -1033,6 +1043,37 @@ impl Foo {
10331043
fn bar(&self<|>) {
10341044
//^^^^
10351045
}
1046+
}"#,
1047+
)
1048+
}
1049+
1050+
#[test]
1051+
fn goto_lifetime_param_on_decl() {
1052+
check(
1053+
r#"
1054+
fn foo<'foobar<|>>(_: &'foobar ()) {
1055+
//^^^^^^^
1056+
}"#,
1057+
)
1058+
}
1059+
1060+
#[test]
1061+
fn goto_lifetime_param_decl() {
1062+
check(
1063+
r#"
1064+
fn foo<'foobar>(_: &'foobar<|> ()) {
1065+
//^^^^^^^
1066+
}"#,
1067+
)
1068+
}
1069+
1070+
#[test]
1071+
fn goto_lifetime_param_decl_nested() {
1072+
check(
1073+
r#"
1074+
fn foo<'foobar>(_: &'foobar ()) {
1075+
fn foo<'foobar>(_: &'foobar<|> ()) {}
1076+
//^^^^^^^
10361077
}"#,
10371078
)
10381079
}

crates/ide/src/hover.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
364364
Adt::Enum(it) => from_def_source(db, it, mod_path),
365365
})
366366
}
367-
Definition::TypeParam(_) => {
367+
Definition::TypeParam(_) | Definition::LifetimeParam(_) => {
368368
// FIXME: Hover for generic param
369369
None
370370
}

crates/ide/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,13 @@ impl Analysis {
528528
self.with_db(|db| references::rename::rename(db, position, new_name))
529529
}
530530

531+
pub fn prepare_rename(
532+
&self,
533+
position: FilePosition,
534+
) -> Cancelable<Result<RangeInfo<()>, RenameError>> {
535+
self.with_db(|db| references::rename::prepare_rename(db, position))
536+
}
537+
531538
pub fn structural_search_replace(
532539
&self,
533540
query: &str,

crates/ide/src/references.rs

Lines changed: 86 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ pub(crate) fn find_all_refs(
130130
kind = ReferenceKind::FieldShorthandForLocal;
131131
}
132132
}
133+
} else if let Definition::LifetimeParam(_) = def {
134+
kind = ReferenceKind::Lifetime;
133135
};
134136

135137
let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) };
@@ -148,11 +150,29 @@ fn find_name(
148150
let range = name.syntax().text_range();
149151
return Some(RangeInfo::new(range, def));
150152
}
151-
let name_ref =
152-
sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?;
153-
let def = NameRefClass::classify(sema, &name_ref)?.referenced(sema.db);
154-
let range = name_ref.syntax().text_range();
155-
Some(RangeInfo::new(range, def))
153+
154+
let (text_range, def) = if let Some(lifetime) =
155+
sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset)
156+
{
157+
if let Some(def) = NameRefClass::classify_lifetime(sema, &lifetime)
158+
.map(|class| NameRefClass::referenced(class, sema.db))
159+
{
160+
(lifetime.syntax().text_range(), def)
161+
} else {
162+
(
163+
lifetime.syntax().text_range(),
164+
NameClass::classify_lifetime(sema, &lifetime)?.referenced_or_defined(sema.db),
165+
)
166+
}
167+
} else {
168+
let name_ref =
169+
sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?;
170+
(
171+
name_ref.syntax().text_range(),
172+
NameRefClass::classify(sema, &name_ref)?.referenced(sema.db),
173+
)
174+
};
175+
Some(RangeInfo::new(text_range, def))
156176
}
157177

158178
fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> {
@@ -1005,4 +1025,65 @@ impl Foo {
10051025
}
10061026
expect.assert_eq(&actual)
10071027
}
1028+
1029+
#[test]
1030+
fn test_find_lifetimes_function() {
1031+
check(
1032+
r#"
1033+
trait Foo<'a> {}
1034+
impl<'a> Foo<'a> for &'a () {}
1035+
fn foo<'a, 'b: 'a>(x: &'a<|> ()) -> &'a () where &'a (): Foo<'a> {
1036+
fn bar<'a>(_: &'a ()) {}
1037+
x
1038+
}
1039+
"#,
1040+
expect![[r#"
1041+
'a LIFETIME_PARAM FileId(0) 55..57 55..57 Lifetime
1042+
1043+
FileId(0) 63..65 Lifetime
1044+
FileId(0) 71..73 Lifetime
1045+
FileId(0) 82..84 Lifetime
1046+
FileId(0) 95..97 Lifetime
1047+
FileId(0) 106..108 Lifetime
1048+
"#]],
1049+
);
1050+
}
1051+
1052+
#[test]
1053+
fn test_find_lifetimes_type_alias() {
1054+
check(
1055+
r#"
1056+
type Foo<'a, T> where T: 'a<|> = &'a T;
1057+
"#,
1058+
expect![[r#"
1059+
'a LIFETIME_PARAM FileId(0) 9..11 9..11 Lifetime
1060+
1061+
FileId(0) 25..27 Lifetime
1062+
FileId(0) 31..33 Lifetime
1063+
"#]],
1064+
);
1065+
}
1066+
1067+
#[test]
1068+
fn test_find_lifetimes_trait_impl() {
1069+
check(
1070+
r#"
1071+
trait Foo<'a> {
1072+
fn foo() -> &'a ();
1073+
}
1074+
impl<'a> Foo<'a> for &'a () {
1075+
fn foo() -> &'a<|> () {
1076+
unimplemented!()
1077+
}
1078+
}
1079+
"#,
1080+
expect![[r#"
1081+
'a LIFETIME_PARAM FileId(0) 47..49 47..49 Lifetime
1082+
1083+
FileId(0) 55..57 Lifetime
1084+
FileId(0) 64..66 Lifetime
1085+
FileId(0) 89..91 Lifetime
1086+
"#]],
1087+
);
1088+
}
10081089
}

0 commit comments

Comments
 (0)