diff options
Diffstat (limited to 'crates/ide/src')
-rw-r--r-- | crates/ide/src/references/rename.rs | 152 |
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. | ||
2 | use std::fmt::{self, Display}; | 5 | use std::fmt::{self, Display}; |
3 | 6 | ||
4 | use either::Either; | 7 | use either::Either; |
5 | use hir::{HasSource, InFile, Module, ModuleDef, ModuleSource, Semantics}; | 8 | use hir::{AsAssocItem, InFile, Module, ModuleDef, ModuleSource, Semantics}; |
6 | use ide_db::{ | 9 | use 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 | ||
345 | fn rename_self_to_param( | 340 | fn 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 | ||