aboutsummaryrefslogtreecommitdiff
path: root/crates/ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide')
-rw-r--r--crates/ide/src/references/rename.rs152
1 files changed, 72 insertions, 80 deletions
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 5340b638a..26d6dc9c9 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -1,8 +1,11 @@
1//! FIXME: write short doc here 1//! Renaming functionality
2//!
3//! All reference and file rename requests go through here where the corresponding [`SourceChange`]s
4//! will be calculated.
2use std::fmt::{self, Display}; 5use std::fmt::{self, Display};
3 6
4use either::Either; 7use either::Either;
5use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics}; 8use hir::{AsAssocItem, InFile, Module, ModuleDef, ModuleSource, Semantics};
6use ide_db::{ 9use ide_db::{
7 base_db::{AnchoredPathBuf, FileId}, 10 base_db::{AnchoredPathBuf, FileId},
8 defs::{Definition, NameClass, NameRefClass}, 11 defs::{Definition, NameClass, NameRefClass},
@@ -196,7 +199,7 @@ fn rename_mod(
196 file_id, 199 file_id,
197 TextEdit::replace(name.syntax().text_range(), new_name.to_string()), 200 TextEdit::replace(name.syntax().text_range(), new_name.to_string()),
198 ), 201 ),
199 _ => unreachable!(), 202 _ => never!("Module source node is missing a name"),
200 } 203 }
201 } 204 }
202 let def = Definition::ModuleDef(ModuleDef::Module(module)); 205 let def = Definition::ModuleDef(ModuleDef::Module(module));
@@ -216,40 +219,44 @@ fn rename_reference(
216) -> RenameResult<SourceChange> { 219) -> RenameResult<SourceChange> {
217 let ident_kind = check_identifier(new_name)?; 220 let ident_kind = check_identifier(new_name)?;
218 221
219 let def_is_lbl_or_lt = matches!( 222 if matches!(
220 def, 223 def, // is target a lifetime?
221 Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_) 224 Definition::GenericParam(hir::GenericParam::LifetimeParam(_)) | Definition::Label(_)
222 ); 225 ) {
223 match (ident_kind, def) { 226 match ident_kind {
224 (IdentifierKind::ToSelf, _) 227 IdentifierKind::Ident | IdentifierKind::ToSelf | IdentifierKind::Underscore => {
225 | (IdentifierKind::Underscore, _) 228 cov_mark::hit!(rename_not_a_lifetime_ident_ref);
226 | (IdentifierKind::Ident, _) 229 bail!("Invalid name `{}`: not a lifetime identifier", new_name);
227 if def_is_lbl_or_lt => 230 }
228 { 231 IdentifierKind::Lifetime => cov_mark::hit!(rename_lifetime),
229 cov_mark::hit!(rename_not_a_lifetime_ident_ref);
230 bail!("Invalid name `{}`: not a lifetime identifier", new_name)
231 }
232 (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => cov_mark::hit!(rename_lifetime),
233 (IdentifierKind::Lifetime, _) => {
234 cov_mark::hit!(rename_not_an_ident_ref);
235 bail!("Invalid name `{}`: not an identifier", new_name)
236 }
237 (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => {
238 // no-op
239 cov_mark::hit!(rename_self_to_self);
240 return Ok(SourceChange::default());
241 }
242 (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => {
243 cov_mark::hit!(rename_self_to_param);
244 return rename_self_to_param(sema, local, new_name, ident_kind);
245 }
246 (IdentifierKind::ToSelf, Definition::Local(local)) => {
247 cov_mark::hit!(rename_to_self);
248 return rename_to_self(sema, local);
249 } 232 }
250 (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name), 233 } else {
251 (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => { 234 match (ident_kind, def) {
252 cov_mark::hit!(rename_ident) 235 (IdentifierKind::Lifetime, _) => {
236 cov_mark::hit!(rename_not_an_ident_ref);
237 bail!("Invalid name `{}`: not an identifier", new_name);
238 }
239 (IdentifierKind::ToSelf, Definition::Local(local)) => {
240 if local.is_self(sema.db) {
241 // no-op
242 cov_mark::hit!(rename_self_to_self);
243 return Ok(SourceChange::default());
244 } else {
245 cov_mark::hit!(rename_to_self);
246 return rename_to_self(sema, local);
247 }
248 }
249 (ident_kind, Definition::Local(local)) => {
250 if let Some(self_param) = local.as_self_param(sema.db) {
251 cov_mark::hit!(rename_self_to_param);
252 return rename_self_to_param(sema, local, self_param, new_name, ident_kind);
253 } else {
254 cov_mark::hit!(rename_local);
255 }
256 }
257 (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name),
258 (IdentifierKind::Ident, _) => cov_mark::hit!(rename_non_local),
259 (IdentifierKind::Underscore, _) => (),
253 } 260 }
254 } 261 }
255 262
@@ -275,46 +282,32 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe
275 282
276 let fn_def = match local.parent(sema.db) { 283 let fn_def = match local.parent(sema.db) {
277 hir::DefWithBody::Function(func) => func, 284 hir::DefWithBody::Function(func) => func,
278 _ => bail!("Cannot rename non-param local to self"), 285 _ => bail!("Cannot rename local to self outside of function"),
279 }; 286 };
280 287
281 // FIXME: reimplement this on the hir instead 288 if let Some(_) = fn_def.self_param(sema.db) {
282 // as of the time of this writing params in hir don't keep their names
283 let fn_ast = fn_def
284 .source(sema.db)
285 .ok_or_else(|| format_err!("Cannot rename non-param local to self"))?
286 .value;
287
288 let first_param_range = fn_ast
289 .param_list()
290 .and_then(|p| p.params().next())
291 .ok_or_else(|| format_err!("Method has no parameters"))?
292 .syntax()
293 .text_range();
294 let InFile { file_id, value: local_source } = local.source(sema.db);
295 match local_source {
296 either::Either::Left(pat)
297 if !first_param_range.contains_range(pat.syntax().text_range()) =>
298 {
299 bail!("Only the first parameter can be self");
300 }
301 _ => (),
302 }
303
304 let impl_block = fn_ast
305 .syntax()
306 .ancestors()
307 .find_map(|node| ast::Impl::cast(node))
308 .and_then(|def| sema.to_def(&def))
309 .ok_or_else(|| format_err!("No impl block found for function"))?;
310 if fn_def.self_param(sema.db).is_some() {
311 bail!("Method already has a self parameter"); 289 bail!("Method already has a self parameter");
312 } 290 }
313 291
314 let params = fn_def.assoc_fn_params(sema.db); 292 let params = fn_def.assoc_fn_params(sema.db);
315 let first_param = params.first().ok_or_else(|| format_err!("Method has no parameters"))?; 293 let first_param = params
294 .first()
295 .ok_or_else(|| format_err!("Cannot rename local to self unless it is a parameter"))?;
296 if first_param.as_local(sema.db) != local {
297 bail!("Only the first parameter may be renamed to self");
298 }
299
300 let assoc_item = fn_def
301 .as_assoc_item(sema.db)
302 .ok_or_else(|| format_err!("Cannot rename parameter to self for free function"))?;
303 let impl_ = match assoc_item.container(sema.db) {
304 hir::AssocItemContainer::Trait(_) => {
305 bail!("Cannot rename parameter to self for trait functions");
306 }
307 hir::AssocItemContainer::Impl(impl_) => impl_,
308 };
316 let first_param_ty = first_param.ty(); 309 let first_param_ty = first_param.ty();
317 let impl_ty = impl_block.target_ty(sema.db); 310 let impl_ty = impl_.target_ty(sema.db);
318 let (ty, self_param) = if impl_ty.remove_ref().is_some() { 311 let (ty, self_param) = if impl_ty.remove_ref().is_some() {
319 // if the impl is a ref to the type we can just match the `&T` with self directly 312 // if the impl is a ref to the type we can just match the `&T` with self directly
320 (first_param_ty.clone(), "self") 313 (first_param_ty.clone(), "self")
@@ -328,6 +321,9 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe
328 bail!("Parameter type differs from impl block type"); 321 bail!("Parameter type differs from impl block type");
329 } 322 }
330 323
324 let InFile { file_id, value: param_source } =
325 first_param.source(sema.db).ok_or_else(|| format_err!("No source for parameter found"))?;
326
331 let def = Definition::Local(local); 327 let def = Definition::Local(local);
332 let usages = def.usages(sema).all(); 328 let usages = def.usages(sema).all();
333 let mut source_change = SourceChange::default(); 329 let mut source_change = SourceChange::default();
@@ -336,25 +332,20 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe
336 })); 332 }));
337 source_change.insert_source_edit( 333 source_change.insert_source_edit(
338 file_id.original_file(sema.db), 334 file_id.original_file(sema.db),
339 TextEdit::replace(first_param_range, String::from(self_param)), 335 TextEdit::replace(param_source.syntax().text_range(), String::from(self_param)),
340 ); 336 );
341
342 Ok(source_change) 337 Ok(source_change)
343} 338}
344 339
345fn rename_self_to_param( 340fn rename_self_to_param(
346 sema: &Semantics<RootDatabase>, 341 sema: &Semantics<RootDatabase>,
347 local: hir::Local, 342 local: hir::Local,
343 self_param: hir::SelfParam,
348 new_name: &str, 344 new_name: &str,
349 identifier_kind: IdentifierKind, 345 identifier_kind: IdentifierKind,
350) -> RenameResult<SourceChange> { 346) -> RenameResult<SourceChange> {
351 let (file_id, self_param) = match local.source(sema.db) { 347 let InFile { file_id, value: self_param } =
352 InFile { file_id, value: Either::Right(self_param) } => (file_id, self_param), 348 self_param.source(sema.db).ok_or_else(|| format_err!("cannot find function source"))?;
353 _ => {
354 never!(true, "rename_self_to_param invoked on a non-self local");
355 bail!("rename_self_to_param invoked on a non-self local");
356 }
357 };
358 349
359 let def = Definition::Local(local); 350 let def = Definition::Local(local);
360 let usages = def.usages(sema).all(); 351 let usages = def.usages(sema).all();
@@ -710,7 +701,7 @@ foo!(Foo$0);",
710 701
711 #[test] 702 #[test]
712 fn test_rename_for_local() { 703 fn test_rename_for_local() {
713 cov_mark::check!(rename_ident); 704 cov_mark::check!(rename_local);
714 check( 705 check(
715 "k", 706 "k",
716 r#" 707 r#"
@@ -1251,6 +1242,7 @@ pub mod foo$0;
1251 1242
1252 #[test] 1243 #[test]
1253 fn test_enum_variant_from_module_1() { 1244 fn test_enum_variant_from_module_1() {
1245 cov_mark::check!(rename_non_local);
1254 check( 1246 check(
1255 "Baz", 1247 "Baz",
1256 r#" 1248 r#"
@@ -1361,7 +1353,7 @@ fn f(foo$0: &mut Foo) -> i32 {
1361 foo.i 1353 foo.i
1362} 1354}
1363"#, 1355"#,
1364 "error: No impl block found for function", 1356 "error: Cannot rename parameter to self for free function",
1365 ); 1357 );
1366 check( 1358 check(
1367 "self", 1359 "self",
@@ -1391,7 +1383,7 @@ impl Foo {
1391 } 1383 }
1392} 1384}
1393"#, 1385"#,
1394 "error: Only the first parameter can be self", 1386 "error: Only the first parameter may be renamed to self",
1395 ); 1387 );
1396 } 1388 }
1397 1389