aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Wirth <[email protected]>2020-12-16 20:35:15 +0000
committerLukas Wirth <[email protected]>2020-12-16 21:21:01 +0000
commit55faa2daa3fc8bd213038a012b1c5e9ad5fd3736 (patch)
tree2d84a7fa9fe93aa7a6bdd4d0f9cda87e6bb4a5da
parent067067a6c11bb5afda98f5af14bfdec4744e7812 (diff)
Lifetime reference search
-rw-r--r--crates/ide/src/display/navigation_target.rs20
-rw-r--r--crates/ide/src/doc_links.rs5
-rw-r--r--crates/ide/src/goto_definition.rs57
-rw-r--r--crates/ide/src/hover.rs2
-rw-r--r--crates/ide/src/lib.rs7
-rw-r--r--crates/ide/src/references.rs91
-rw-r--r--crates/ide/src/references/rename.rs104
-rw-r--r--crates/ide/src/syntax_highlighting.rs1
-rw-r--r--crates/ide_db/src/defs.rs59
-rw-r--r--crates/ide_db/src/search.rs63
-rw-r--r--crates/ide_db/src/symbol_index.rs3
-rw-r--r--crates/rust-analyzer/src/handlers.rs2
12 files changed, 373 insertions, 41 deletions
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index 234f80a3a..ce0f4214c 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -9,7 +9,7 @@ use ide_db::{defs::Definition, RootDatabase};
9use syntax::{ 9use syntax::{
10 ast::{self, NameOwner}, 10 ast::{self, NameOwner},
11 match_ast, AstNode, SmolStr, 11 match_ast, AstNode, SmolStr,
12 SyntaxKind::{self, IDENT_PAT, TYPE_PARAM}, 12 SyntaxKind::{self, IDENT_PAT, LIFETIME_PARAM, TYPE_PARAM},
13 TextRange, 13 TextRange,
14}; 14};
15 15
@@ -182,6 +182,7 @@ impl TryToNav for Definition {
182 Definition::SelfType(it) => Some(it.to_nav(db)), 182 Definition::SelfType(it) => Some(it.to_nav(db)),
183 Definition::Local(it) => Some(it.to_nav(db)), 183 Definition::Local(it) => Some(it.to_nav(db)),
184 Definition::TypeParam(it) => Some(it.to_nav(db)), 184 Definition::TypeParam(it) => Some(it.to_nav(db)),
185 Definition::LifetimeParam(it) => Some(it.to_nav(db)),
185 } 186 }
186 } 187 }
187} 188}
@@ -376,6 +377,23 @@ impl ToNav for hir::TypeParam {
376 } 377 }
377} 378}
378 379
380impl 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
379pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<Documentation> { 397pub(crate) fn docs_from_symbol(db: &RootDatabase, symbol: &FileSymbol) -> Option<Documentation> {
380 let parse = db.parse(symbol.file_id); 398 let parse = db.parse(symbol.file_id);
381 let node = symbol.ptr.to_node(parse.tree().syntax()); 399 let node = symbol.ptr.to_node(parse.tree().syntax());
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 10263537a..2b5794a31 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -190,7 +190,10 @@ fn rewrite_intra_doc_link(
190 }, 190 },
191 Definition::Macro(it) => it.resolve_doc_path(db, link, ns), 191 Definition::Macro(it) => it.resolve_doc_path(db, link, ns),
192 Definition::Field(it) => it.resolve_doc_path(db, link, ns), 192 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,
194 }?; 197 }?;
195 let krate = resolved.module(db)?.krate(); 198 let krate = resolved.module(db)?.krate();
196 let canonical_path = resolved.canonical_path(db)?; 199 let canonical_path = resolved.canonical_path(db)?;
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index b9810457f..173509b08 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,3 +1,4 @@
1use either::Either;
1use hir::Semantics; 2use hir::Semantics;
2use ide_db::{ 3use ide_db::{
3 base_db::FileId, 4 base_db::FileId,
@@ -33,7 +34,7 @@ pub(crate) fn goto_definition(
33 let nav_targets = match_ast! { 34 let nav_targets = match_ast! {
34 match parent { 35 match parent {
35 ast::NameRef(name_ref) => { 36 ast::NameRef(name_ref) => {
36 reference_definition(&sema, &name_ref).to_vec() 37 reference_definition(&sema, Either::Right(&name_ref)).to_vec()
37 }, 38 },
38 ast::Name(name) => { 39 ast::Name(name) => {
39 let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); 40 let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db);
@@ -53,6 +54,13 @@ pub(crate) fn goto_definition(
53 let self_param = func.param_list()?.self_param()?; 54 let self_param = func.param_list()?.self_param()?;
54 vec![self_to_nav_target(self_param, position.file_id)?] 55 vec![self_to_nav_target(self_param, position.file_id)?]
55 }, 56 },
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 },
56 _ => return None, 64 _ => return None,
57 } 65 }
58 }; 66 };
@@ -64,7 +72,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
64 return tokens.max_by_key(priority); 72 return tokens.max_by_key(priority);
65 fn priority(n: &SyntaxToken) -> usize { 73 fn priority(n: &SyntaxToken) -> usize {
66 match n.kind() { 74 match n.kind() {
67 IDENT | INT_NUMBER | T![self] => 2, 75 IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] => 2,
68 kind if kind.is_trivia() => 0, 76 kind if kind.is_trivia() => 0,
69 _ => 1, 77 _ => 1,
70 } 78 }
@@ -102,9 +110,12 @@ impl ReferenceResult {
102 110
103pub(crate) fn reference_definition( 111pub(crate) fn reference_definition(
104 sema: &Semantics<RootDatabase>, 112 sema: &Semantics<RootDatabase>,
105 name_ref: &ast::NameRef, 113 name_ref: Either<&ast::Lifetime, &ast::NameRef>,
106) -> ReferenceResult { 114) -> 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 );
108 if let Some(def) = name_kind { 119 if let Some(def) = name_kind {
109 let def = def.referenced(sema.db); 120 let def = def.referenced(sema.db);
110 return match def.try_to_nav(sema.db) { 121 return match def.try_to_nav(sema.db) {
@@ -114,10 +125,9 @@ pub(crate) fn reference_definition(
114 } 125 }
115 126
116 // Fallback index based approach: 127 // Fallback index based approach:
117 let navs = symbol_index::index_resolve(sema.db, name_ref) 128 let name = name_ref.either(ast::Lifetime::text, ast::NameRef::text);
118 .into_iter() 129 let navs =
119 .map(|s| s.to_nav(sema.db)) 130 symbol_index::index_resolve(sema.db, name).into_iter().map(|s| s.to_nav(sema.db)).collect();
120 .collect();
121 ReferenceResult::Approximate(navs) 131 ReferenceResult::Approximate(navs)
122} 132}
123 133
@@ -1036,4 +1046,35 @@ impl Foo {
1036}"#, 1046}"#,
1037 ) 1047 )
1038 } 1048 }
1049
1050 #[test]
1051 fn goto_lifetime_param_on_decl() {
1052 check(
1053 r#"
1054fn foo<'foobar<|>>(_: &'foobar ()) {
1055 //^^^^^^^
1056}"#,
1057 )
1058 }
1059
1060 #[test]
1061 fn goto_lifetime_param_decl() {
1062 check(
1063 r#"
1064fn foo<'foobar>(_: &'foobar<|> ()) {
1065 //^^^^^^^
1066}"#,
1067 )
1068 }
1069
1070 #[test]
1071 fn goto_lifetime_param_decl_nested() {
1072 check(
1073 r#"
1074fn foo<'foobar>(_: &'foobar ()) {
1075 fn foo<'foobar>(_: &'foobar<|> ()) {}
1076 //^^^^^^^
1077}"#,
1078 )
1079 }
1039} 1080}
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index ab017d2ad..a01b0c894 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -364,7 +364,7 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option<Markup> {
364 Adt::Enum(it) => from_def_source(db, it, mod_path), 364 Adt::Enum(it) => from_def_source(db, it, mod_path),
365 }) 365 })
366 } 366 }
367 Definition::TypeParam(_) => { 367 Definition::TypeParam(_) | Definition::LifetimeParam(_) => {
368 // FIXME: Hover for generic param 368 // FIXME: Hover for generic param
369 None 369 None
370 } 370 }
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 71068cac2..c5c652cda 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -528,6 +528,13 @@ impl Analysis {
528 self.with_db(|db| references::rename::rename(db, position, new_name)) 528 self.with_db(|db| references::rename::rename(db, position, new_name))
529 } 529 }
530 530
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
531 pub fn structural_search_replace( 538 pub fn structural_search_replace(
532 &self, 539 &self,
533 query: &str, 540 query: &str,
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index 675957fff..98190a86b 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -130,6 +130,8 @@ pub(crate) fn find_all_refs(
130 kind = ReferenceKind::FieldShorthandForLocal; 130 kind = ReferenceKind::FieldShorthandForLocal;
131 } 131 }
132 } 132 }
133 } else if let Definition::LifetimeParam(_) = def {
134 kind = ReferenceKind::Lifetime;
133 }; 135 };
134 136
135 let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) }; 137 let declaration = Declaration { nav, kind, access: decl_access(&def, &syntax, decl_range) };
@@ -148,11 +150,29 @@ fn find_name(
148 let range = name.syntax().text_range(); 150 let range = name.syntax().text_range();
149 return Some(RangeInfo::new(range, def)); 151 return Some(RangeInfo::new(range, def));
150 } 152 }
151 let name_ref = 153
152 sema.find_node_at_offset_with_descend::<ast::NameRef>(&syntax, position.offset)?; 154 let (text_range, def) = if let Some(lifetime) =
153 let def = NameRefClass::classify(sema, &name_ref)?.referenced(sema.db); 155 sema.find_node_at_offset_with_descend::<ast::Lifetime>(&syntax, position.offset)
154 let range = name_ref.syntax().text_range(); 156 {
155 Some(RangeInfo::new(range, def)) 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))
156} 176}
157 177
158fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> { 178fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Option<ReferenceAccess> {
@@ -1005,4 +1025,65 @@ impl Foo {
1005 } 1025 }
1006 expect.assert_eq(&actual) 1026 expect.assert_eq(&actual)
1007 } 1027 }
1028
1029 #[test]
1030 fn test_find_lifetimes_function() {
1031 check(
1032 r#"
1033trait Foo<'a> {}
1034impl<'a> Foo<'a> for &'a () {}
1035fn 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#"
1056type 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#"
1071trait Foo<'a> {
1072 fn foo() -> &'a ();
1073}
1074impl<'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 }
1008} 1089}
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 44081f210..56e923841 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -35,6 +35,29 @@ impl fmt::Display for RenameError {
35 35
36impl Error for RenameError {} 36impl Error for RenameError {}
37 37
38pub(crate) fn prepare_rename(
39 db: &RootDatabase,
40 position: FilePosition,
41) -> Result<RangeInfo<()>, RenameError> {
42 let sema = Semantics::new(db);
43 let source_file = sema.parse(position.file_id);
44 let syntax = source_file.syntax();
45 if let Some(module) = find_module_at_offset(&sema, position, syntax) {
46 rename_mod(&sema, position, module, "dummy")
47 } else if let Some(self_token) =
48 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
49 {
50 rename_self_to_param(&sema, position, self_token, "dummy")
51 } else {
52 let range = match find_all_refs(&sema, position, None) {
53 Some(RangeInfo { range, .. }) => range,
54 None => return Err(RenameError("No references found at position".to_string())),
55 };
56 Ok(RangeInfo::new(range, SourceChange::from(vec![])))
57 }
58 .map(|info| RangeInfo::new(info.range, ()))
59}
60
38pub(crate) fn rename( 61pub(crate) fn rename(
39 db: &RootDatabase, 62 db: &RootDatabase,
40 position: FilePosition, 63 position: FilePosition,
@@ -49,11 +72,18 @@ pub(crate) fn rename_with_semantics(
49 position: FilePosition, 72 position: FilePosition,
50 new_name: &str, 73 new_name: &str,
51) -> Result<RangeInfo<SourceChange>, RenameError> { 74) -> Result<RangeInfo<SourceChange>, RenameError> {
52 match lex_single_syntax_kind(new_name) { 75 let is_lifetime_name = match lex_single_syntax_kind(new_name) {
53 Some(res) => match res { 76 Some(res) => match res {
54 (SyntaxKind::IDENT, _) => (), 77 (SyntaxKind::IDENT, _) => false,
55 (SyntaxKind::UNDERSCORE, _) => (), 78 (SyntaxKind::UNDERSCORE, _) => false,
56 (SyntaxKind::SELF_KW, _) => return rename_to_self(&sema, position), 79 (SyntaxKind::SELF_KW, _) => return rename_to_self(&sema, position),
80 (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => true,
81 (SyntaxKind::LIFETIME_IDENT, _) => {
82 return Err(RenameError(format!(
83 "Invalid name `{0}`: Cannot rename lifetime to {0}",
84 new_name
85 )))
86 }
57 (_, Some(syntax_error)) => { 87 (_, Some(syntax_error)) => {
58 return Err(RenameError(format!("Invalid name `{}`: {}", new_name, syntax_error))) 88 return Err(RenameError(format!("Invalid name `{}`: {}", new_name, syntax_error)))
59 } 89 }
@@ -62,18 +92,21 @@ pub(crate) fn rename_with_semantics(
62 } 92 }
63 }, 93 },
64 None => return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))), 94 None => return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))),
65 } 95 };
66 96
67 let source_file = sema.parse(position.file_id); 97 let source_file = sema.parse(position.file_id);
68 let syntax = source_file.syntax(); 98 let syntax = source_file.syntax();
69 if let Some(module) = find_module_at_offset(&sema, position, syntax) { 99 // this is here to prevent lifetime renames from happening on modules and self
100 if is_lifetime_name {
101 rename_reference(&sema, position, new_name, is_lifetime_name)
102 } else if let Some(module) = find_module_at_offset(&sema, position, syntax) {
70 rename_mod(&sema, position, module, new_name) 103 rename_mod(&sema, position, module, new_name)
71 } else if let Some(self_token) = 104 } else if let Some(self_token) =
72 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) 105 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
73 { 106 {
74 rename_self_to_param(&sema, position, self_token, new_name) 107 rename_self_to_param(&sema, position, self_token, new_name)
75 } else { 108 } else {
76 rename_reference(&sema, position, new_name) 109 rename_reference(&sema, position, new_name, is_lifetime_name)
77 } 110 }
78} 111}
79 112
@@ -355,12 +388,26 @@ fn rename_reference(
355 sema: &Semantics<RootDatabase>, 388 sema: &Semantics<RootDatabase>,
356 position: FilePosition, 389 position: FilePosition,
357 new_name: &str, 390 new_name: &str,
391 is_lifetime_name: bool,
358) -> Result<RangeInfo<SourceChange>, RenameError> { 392) -> Result<RangeInfo<SourceChange>, RenameError> {
359 let RangeInfo { range, info: refs } = match find_all_refs(sema, position, None) { 393 let RangeInfo { range, info: refs } = match find_all_refs(sema, position, None) {
360 Some(range_info) => range_info, 394 Some(range_info) => range_info,
361 None => return Err(RenameError("No references found at position".to_string())), 395 None => return Err(RenameError("No references found at position".to_string())),
362 }; 396 };
363 397
398 match (refs.declaration.kind == ReferenceKind::Lifetime, is_lifetime_name) {
399 (true, false) => {
400 return Err(RenameError(format!(
401 "Invalid name `{}`: not a lifetime identifier",
402 new_name
403 )))
404 }
405 (false, true) => {
406 return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name)))
407 }
408 _ => (),
409 }
410
364 let edit = refs 411 let edit = refs
365 .into_iter() 412 .into_iter()
366 .map(|reference| source_edit_from_reference(sema, reference, new_name)) 413 .map(|reference| source_edit_from_reference(sema, reference, new_name))
@@ -465,6 +512,24 @@ mod tests {
465 } 512 }
466 513
467 #[test] 514 #[test]
515 fn test_rename_to_invalid_identifier_lifetime() {
516 check(
517 "'foo",
518 r#"fn main() { let i<|> = 1; }"#,
519 "error: Invalid name `'foo`: not an identifier",
520 );
521 }
522
523 #[test]
524 fn test_rename_to_invalid_identifier_lifetime2() {
525 check(
526 "foo",
527 r#"fn main<'a>(_: &'a<|> ()) {}"#,
528 "error: Invalid name `foo`: not a lifetime identifier",
529 );
530 }
531
532 #[test]
468 fn test_rename_for_local() { 533 fn test_rename_for_local() {
469 check( 534 check(
470 "k", 535 "k",
@@ -1396,4 +1461,31 @@ fn foo(Foo { i: bar }: foo) -> i32 {
1396"#, 1461"#,
1397 ) 1462 )
1398 } 1463 }
1464
1465 #[test]
1466 fn test_rename_lifetimes() {
1467 check(
1468 "'yeeee",
1469 r#"
1470trait Foo<'a> {
1471 fn foo() -> &'a ();
1472}
1473impl<'a> Foo<'a> for &'a () {
1474 fn foo() -> &'a<|> () {
1475 unimplemented!()
1476 }
1477}
1478"#,
1479 r#"
1480trait Foo<'a> {
1481 fn foo() -> &'a ();
1482}
1483impl<'yeeee> Foo<'yeeee> for &'yeeee () {
1484 fn foo() -> &'yeeee () {
1485 unimplemented!()
1486 }
1487}
1488"#,
1489 )
1490 }
1399} 1491}
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 990b0f7d9..488969f1a 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -806,6 +806,7 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
806 } 806 }
807 return h; 807 return h;
808 } 808 }
809 Definition::LifetimeParam(_) => HighlightTag::Lifetime,
809 } 810 }
810 .into() 811 .into()
811} 812}
diff --git a/crates/ide_db/src/defs.rs b/crates/ide_db/src/defs.rs
index d4a774261..f2d1e4c39 100644
--- a/crates/ide_db/src/defs.rs
+++ b/crates/ide_db/src/defs.rs
@@ -6,12 +6,12 @@
6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). 6// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
7 7
8use hir::{ 8use hir::{
9 db::HirDatabase, Crate, Field, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, 9 db::HirDatabase, Crate, Field, HasVisibility, ImplDef, LifetimeParam, Local, MacroDef, Module,
10 Name, PathResolution, Semantics, TypeParam, Visibility, 10 ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility,
11}; 11};
12use syntax::{ 12use syntax::{
13 ast::{self, AstNode}, 13 ast::{self, AstNode},
14 match_ast, SyntaxNode, 14 match_ast, SyntaxKind, SyntaxNode,
15}; 15};
16 16
17use crate::RootDatabase; 17use crate::RootDatabase;
@@ -25,6 +25,8 @@ pub enum Definition {
25 SelfType(ImplDef), 25 SelfType(ImplDef),
26 Local(Local), 26 Local(Local),
27 TypeParam(TypeParam), 27 TypeParam(TypeParam),
28 LifetimeParam(LifetimeParam),
29 // FIXME: Label
28} 30}
29 31
30impl Definition { 32impl Definition {
@@ -36,6 +38,7 @@ impl Definition {
36 Definition::SelfType(it) => Some(it.module(db)), 38 Definition::SelfType(it) => Some(it.module(db)),
37 Definition::Local(it) => Some(it.module(db)), 39 Definition::Local(it) => Some(it.module(db)),
38 Definition::TypeParam(it) => Some(it.module(db)), 40 Definition::TypeParam(it) => Some(it.module(db)),
41 Definition::LifetimeParam(it) => Some(it.module(db)),
39 } 42 }
40 } 43 }
41 44
@@ -47,6 +50,7 @@ impl Definition {
47 Definition::SelfType(_) => None, 50 Definition::SelfType(_) => None,
48 Definition::Local(_) => None, 51 Definition::Local(_) => None,
49 Definition::TypeParam(_) => None, 52 Definition::TypeParam(_) => None,
53 Definition::LifetimeParam(_) => None,
50 } 54 }
51 } 55 }
52 56
@@ -72,6 +76,7 @@ impl Definition {
72 Definition::SelfType(_) => return None, 76 Definition::SelfType(_) => return None,
73 Definition::Local(it) => it.name(db)?, 77 Definition::Local(it) => it.name(db)?,
74 Definition::TypeParam(it) => it.name(db), 78 Definition::TypeParam(it) => it.name(db),
79 Definition::LifetimeParam(it) => it.name(db),
75 }; 80 };
76 Some(name) 81 Some(name)
77 } 82 }
@@ -229,6 +234,25 @@ impl NameClass {
229 } 234 }
230 } 235 }
231 } 236 }
237
238 pub fn classify_lifetime(
239 sema: &Semantics<RootDatabase>,
240 lifetime: &ast::Lifetime,
241 ) -> Option<NameClass> {
242 let _p = profile::span("classify_lifetime").detail(|| lifetime.to_string());
243 let parent = lifetime.syntax().parent()?;
244
245 match_ast! {
246 match parent {
247 ast::LifetimeParam(it) => {
248 let def = sema.to_def(&it)?;
249 Some(NameClass::Definition(Definition::LifetimeParam(def)))
250 },
251 ast::Label(_it) => None,
252 _ => None,
253 }
254 }
255 }
232} 256}
233 257
234#[derive(Debug)] 258#[derive(Debug)]
@@ -338,6 +362,35 @@ impl NameRefClass {
338 let resolved = sema.resolve_extern_crate(&extern_crate)?; 362 let resolved = sema.resolve_extern_crate(&extern_crate)?;
339 Some(NameRefClass::ExternCrate(resolved)) 363 Some(NameRefClass::ExternCrate(resolved))
340 } 364 }
365
366 pub fn classify_lifetime(
367 sema: &Semantics<RootDatabase>,
368 lifetime: &ast::Lifetime,
369 ) -> Option<NameRefClass> {
370 let _p = profile::span("classify_lifetime_ref").detail(|| lifetime.to_string());
371 let parent = lifetime.syntax().parent()?;
372 match parent.kind() {
373 SyntaxKind::LIFETIME_ARG
374 | SyntaxKind::SELF_PARAM
375 | SyntaxKind::TYPE_BOUND
376 | SyntaxKind::WHERE_PRED
377 | SyntaxKind::REF_TYPE => sema
378 .resolve_lifetime_param(lifetime)
379 .map(Definition::LifetimeParam)
380 .map(NameRefClass::Definition),
381 // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
382 // if our lifetime is in a LifetimeParam without being the constrained lifetime
383 _ if ast::LifetimeParam::cast(parent).and_then(|param| param.lifetime()).as_ref()
384 != Some(lifetime) =>
385 {
386 sema.resolve_lifetime_param(lifetime)
387 .map(Definition::LifetimeParam)
388 .map(NameRefClass::Definition)
389 }
390 SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => None,
391 _ => None,
392 }
393 }
341} 394}
342 395
343impl From<PathResolution> for Definition { 396impl From<PathResolution> for Definition {
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index 3936c7390..5b3997bcf 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -33,6 +33,7 @@ pub enum ReferenceKind {
33 RecordFieldExprOrPat, 33 RecordFieldExprOrPat,
34 SelfKw, 34 SelfKw,
35 EnumLiteral, 35 EnumLiteral,
36 Lifetime,
36 Other, 37 Other,
37} 38}
38 39
@@ -129,6 +130,25 @@ impl Definition {
129 return SearchScope::new(res); 130 return SearchScope::new(res);
130 } 131 }
131 132
133 if let Definition::LifetimeParam(param) = self {
134 let range = match param.parent(db) {
135 hir::GenericDef::Function(it) => it.source(db).value.syntax().text_range(),
136 hir::GenericDef::Adt(it) => match it {
137 hir::Adt::Struct(it) => it.source(db).value.syntax().text_range(),
138 hir::Adt::Union(it) => it.source(db).value.syntax().text_range(),
139 hir::Adt::Enum(it) => it.source(db).value.syntax().text_range(),
140 },
141 hir::GenericDef::Trait(it) => it.source(db).value.syntax().text_range(),
142 hir::GenericDef::TypeAlias(it) => it.source(db).value.syntax().text_range(),
143 hir::GenericDef::ImplDef(it) => it.source(db).value.syntax().text_range(),
144 hir::GenericDef::EnumVariant(it) => it.source(db).value.syntax().text_range(),
145 hir::GenericDef::Const(it) => it.source(db).value.syntax().text_range(),
146 };
147 let mut res = FxHashMap::default();
148 res.insert(file_id, Some(range));
149 return SearchScope::new(res);
150 }
151
132 let vis = self.visibility(db); 152 let vis = self.visibility(db);
133 153
134 if let Some(Visibility::Module(module)) = vis.and_then(|it| it.into()) { 154 if let Some(Visibility::Module(module)) = vis.and_then(|it| it.into()) {
@@ -255,25 +275,42 @@ impl<'a> FindUsages<'a> {
255 continue; 275 continue;
256 } 276 }
257 277
258 match sema.find_node_at_offset_with_descend(&tree, offset) { 278 if let Some(name_ref) = sema.find_node_at_offset_with_descend(&tree, offset) {
259 Some(name_ref) => { 279 if self.found_name_ref(&name_ref, sink) {
260 if self.found_name_ref(&name_ref, sink) { 280 return;
261 return; 281 }
262 } 282 } else if let Some(name) = sema.find_node_at_offset_with_descend(&tree, offset) {
283 if self.found_name(&name, sink) {
284 return;
285 }
286 } else if let Some(lifetime) = sema.find_node_at_offset_with_descend(&tree, offset)
287 {
288 if self.found_lifetime(&lifetime, sink) {
289 return;
263 } 290 }
264 None => match sema.find_node_at_offset_with_descend(&tree, offset) {
265 Some(name) => {
266 if self.found_name(&name, sink) {
267 return;
268 }
269 }
270 None => {}
271 },
272 } 291 }
273 } 292 }
274 } 293 }
275 } 294 }
276 295
296 fn found_lifetime(
297 &self,
298 lifetime: &ast::Lifetime,
299 sink: &mut dyn FnMut(Reference) -> bool,
300 ) -> bool {
301 match NameRefClass::classify_lifetime(self.sema, lifetime) {
302 Some(NameRefClass::Definition(def)) if &def == self.def => {
303 let reference = Reference {
304 file_range: self.sema.original_range(lifetime.syntax()),
305 kind: ReferenceKind::Lifetime,
306 access: None,
307 };
308 sink(reference)
309 }
310 _ => false, // not a usage
311 }
312 }
313
277 fn found_name_ref( 314 fn found_name_ref(
278 &self, 315 &self,
279 name_ref: &ast::NameRef, 316 name_ref: &ast::NameRef,
diff --git a/crates/ide_db/src/symbol_index.rs b/crates/ide_db/src/symbol_index.rs
index 121063aea..ca455fa03 100644
--- a/crates/ide_db/src/symbol_index.rs
+++ b/crates/ide_db/src/symbol_index.rs
@@ -209,8 +209,7 @@ pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<Fil
209 query.search(&buf) 209 query.search(&buf)
210} 210}
211 211
212pub fn index_resolve(db: &RootDatabase, name_ref: &ast::NameRef) -> Vec<FileSymbol> { 212pub fn index_resolve(db: &RootDatabase, name: &SmolStr) -> Vec<FileSymbol> {
213 let name = name_ref.text();
214 let mut query = Query::new(name.to_string()); 213 let mut query = Query::new(name.to_string());
215 query.exact(); 214 query.exact();
216 query.limit(4); 215 query.limit(4);
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 94e2bfa1b..af226c109 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -733,7 +733,7 @@ pub(crate) fn handle_prepare_rename(
733 let _p = profile::span("handle_prepare_rename"); 733 let _p = profile::span("handle_prepare_rename");
734 let position = from_proto::file_position(&snap, params)?; 734 let position = from_proto::file_position(&snap, params)?;
735 735
736 let change = snap.analysis.rename(position, "dummy")??; 736 let change = snap.analysis.prepare_rename(position)??;
737 let line_index = snap.analysis.file_line_index(position.file_id)?; 737 let line_index = snap.analysis.file_line_index(position.file_id)?;
738 let range = to_proto::range(&line_index, change.range); 738 let range = to_proto::range(&line_index, change.range);
739 Ok(Some(PrepareRenameResponse::Range(range))) 739 Ok(Some(PrepareRenameResponse::Range(range)))