diff options
Diffstat (limited to 'crates/ide/src/references')
-rw-r--r-- | crates/ide/src/references/rename.rs | 104 |
1 files changed, 98 insertions, 6 deletions
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 { | |||
35 | 35 | ||
36 | impl Error for RenameError {} | 36 | impl Error for RenameError {} |
37 | 37 | ||
38 | pub(crate) fn prepare_rename( | ||
39 | db: &RootDatabase, | ||
40 | position: FilePosition, | ||
41 | ) -> Result<RangeInfo<()>, RenameError> { | ||
42 | let sema = Semantics::new(db); | ||
43 | let source_file = sema.parse(position.file_id); | ||
44 | let syntax = source_file.syntax(); | ||
45 | if let Some(module) = find_module_at_offset(&sema, position, syntax) { | ||
46 | rename_mod(&sema, position, module, "dummy") | ||
47 | } else if let Some(self_token) = | ||
48 | syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) | ||
49 | { | ||
50 | rename_self_to_param(&sema, position, self_token, "dummy") | ||
51 | } else { | ||
52 | let range = match find_all_refs(&sema, position, None) { | ||
53 | Some(RangeInfo { range, .. }) => range, | ||
54 | None => return Err(RenameError("No references found at position".to_string())), | ||
55 | }; | ||
56 | Ok(RangeInfo::new(range, SourceChange::from(vec![]))) | ||
57 | } | ||
58 | .map(|info| RangeInfo::new(info.range, ())) | ||
59 | } | ||
60 | |||
38 | pub(crate) fn rename( | 61 | pub(crate) fn rename( |
39 | db: &RootDatabase, | 62 | db: &RootDatabase, |
40 | position: FilePosition, | 63 | position: FilePosition, |
@@ -49,11 +72,18 @@ pub(crate) fn rename_with_semantics( | |||
49 | position: FilePosition, | 72 | position: FilePosition, |
50 | new_name: &str, | 73 | new_name: &str, |
51 | ) -> Result<RangeInfo<SourceChange>, RenameError> { | 74 | ) -> Result<RangeInfo<SourceChange>, RenameError> { |
52 | match lex_single_syntax_kind(new_name) { | 75 | let is_lifetime_name = match lex_single_syntax_kind(new_name) { |
53 | Some(res) => match res { | 76 | Some(res) => match res { |
54 | (SyntaxKind::IDENT, _) => (), | 77 | (SyntaxKind::IDENT, _) => false, |
55 | (SyntaxKind::UNDERSCORE, _) => (), | 78 | (SyntaxKind::UNDERSCORE, _) => false, |
56 | (SyntaxKind::SELF_KW, _) => return rename_to_self(&sema, position), | 79 | (SyntaxKind::SELF_KW, _) => return rename_to_self(&sema, position), |
80 | (SyntaxKind::LIFETIME_IDENT, _) if new_name != "'static" && new_name != "'_" => true, | ||
81 | (SyntaxKind::LIFETIME_IDENT, _) => { | ||
82 | return Err(RenameError(format!( | ||
83 | "Invalid name `{0}`: Cannot rename lifetime to {0}", | ||
84 | new_name | ||
85 | ))) | ||
86 | } | ||
57 | (_, Some(syntax_error)) => { | 87 | (_, Some(syntax_error)) => { |
58 | return Err(RenameError(format!("Invalid name `{}`: {}", new_name, syntax_error))) | 88 | return Err(RenameError(format!("Invalid name `{}`: {}", new_name, syntax_error))) |
59 | } | 89 | } |
@@ -62,18 +92,21 @@ pub(crate) fn rename_with_semantics( | |||
62 | } | 92 | } |
63 | }, | 93 | }, |
64 | None => return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))), | 94 | None => return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))), |
65 | } | 95 | }; |
66 | 96 | ||
67 | let source_file = sema.parse(position.file_id); | 97 | let source_file = sema.parse(position.file_id); |
68 | let syntax = source_file.syntax(); | 98 | let syntax = source_file.syntax(); |
69 | if let Some(module) = find_module_at_offset(&sema, position, syntax) { | 99 | // this is here to prevent lifetime renames from happening on modules and self |
100 | if is_lifetime_name { | ||
101 | rename_reference(&sema, position, new_name, is_lifetime_name) | ||
102 | } else if let Some(module) = find_module_at_offset(&sema, position, syntax) { | ||
70 | rename_mod(&sema, position, module, new_name) | 103 | rename_mod(&sema, position, module, new_name) |
71 | } else if let Some(self_token) = | 104 | } else if let Some(self_token) = |
72 | syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) | 105 | syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) |
73 | { | 106 | { |
74 | rename_self_to_param(&sema, position, self_token, new_name) | 107 | rename_self_to_param(&sema, position, self_token, new_name) |
75 | } else { | 108 | } else { |
76 | rename_reference(&sema, position, new_name) | 109 | rename_reference(&sema, position, new_name, is_lifetime_name) |
77 | } | 110 | } |
78 | } | 111 | } |
79 | 112 | ||
@@ -355,12 +388,26 @@ fn rename_reference( | |||
355 | sema: &Semantics<RootDatabase>, | 388 | sema: &Semantics<RootDatabase>, |
356 | position: FilePosition, | 389 | position: FilePosition, |
357 | new_name: &str, | 390 | new_name: &str, |
391 | is_lifetime_name: bool, | ||
358 | ) -> Result<RangeInfo<SourceChange>, RenameError> { | 392 | ) -> Result<RangeInfo<SourceChange>, RenameError> { |
359 | let RangeInfo { range, info: refs } = match find_all_refs(sema, position, None) { | 393 | let RangeInfo { range, info: refs } = match find_all_refs(sema, position, None) { |
360 | Some(range_info) => range_info, | 394 | Some(range_info) => range_info, |
361 | None => return Err(RenameError("No references found at position".to_string())), | 395 | None => return Err(RenameError("No references found at position".to_string())), |
362 | }; | 396 | }; |
363 | 397 | ||
398 | match (refs.declaration.kind == ReferenceKind::Lifetime, is_lifetime_name) { | ||
399 | (true, false) => { | ||
400 | return Err(RenameError(format!( | ||
401 | "Invalid name `{}`: not a lifetime identifier", | ||
402 | new_name | ||
403 | ))) | ||
404 | } | ||
405 | (false, true) => { | ||
406 | return Err(RenameError(format!("Invalid name `{}`: not an identifier", new_name))) | ||
407 | } | ||
408 | _ => (), | ||
409 | } | ||
410 | |||
364 | let edit = refs | 411 | let edit = refs |
365 | .into_iter() | 412 | .into_iter() |
366 | .map(|reference| source_edit_from_reference(sema, reference, new_name)) | 413 | .map(|reference| source_edit_from_reference(sema, reference, new_name)) |
@@ -465,6 +512,24 @@ mod tests { | |||
465 | } | 512 | } |
466 | 513 | ||
467 | #[test] | 514 | #[test] |
515 | fn test_rename_to_invalid_identifier_lifetime() { | ||
516 | check( | ||
517 | "'foo", | ||
518 | r#"fn main() { let i<|> = 1; }"#, | ||
519 | "error: Invalid name `'foo`: not an identifier", | ||
520 | ); | ||
521 | } | ||
522 | |||
523 | #[test] | ||
524 | fn test_rename_to_invalid_identifier_lifetime2() { | ||
525 | check( | ||
526 | "foo", | ||
527 | r#"fn main<'a>(_: &'a<|> ()) {}"#, | ||
528 | "error: Invalid name `foo`: not a lifetime identifier", | ||
529 | ); | ||
530 | } | ||
531 | |||
532 | #[test] | ||
468 | fn test_rename_for_local() { | 533 | fn test_rename_for_local() { |
469 | check( | 534 | check( |
470 | "k", | 535 | "k", |
@@ -1396,4 +1461,31 @@ fn foo(Foo { i: bar }: foo) -> i32 { | |||
1396 | "#, | 1461 | "#, |
1397 | ) | 1462 | ) |
1398 | } | 1463 | } |
1464 | |||
1465 | #[test] | ||
1466 | fn test_rename_lifetimes() { | ||
1467 | check( | ||
1468 | "'yeeee", | ||
1469 | r#" | ||
1470 | trait Foo<'a> { | ||
1471 | fn foo() -> &'a (); | ||
1472 | } | ||
1473 | impl<'a> Foo<'a> for &'a () { | ||
1474 | fn foo() -> &'a<|> () { | ||
1475 | unimplemented!() | ||
1476 | } | ||
1477 | } | ||
1478 | "#, | ||
1479 | r#" | ||
1480 | trait Foo<'a> { | ||
1481 | fn foo() -> &'a (); | ||
1482 | } | ||
1483 | impl<'yeeee> Foo<'yeeee> for &'yeeee () { | ||
1484 | fn foo() -> &'yeeee () { | ||
1485 | unimplemented!() | ||
1486 | } | ||
1487 | } | ||
1488 | "#, | ||
1489 | ) | ||
1490 | } | ||
1399 | } | 1491 | } |