diff options
-rw-r--r-- | crates/hir/src/lib.rs | 21 | ||||
-rw-r--r-- | crates/ide/src/references/rename.rs | 71 |
2 files changed, 48 insertions, 44 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 5da6a0340..bdc1ad852 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -852,6 +852,7 @@ impl Function { | |||
852 | }) | 852 | }) |
853 | .collect() | 853 | .collect() |
854 | } | 854 | } |
855 | |||
855 | pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> { | 856 | pub fn method_params(self, db: &dyn HirDatabase) -> Option<Vec<Param>> { |
856 | if self.self_param(db).is_none() { | 857 | if self.self_param(db).is_none() { |
857 | return None; | 858 | return None; |
@@ -909,7 +910,7 @@ impl From<hir_ty::Mutability> for Access { | |||
909 | } | 910 | } |
910 | } | 911 | } |
911 | 912 | ||
912 | #[derive(Debug)] | 913 | #[derive(Clone, Debug)] |
913 | pub struct Param { | 914 | pub struct Param { |
914 | func: Function, | 915 | func: Function, |
915 | /// The index in parameter list, including self parameter. | 916 | /// The index in parameter list, including self parameter. |
@@ -922,13 +923,25 @@ impl Param { | |||
922 | &self.ty | 923 | &self.ty |
923 | } | 924 | } |
924 | 925 | ||
926 | pub fn as_local(&self, db: &dyn HirDatabase) -> Local { | ||
927 | let parent = DefWithBodyId::FunctionId(self.func.into()); | ||
928 | let body = db.body(parent); | ||
929 | Local { parent, pat_id: body.params[self.idx] } | ||
930 | } | ||
931 | |||
925 | pub fn pattern_source(&self, db: &dyn HirDatabase) -> Option<ast::Pat> { | 932 | pub fn pattern_source(&self, db: &dyn HirDatabase) -> Option<ast::Pat> { |
926 | let params = self.func.source(db)?.value.param_list()?; | 933 | self.source(db).and_then(|p| p.value.pat()) |
934 | } | ||
935 | |||
936 | pub fn source(&self, db: &dyn HirDatabase) -> Option<InFile<ast::Param>> { | ||
937 | let InFile { file_id, value } = self.func.source(db)?; | ||
938 | let params = value.param_list()?; | ||
927 | if params.self_param().is_some() { | 939 | if params.self_param().is_some() { |
928 | params.params().nth(self.idx.checked_sub(1)?)?.pat() | 940 | params.params().nth(self.idx.checked_sub(1)?) |
929 | } else { | 941 | } else { |
930 | params.params().nth(self.idx)?.pat() | 942 | params.params().nth(self.idx) |
931 | } | 943 | } |
944 | .map(|value| InFile { file_id, value }) | ||
932 | } | 945 | } |
933 | } | 946 | } |
934 | 947 | ||
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 5340b638a..b1ca6d50f 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)); |
@@ -275,46 +278,32 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe | |||
275 | 278 | ||
276 | let fn_def = match local.parent(sema.db) { | 279 | let fn_def = match local.parent(sema.db) { |
277 | hir::DefWithBody::Function(func) => func, | 280 | hir::DefWithBody::Function(func) => func, |
278 | _ => bail!("Cannot rename non-param local to self"), | 281 | _ => bail!("Cannot rename local to self outside of function"), |
279 | }; | 282 | }; |
280 | 283 | ||
281 | // FIXME: reimplement this on the hir instead | 284 | 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"); | 285 | bail!("Method already has a self parameter"); |
312 | } | 286 | } |
313 | 287 | ||
314 | let params = fn_def.assoc_fn_params(sema.db); | 288 | let params = fn_def.assoc_fn_params(sema.db); |
315 | let first_param = params.first().ok_or_else(|| format_err!("Method has no parameters"))?; | 289 | let first_param = params |
290 | .first() | ||
291 | .ok_or_else(|| format_err!("Cannot rename local to self unless it is a parameter"))?; | ||
292 | if first_param.as_local(sema.db) != local { | ||
293 | bail!("Only the first parameter may be renamed to self"); | ||
294 | } | ||
295 | |||
296 | let assoc_item = fn_def | ||
297 | .as_assoc_item(sema.db) | ||
298 | .ok_or_else(|| format_err!("Cannot rename parameter to self for free function"))?; | ||
299 | let impl_ = match assoc_item.container(sema.db) { | ||
300 | hir::AssocItemContainer::Trait(_) => { | ||
301 | bail!("Cannot rename parameter to self for trait functions"); | ||
302 | } | ||
303 | hir::AssocItemContainer::Impl(impl_) => impl_, | ||
304 | }; | ||
316 | let first_param_ty = first_param.ty(); | 305 | let first_param_ty = first_param.ty(); |
317 | let impl_ty = impl_block.target_ty(sema.db); | 306 | let impl_ty = impl_.target_ty(sema.db); |
318 | let (ty, self_param) = if impl_ty.remove_ref().is_some() { | 307 | 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 | 308 | // if the impl is a ref to the type we can just match the `&T` with self directly |
320 | (first_param_ty.clone(), "self") | 309 | (first_param_ty.clone(), "self") |
@@ -328,6 +317,9 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe | |||
328 | bail!("Parameter type differs from impl block type"); | 317 | bail!("Parameter type differs from impl block type"); |
329 | } | 318 | } |
330 | 319 | ||
320 | let InFile { file_id, value: param_source } = | ||
321 | first_param.source(sema.db).ok_or_else(|| format_err!("No source for parameter found"))?; | ||
322 | |||
331 | let def = Definition::Local(local); | 323 | let def = Definition::Local(local); |
332 | let usages = def.usages(sema).all(); | 324 | let usages = def.usages(sema).all(); |
333 | let mut source_change = SourceChange::default(); | 325 | let mut source_change = SourceChange::default(); |
@@ -336,9 +328,8 @@ fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameRe | |||
336 | })); | 328 | })); |
337 | source_change.insert_source_edit( | 329 | source_change.insert_source_edit( |
338 | file_id.original_file(sema.db), | 330 | file_id.original_file(sema.db), |
339 | TextEdit::replace(first_param_range, String::from(self_param)), | 331 | TextEdit::replace(param_source.syntax().text_range(), String::from(self_param)), |
340 | ); | 332 | ); |
341 | |||
342 | Ok(source_change) | 333 | Ok(source_change) |
343 | } | 334 | } |
344 | 335 | ||
@@ -1361,7 +1352,7 @@ fn f(foo$0: &mut Foo) -> i32 { | |||
1361 | foo.i | 1352 | foo.i |
1362 | } | 1353 | } |
1363 | "#, | 1354 | "#, |
1364 | "error: No impl block found for function", | 1355 | "error: Cannot rename parameter to self for free function", |
1365 | ); | 1356 | ); |
1366 | check( | 1357 | check( |
1367 | "self", | 1358 | "self", |
@@ -1391,7 +1382,7 @@ impl Foo { | |||
1391 | } | 1382 | } |
1392 | } | 1383 | } |
1393 | "#, | 1384 | "#, |
1394 | "error: Only the first parameter can be self", | 1385 | "error: Only the first parameter may be renamed to self", |
1395 | ); | 1386 | ); |
1396 | } | 1387 | } |
1397 | 1388 | ||