aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/references/rename.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/references/rename.rs')
-rw-r--r--crates/ide/src/references/rename.rs113
1 files changed, 78 insertions, 35 deletions
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index 1919639a3..1e378279d 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -14,14 +14,14 @@ use syntax::{
14 ast::{self, NameOwner}, 14 ast::{self, NameOwner},
15 lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T, 15 lex_single_syntax_kind, AstNode, SyntaxKind, SyntaxNode, T,
16}; 16};
17use test_utils::mark; 17
18use text_edit::TextEdit; 18use text_edit::TextEdit;
19 19
20use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange}; 20use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange};
21 21
22type RenameResult<T> = Result<T, RenameError>; 22type RenameResult<T> = Result<T, RenameError>;
23#[derive(Debug)] 23#[derive(Debug)]
24pub struct RenameError(pub(crate) String); 24pub struct RenameError(String);
25 25
26impl fmt::Display for RenameError { 26impl fmt::Display for RenameError {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -47,16 +47,15 @@ pub(crate) fn prepare_rename(
47 let sema = Semantics::new(db); 47 let sema = Semantics::new(db);
48 let source_file = sema.parse(position.file_id); 48 let source_file = sema.parse(position.file_id);
49 let syntax = source_file.syntax(); 49 let syntax = source_file.syntax();
50 let range = match &sema 50 let name_like = sema
51 .find_node_at_offset_with_descend(&syntax, position.offset) 51 .find_node_at_offset_with_descend(&syntax, position.offset)
52 .ok_or_else(|| format_err!("No references found at position"))? 52 .ok_or_else(|| format_err!("No references found at position"))?;
53 { 53 let node = match &name_like {
54 ast::NameLike::Name(it) => it.syntax(), 54 ast::NameLike::Name(it) => it.syntax(),
55 ast::NameLike::NameRef(it) => it.syntax(), 55 ast::NameLike::NameRef(it) => it.syntax(),
56 ast::NameLike::Lifetime(it) => it.syntax(), 56 ast::NameLike::Lifetime(it) => it.syntax(),
57 } 57 };
58 .text_range(); 58 Ok(RangeInfo::new(sema.original_range(node).range, ()))
59 Ok(RangeInfo::new(range, ()))
60} 59}
61 60
62// Feature: Rename 61// Feature: Rename
@@ -94,6 +93,7 @@ pub(crate) fn rename_with_semantics(
94 } 93 }
95} 94}
96 95
96/// Called by the client when it is about to rename a file.
97pub(crate) fn will_rename_file( 97pub(crate) fn will_rename_file(
98 db: &RootDatabase, 98 db: &RootDatabase,
99 file_id: FileId, 99 file_id: FileId,
@@ -226,34 +226,36 @@ fn rename_reference(
226 | (IdentifierKind::Ident, _) 226 | (IdentifierKind::Ident, _)
227 if def_is_lbl_or_lt => 227 if def_is_lbl_or_lt =>
228 { 228 {
229 mark::hit!(rename_not_a_lifetime_ident_ref); 229 cov_mark::hit!(rename_not_a_lifetime_ident_ref);
230 bail!("Invalid name `{}`: not a lifetime identifier", new_name) 230 bail!("Invalid name `{}`: not a lifetime identifier", new_name)
231 } 231 }
232 (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => mark::hit!(rename_lifetime), 232 (IdentifierKind::Lifetime, _) if def_is_lbl_or_lt => cov_mark::hit!(rename_lifetime),
233 (IdentifierKind::Lifetime, _) => { 233 (IdentifierKind::Lifetime, _) => {
234 mark::hit!(rename_not_an_ident_ref); 234 cov_mark::hit!(rename_not_an_ident_ref);
235 bail!("Invalid name `{}`: not an identifier", new_name) 235 bail!("Invalid name `{}`: not an identifier", new_name)
236 } 236 }
237 (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => { 237 (IdentifierKind::ToSelf, Definition::Local(local)) if local.is_self(sema.db) => {
238 // no-op 238 // no-op
239 mark::hit!(rename_self_to_self); 239 cov_mark::hit!(rename_self_to_self);
240 return Ok(SourceChange::default()); 240 return Ok(SourceChange::default());
241 } 241 }
242 (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => { 242 (ident_kind, Definition::Local(local)) if local.is_self(sema.db) => {
243 mark::hit!(rename_self_to_param); 243 cov_mark::hit!(rename_self_to_param);
244 return rename_self_to_param(sema, local, new_name, ident_kind); 244 return rename_self_to_param(sema, local, new_name, ident_kind);
245 } 245 }
246 (IdentifierKind::ToSelf, Definition::Local(local)) => { 246 (IdentifierKind::ToSelf, Definition::Local(local)) => {
247 mark::hit!(rename_to_self); 247 cov_mark::hit!(rename_to_self);
248 return rename_to_self(sema, local); 248 return rename_to_self(sema, local);
249 } 249 }
250 (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name), 250 (IdentifierKind::ToSelf, _) => bail!("Invalid name `{}`: not an identifier", new_name),
251 (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => mark::hit!(rename_ident), 251 (IdentifierKind::Ident, _) | (IdentifierKind::Underscore, _) => {
252 cov_mark::hit!(rename_ident)
253 }
252 } 254 }
253 255
254 let usages = def.usages(sema).all(); 256 let usages = def.usages(sema).all();
255 if !usages.is_empty() && ident_kind == IdentifierKind::Underscore { 257 if !usages.is_empty() && ident_kind == IdentifierKind::Underscore {
256 mark::hit!(rename_underscore_multiple); 258 cov_mark::hit!(rename_underscore_multiple);
257 bail!("Cannot rename reference to `_` as it is being referenced multiple times"); 259 bail!("Cannot rename reference to `_` as it is being referenced multiple times");
258 } 260 }
259 let mut source_change = SourceChange::default(); 261 let mut source_change = SourceChange::default();
@@ -444,7 +446,7 @@ fn source_edit_from_name_ref(
444 (Some(field_name), Some(init)) => { 446 (Some(field_name), Some(init)) => {
445 if field_name == *name_ref { 447 if field_name == *name_ref {
446 if init.text() == new_name { 448 if init.text() == new_name {
447 mark::hit!(test_rename_field_put_init_shorthand); 449 cov_mark::hit!(test_rename_field_put_init_shorthand);
448 // same names, we can use a shorthand here instead. 450 // same names, we can use a shorthand here instead.
449 // we do not want to erase attributes hence this range start 451 // we do not want to erase attributes hence this range start
450 let s = field_name.syntax().text_range().start(); 452 let s = field_name.syntax().text_range().start();
@@ -453,7 +455,7 @@ fn source_edit_from_name_ref(
453 } 455 }
454 } else if init == *name_ref { 456 } else if init == *name_ref {
455 if field_name.text() == new_name { 457 if field_name.text() == new_name {
456 mark::hit!(test_rename_local_put_init_shorthand); 458 cov_mark::hit!(test_rename_local_put_init_shorthand);
457 // same names, we can use a shorthand here instead. 459 // same names, we can use a shorthand here instead.
458 // we do not want to erase attributes hence this range start 460 // we do not want to erase attributes hence this range start
459 let s = field_name.syntax().text_range().start(); 461 let s = field_name.syntax().text_range().start();
@@ -467,12 +469,12 @@ fn source_edit_from_name_ref(
467 // FIXME: instead of splitting the shorthand, recursively trigger a rename of the 469 // FIXME: instead of splitting the shorthand, recursively trigger a rename of the
468 // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 470 // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
469 (None, Some(_)) if matches!(def, Definition::Field(_)) => { 471 (None, Some(_)) if matches!(def, Definition::Field(_)) => {
470 mark::hit!(test_rename_field_in_field_shorthand); 472 cov_mark::hit!(test_rename_field_in_field_shorthand);
471 let s = name_ref.syntax().text_range().start(); 473 let s = name_ref.syntax().text_range().start();
472 Some((TextRange::empty(s), format!("{}: ", new_name))) 474 Some((TextRange::empty(s), format!("{}: ", new_name)))
473 } 475 }
474 (None, Some(_)) if matches!(def, Definition::Local(_)) => { 476 (None, Some(_)) if matches!(def, Definition::Local(_)) => {
475 mark::hit!(test_rename_local_in_field_shorthand); 477 cov_mark::hit!(test_rename_local_in_field_shorthand);
476 let s = name_ref.syntax().text_range().end(); 478 let s = name_ref.syntax().text_range().end();
477 Some((TextRange::empty(s), format!(": {}", new_name))) 479 Some((TextRange::empty(s), format!(": {}", new_name)))
478 } 480 }
@@ -486,7 +488,7 @@ fn source_edit_from_name_ref(
486 (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => { 488 (Some(field_name), Some(ast::Pat::IdentPat(pat))) if field_name == *name_ref => {
487 // field name is being renamed 489 // field name is being renamed
488 if pat.name().map_or(false, |it| it.text() == new_name) { 490 if pat.name().map_or(false, |it| it.text() == new_name) {
489 mark::hit!(test_rename_field_put_init_shorthand_pat); 491 cov_mark::hit!(test_rename_field_put_init_shorthand_pat);
490 // same names, we can use a shorthand here instead/ 492 // same names, we can use a shorthand here instead/
491 // we do not want to erase attributes hence this range start 493 // we do not want to erase attributes hence this range start
492 let s = field_name.syntax().text_range().start(); 494 let s = field_name.syntax().text_range().start();
@@ -538,11 +540,13 @@ fn source_edit_from_def(
538mod tests { 540mod tests {
539 use expect_test::{expect, Expect}; 541 use expect_test::{expect, Expect};
540 use stdx::trim_indent; 542 use stdx::trim_indent;
541 use test_utils::{assert_eq_text, mark}; 543 use test_utils::assert_eq_text;
542 use text_edit::TextEdit; 544 use text_edit::TextEdit;
543 545
544 use crate::{fixture, FileId}; 546 use crate::{fixture, FileId};
545 547
548 use super::{RangeInfo, RenameError};
549
546 fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) { 550 fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
547 let ra_fixture_after = &trim_indent(ra_fixture_after); 551 let ra_fixture_after = &trim_indent(ra_fixture_after);
548 let (analysis, position) = fixture::position(ra_fixture_before); 552 let (analysis, position) = fixture::position(ra_fixture_before);
@@ -588,6 +592,45 @@ mod tests {
588 expect.assert_debug_eq(&source_change) 592 expect.assert_debug_eq(&source_change)
589 } 593 }
590 594
595 fn check_prepare(ra_fixture: &str, expect: Expect) {
596 let (analysis, position) = fixture::position(ra_fixture);
597 let result = analysis
598 .prepare_rename(position)
599 .unwrap_or_else(|err| panic!("PrepareRename was cancelled: {}", err));
600 match result {
601 Ok(RangeInfo { range, info: () }) => {
602 let source = analysis.file_text(position.file_id).unwrap();
603 expect.assert_eq(&format!("{:?}: {}", range, &source[range]))
604 }
605 Err(RenameError(err)) => expect.assert_eq(&err),
606 };
607 }
608
609 #[test]
610 fn test_prepare_rename_namelikes() {
611 check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]);
612 check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"8..17: 'lifetime"#]]);
613 check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"23..27: name"#]]);
614 }
615
616 #[test]
617 fn test_prepare_rename_in_macro() {
618 check_prepare(
619 r"macro_rules! foo {
620 ($ident:ident) => {
621 pub struct $ident;
622 }
623}
624foo!(Foo$0);",
625 expect![[r#"83..86: Foo"#]],
626 );
627 }
628
629 #[test]
630 fn test_prepare_rename_keyword() {
631 check_prepare(r"struct$0 Foo;", expect![[r#"No references found at position"#]]);
632 }
633
591 #[test] 634 #[test]
592 fn test_rename_to_underscore() { 635 fn test_rename_to_underscore() {
593 check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#); 636 check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#);
@@ -627,7 +670,7 @@ mod tests {
627 670
628 #[test] 671 #[test]
629 fn test_rename_to_invalid_identifier_lifetime() { 672 fn test_rename_to_invalid_identifier_lifetime() {
630 mark::check!(rename_not_an_ident_ref); 673 cov_mark::check!(rename_not_an_ident_ref);
631 check( 674 check(
632 "'foo", 675 "'foo",
633 r#"fn main() { let i$0 = 1; }"#, 676 r#"fn main() { let i$0 = 1; }"#,
@@ -637,7 +680,7 @@ mod tests {
637 680
638 #[test] 681 #[test]
639 fn test_rename_to_invalid_identifier_lifetime2() { 682 fn test_rename_to_invalid_identifier_lifetime2() {
640 mark::check!(rename_not_a_lifetime_ident_ref); 683 cov_mark::check!(rename_not_a_lifetime_ident_ref);
641 check( 684 check(
642 "foo", 685 "foo",
643 r#"fn main<'a>(_: &'a$0 ()) {}"#, 686 r#"fn main<'a>(_: &'a$0 ()) {}"#,
@@ -647,7 +690,7 @@ mod tests {
647 690
648 #[test] 691 #[test]
649 fn test_rename_to_underscore_invalid() { 692 fn test_rename_to_underscore_invalid() {
650 mark::check!(rename_underscore_multiple); 693 cov_mark::check!(rename_underscore_multiple);
651 check( 694 check(
652 "_", 695 "_",
653 r#"fn main(foo$0: ()) {foo;}"#, 696 r#"fn main(foo$0: ()) {foo;}"#,
@@ -666,7 +709,7 @@ mod tests {
666 709
667 #[test] 710 #[test]
668 fn test_rename_for_local() { 711 fn test_rename_for_local() {
669 mark::check!(rename_ident); 712 cov_mark::check!(rename_ident);
670 check( 713 check(
671 "k", 714 "k",
672 r#" 715 r#"
@@ -829,7 +872,7 @@ impl Foo {
829 872
830 #[test] 873 #[test]
831 fn test_rename_field_in_field_shorthand() { 874 fn test_rename_field_in_field_shorthand() {
832 mark::check!(test_rename_field_in_field_shorthand); 875 cov_mark::check!(test_rename_field_in_field_shorthand);
833 check( 876 check(
834 "j", 877 "j",
835 r#" 878 r#"
@@ -855,7 +898,7 @@ impl Foo {
855 898
856 #[test] 899 #[test]
857 fn test_rename_local_in_field_shorthand() { 900 fn test_rename_local_in_field_shorthand() {
858 mark::check!(test_rename_local_in_field_shorthand); 901 cov_mark::check!(test_rename_local_in_field_shorthand);
859 check( 902 check(
860 "j", 903 "j",
861 r#" 904 r#"
@@ -1261,7 +1304,7 @@ fn foo(f: foo::Foo) {
1261 1304
1262 #[test] 1305 #[test]
1263 fn test_parameter_to_self() { 1306 fn test_parameter_to_self() {
1264 mark::check!(rename_to_self); 1307 cov_mark::check!(rename_to_self);
1265 check( 1308 check(
1266 "self", 1309 "self",
1267 r#" 1310 r#"
@@ -1401,7 +1444,7 @@ impl Foo {
1401 1444
1402 #[test] 1445 #[test]
1403 fn test_owned_self_to_parameter() { 1446 fn test_owned_self_to_parameter() {
1404 mark::check!(rename_self_to_param); 1447 cov_mark::check!(rename_self_to_param);
1405 check( 1448 check(
1406 "foo", 1449 "foo",
1407 r#" 1450 r#"
@@ -1454,7 +1497,7 @@ impl Foo {
1454 1497
1455 #[test] 1498 #[test]
1456 fn test_rename_field_put_init_shorthand() { 1499 fn test_rename_field_put_init_shorthand() {
1457 mark::check!(test_rename_field_put_init_shorthand); 1500 cov_mark::check!(test_rename_field_put_init_shorthand);
1458 check( 1501 check(
1459 "bar", 1502 "bar",
1460 r#" 1503 r#"
@@ -1476,7 +1519,7 @@ fn foo(bar: i32) -> Foo {
1476 1519
1477 #[test] 1520 #[test]
1478 fn test_rename_local_put_init_shorthand() { 1521 fn test_rename_local_put_init_shorthand() {
1479 mark::check!(test_rename_local_put_init_shorthand); 1522 cov_mark::check!(test_rename_local_put_init_shorthand);
1480 check( 1523 check(
1481 "i", 1524 "i",
1482 r#" 1525 r#"
@@ -1498,7 +1541,7 @@ fn foo(i: i32) -> Foo {
1498 1541
1499 #[test] 1542 #[test]
1500 fn test_struct_field_pat_into_shorthand() { 1543 fn test_struct_field_pat_into_shorthand() {
1501 mark::check!(test_rename_field_put_init_shorthand_pat); 1544 cov_mark::check!(test_rename_field_put_init_shorthand_pat);
1502 check( 1545 check(
1503 "baz", 1546 "baz",
1504 r#" 1547 r#"
@@ -1610,7 +1653,7 @@ fn foo(foo: Foo) {
1610 1653
1611 #[test] 1654 #[test]
1612 fn test_rename_lifetimes() { 1655 fn test_rename_lifetimes() {
1613 mark::check!(rename_lifetime); 1656 cov_mark::check!(rename_lifetime);
1614 check( 1657 check(
1615 "'yeeee", 1658 "'yeeee",
1616 r#" 1659 r#"
@@ -1698,7 +1741,7 @@ fn foo<'a>() -> &'a () {
1698 1741
1699 #[test] 1742 #[test]
1700 fn test_self_to_self() { 1743 fn test_self_to_self() {
1701 mark::check!(rename_self_to_self); 1744 cov_mark::check!(rename_self_to_self);
1702 check( 1745 check(
1703 "self", 1746 "self",
1704 r#" 1747 r#"