From 55faa2daa3fc8bd213038a012b1c5e9ad5fd3736 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 16 Dec 2020 21:35:15 +0100 Subject: Lifetime reference search --- crates/ide/src/display/navigation_target.rs | 20 +++++- crates/ide/src/doc_links.rs | 5 +- crates/ide/src/goto_definition.rs | 57 ++++++++++++--- crates/ide/src/hover.rs | 2 +- crates/ide/src/lib.rs | 7 ++ crates/ide/src/references.rs | 91 ++++++++++++++++++++++-- crates/ide/src/references/rename.rs | 104 ++++++++++++++++++++++++++-- crates/ide/src/syntax_highlighting.rs | 1 + crates/ide_db/src/defs.rs | 59 +++++++++++++++- crates/ide_db/src/search.rs | 63 +++++++++++++---- crates/ide_db/src/symbol_index.rs | 3 +- crates/rust-analyzer/src/handlers.rs | 2 +- 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}; 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 { let parse = db.parse(symbol.file_id); 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( }, 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)?; 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 @@ +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) -> Option { 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, - 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<|> ()) {} + //^^^^^^^ }"#, ) } 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 { Adt::Enum(it) => from_def_source(db, it, mod_path), }) } - Definition::TypeParam(_) => { + Definition::TypeParam(_) | Definition::LifetimeParam(_) => { // FIXME: Hover for generic param None } 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 { self.with_db(|db| references::rename::rename(db, position, new_name)) } + pub fn prepare_rename( + &self, + position: FilePosition, + ) -> Cancelable, RenameError>> { + self.with_db(|db| references::rename::prepare_rename(db, position)) + } + pub fn structural_search_replace( &self, 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( 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::(&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::(&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::(&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 { @@ -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 + "#]], + ); + } } 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 { impl Error for RenameError {} +pub(crate) fn prepare_rename( + db: &RootDatabase, + position: FilePosition, +) -> Result, 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, 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, position: FilePosition, new_name: &str, + is_lifetime_name: bool, ) -> Result, 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!() + } +} "#, ) } 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 { } return h; } + Definition::LifetimeParam(_) => HighlightTag::Lifetime, } .into() } 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 @@ // 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, + lifetime: &ast::Lifetime, + ) -> Option { + 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, + lifetime: &ast::Lifetime, + ) -> Option { + 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 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 { 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, 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 Vec { - let name = name_ref.text(); +pub fn index_resolve(db: &RootDatabase, name: &SmolStr) -> Vec { let mut query = Query::new(name.to_string()); query.exact(); 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( 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))) -- cgit v1.2.3