]> git.lizzy.rs Git - rust.git/commitdiff
Lifetime reference search
authorLukas Wirth <lukastw97@gmail.com>
Wed, 16 Dec 2020 20:35:15 +0000 (21:35 +0100)
committerLukas Wirth <lukastw97@gmail.com>
Wed, 16 Dec 2020 21:21:01 +0000 (22:21 +0100)
12 files changed:
crates/ide/src/display/navigation_target.rs
crates/ide/src/doc_links.rs
crates/ide/src/goto_definition.rs
crates/ide/src/hover.rs
crates/ide/src/lib.rs
crates/ide/src/references.rs
crates/ide/src/references/rename.rs
crates/ide/src/syntax_highlighting.rs
crates/ide_db/src/defs.rs
crates/ide_db/src/search.rs
crates/ide_db/src/symbol_index.rs
crates/rust-analyzer/src/handlers.rs

index 234f80a3a59b2f050356b48f24b90c7088b2288f..ce0f4214c6ed424730f019cfc88dbc287a0863e7 100644 (file)
@@ -9,7 +9,7 @@
 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 @@ fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
             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 @@ fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
     }
 }
 
+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());
index 10263537a488e88b44f1b4e348d3dff65ad0b45b..2b5794a315276948180be74f876c1399e4cb86e6 100644 (file)
@@ -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)?;
index b9810457fc021629f6fbfe7a5cfcec028e67dc5f..173509b08bd5c55ca79cf59c6dd1964eb27dab14 100644 (file)
@@ -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, &lt) {
+                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(&lt)).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 @@ fn to_vec(self) -> Vec<NavigationTarget> {
 
 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<|> ()) {}
+         //^^^^^^^
 }"#,
         )
     }
index ab017d2ada4c6c808b45e6a071f6cc9809dd958b..a01b0c8944f8c330023f6ac70497c2bef03c0ebe 100644 (file)
@@ -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
         }
index 71068cac28aa9021c25784de64d6bfc8429aa7bd..c5c652cda49e52f50f6df5f50641c47f151baa3e 100644 (file)
@@ -528,6 +528,13 @@ pub fn rename(
         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,
index 675957fff6f1edcdc9ca38ac0fdd8b522e13fc94..98190a86b254fe713edcc74a1a453506e0964fd5 100644 (file)
@@ -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 @@ fn check_with_scope(ra_fixture: &str, search_scope: Option<SearchScope>, expect:
         }
         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
+            "#]],
+        );
+    }
 }
index 44081f2100dbb8c2efd59bc73be5465d4293b2d4..56e9238414d2babbce4c57d9ee65ca50c1bf980a 100644 (file)
@@ -35,6 +35,29 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 
 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 @@ fn test_rename_to_invalid_identifier3() {
         );
     }
 
+    #[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!()
+    }
+}
 "#,
         )
     }
index 990b0f7d955c885cff29edda9b4833f61329a497..488969f1a15c4b1a9d3bad597daee7be974b0def 100644 (file)
@@ -806,6 +806,7 @@ fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
             }
             return h;
         }
+        Definition::LifetimeParam(_) => HighlightTag::Lifetime,
     }
     .into()
 }
index d4a774261c5783ba57c7c583410328a2625403f2..f2d1e4c392167eece42f3dd67bab7f8096529501 100644 (file)
@@ -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 @@ pub fn module(&self, db: &RootDatabase) -> Option<Module> {
             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 @@ pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> {
             Definition::SelfType(_) => None,
             Definition::Local(_) => None,
             Definition::TypeParam(_) => None,
+            Definition::LifetimeParam(_) => None,
         }
     }
 
@@ -72,6 +76,7 @@ pub fn name(&self, db: &RootDatabase) -> Option<Name> {
             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 @@ pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<Name
             }
         }
     }
+
+    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 @@ pub fn classify(
         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 {
index 3936c7390fcb27da3678282dbf6ba24b0d66b7f0..5b3997bcf8d461ea42759bf8e69da34187906d84 100644 (file)
@@ -33,6 +33,7 @@ pub enum ReferenceKind {
     RecordFieldExprOrPat,
     SelfKw,
     EnumLiteral,
+    Lifetime,
     Other,
 }
 
@@ -129,6 +130,25 @@ fn search_scope(&self, db: &RootDatabase) -> SearchScope {
             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 @@ fn search(self, sink: &mut dyn FnMut(Reference) -> bool) {
                     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,
index 121063aeab7dc316177f18ef9364b517fa5cdc5c..ca455fa03e32b850f7000c9fdcb81807d40f6340 100644 (file)
@@ -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);
index 94e2bfa1bbd516d59dffdc5d9842f00cb97cdd4e..af226c10900ec0ad66994441a9e34a0b8794d05c 100644 (file)
@@ -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)))