diff options
Diffstat (limited to 'crates/ide/src/references')
-rw-r--r-- | crates/ide/src/references/rename.rs | 113 |
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 | }; |
17 | use test_utils::mark; | 17 | |
18 | use text_edit::TextEdit; | 18 | use text_edit::TextEdit; |
19 | 19 | ||
20 | use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange}; | 20 | use crate::{display::TryToNav, FilePosition, FileSystemEdit, RangeInfo, SourceChange, TextRange}; |
21 | 21 | ||
22 | type RenameResult<T> = Result<T, RenameError>; | 22 | type RenameResult<T> = Result<T, RenameError>; |
23 | #[derive(Debug)] | 23 | #[derive(Debug)] |
24 | pub struct RenameError(pub(crate) String); | 24 | pub struct RenameError(String); |
25 | 25 | ||
26 | impl fmt::Display for RenameError { | 26 | impl 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. | ||
97 | pub(crate) fn will_rename_file( | 97 | pub(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( | |||
538 | mod tests { | 540 | mod 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 | } | ||
624 | foo!(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#" |