diff options
author | Danny Zhu <[email protected]> | 2021-04-25 21:28:38 +0100 |
---|---|---|
committer | Danny Zhu <[email protected]> | 2021-04-25 22:08:56 +0100 |
commit | 09fc5e1dd7f8635d111c12e745cf782fe3b58d95 (patch) | |
tree | d96e6a788638b64c3203b499f5f97985adab60f6 /crates/ide/src | |
parent | 1b4defd2409d6a6120b40a0730e9822a819e971d (diff) |
Check more carefully for cases where a rename can't be done
Attempting to rename an element of a tuple field would previously
replace the type with the new name, which doesn't make sense; now it
fails instead.
The check is done in both `prepare_rename` and `rename` so that the case
is caught before the user is prompted for a new name. Some other
existing failure cases are also now additionally checked in
`prepare_rename`.
Diffstat (limited to 'crates/ide/src')
-rw-r--r-- | crates/ide/src/display/navigation_target.rs | 8 | ||||
-rw-r--r-- | crates/ide/src/references/rename.rs | 103 |
2 files changed, 105 insertions, 6 deletions
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs index 364be260c..2079c22a3 100644 --- a/crates/ide/src/display/navigation_target.rs +++ b/crates/ide/src/display/navigation_target.rs | |||
@@ -20,7 +20,7 @@ use syntax::{ | |||
20 | 20 | ||
21 | use crate::FileSymbol; | 21 | use crate::FileSymbol; |
22 | 22 | ||
23 | /// `NavigationTarget` represents and element in the editor's UI which you can | 23 | /// `NavigationTarget` represents an element in the editor's UI which you can |
24 | /// click on to navigate to a particular piece of code. | 24 | /// click on to navigate to a particular piece of code. |
25 | /// | 25 | /// |
26 | /// Typically, a `NavigationTarget` corresponds to some element in the source | 26 | /// Typically, a `NavigationTarget` corresponds to some element in the source |
@@ -35,12 +35,10 @@ pub struct NavigationTarget { | |||
35 | /// Clients should use this range to answer "is the cursor inside the | 35 | /// Clients should use this range to answer "is the cursor inside the |
36 | /// element?" question. | 36 | /// element?" question. |
37 | pub full_range: TextRange, | 37 | pub full_range: TextRange, |
38 | /// A "most interesting" range withing the `full_range`. | 38 | /// A "most interesting" range within the `full_range`. |
39 | /// | 39 | /// |
40 | /// Typically, `full_range` is the whole syntax node, including doc | 40 | /// Typically, `full_range` is the whole syntax node, including doc |
41 | /// comments, and `focus_range` is the range of the identifier. "Most | 41 | /// comments, and `focus_range` is the range of the identifier. |
42 | /// interesting" range within the full range, typically the range of | ||
43 | /// identifier. | ||
44 | /// | 42 | /// |
45 | /// Clients should place the cursor on this range when navigating to this target. | 43 | /// Clients should place the cursor on this range when navigating to this target. |
46 | pub focus_range: Option<TextRange>, | 44 | pub focus_range: Option<TextRange>, |
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs index 2408a0181..175e7a31d 100644 --- a/crates/ide/src/references/rename.rs +++ b/crates/ide/src/references/rename.rs | |||
@@ -50,6 +50,17 @@ pub(crate) fn prepare_rename( | |||
50 | let sema = Semantics::new(db); | 50 | let sema = Semantics::new(db); |
51 | let source_file = sema.parse(position.file_id); | 51 | let source_file = sema.parse(position.file_id); |
52 | let syntax = source_file.syntax(); | 52 | let syntax = source_file.syntax(); |
53 | |||
54 | let def = find_definition(&sema, syntax, position)?; | ||
55 | match def { | ||
56 | Definition::SelfType(_) => bail!("Cannot rename `Self`"), | ||
57 | Definition::ModuleDef(ModuleDef::BuiltinType(_)) => bail!("Cannot rename builtin type"), | ||
58 | _ => {} | ||
59 | }; | ||
60 | let nav = | ||
61 | def.try_to_nav(sema.db).ok_or_else(|| format_err!("No references found at position"))?; | ||
62 | nav.focus_range.ok_or_else(|| format_err!("No identifier available to rename"))?; | ||
63 | |||
53 | let name_like = sema | 64 | let name_like = sema |
54 | .find_node_at_offset_with_descend(&syntax, position.offset) | 65 | .find_node_at_offset_with_descend(&syntax, position.offset) |
55 | .ok_or_else(|| format_err!("No references found at position"))?; | 66 | .ok_or_else(|| format_err!("No references found at position"))?; |
@@ -507,7 +518,8 @@ fn source_edit_from_def( | |||
507 | def.try_to_nav(sema.db).ok_or_else(|| format_err!("No references found at position"))?; | 518 | def.try_to_nav(sema.db).ok_or_else(|| format_err!("No references found at position"))?; |
508 | 519 | ||
509 | let mut replacement_text = String::new(); | 520 | let mut replacement_text = String::new(); |
510 | let mut repl_range = nav.focus_or_full_range(); | 521 | let mut repl_range = |
522 | nav.focus_range.ok_or_else(|| format_err!("No identifier available to rename"))?; | ||
511 | if let Definition::Local(local) = def { | 523 | if let Definition::Local(local) = def { |
512 | if let Either::Left(pat) = local.source(sema.db).value { | 524 | if let Either::Left(pat) = local.source(sema.db).value { |
513 | if matches!( | 525 | if matches!( |
@@ -626,6 +638,49 @@ foo!(Foo$0);", | |||
626 | } | 638 | } |
627 | 639 | ||
628 | #[test] | 640 | #[test] |
641 | fn test_prepare_rename_tuple_field() { | ||
642 | check_prepare( | ||
643 | r#" | ||
644 | struct Foo(i32); | ||
645 | |||
646 | fn baz() { | ||
647 | let mut x = Foo(4); | ||
648 | x.0$0 = 5; | ||
649 | } | ||
650 | "#, | ||
651 | expect![[r#"No identifier available to rename"#]], | ||
652 | ); | ||
653 | } | ||
654 | |||
655 | #[test] | ||
656 | fn test_prepare_rename_builtin() { | ||
657 | check_prepare( | ||
658 | r#" | ||
659 | fn foo() { | ||
660 | let x: i32$0 = 0; | ||
661 | } | ||
662 | "#, | ||
663 | expect![[r#"Cannot rename builtin type"#]], | ||
664 | ); | ||
665 | } | ||
666 | |||
667 | #[test] | ||
668 | fn test_prepare_rename_self() { | ||
669 | check_prepare( | ||
670 | r#" | ||
671 | struct Foo {} | ||
672 | |||
673 | impl Foo { | ||
674 | fn foo(self) -> Self$0 { | ||
675 | self | ||
676 | } | ||
677 | } | ||
678 | "#, | ||
679 | expect![[r#"Cannot rename `Self`"#]], | ||
680 | ); | ||
681 | } | ||
682 | |||
683 | #[test] | ||
629 | fn test_rename_to_underscore() { | 684 | fn test_rename_to_underscore() { |
630 | check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#); | 685 | check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#); |
631 | } | 686 | } |
@@ -1787,4 +1842,50 @@ fn foo() { | |||
1787 | "#, | 1842 | "#, |
1788 | ) | 1843 | ) |
1789 | } | 1844 | } |
1845 | |||
1846 | #[test] | ||
1847 | fn test_rename_tuple_field() { | ||
1848 | check( | ||
1849 | "foo", | ||
1850 | r#" | ||
1851 | struct Foo(i32); | ||
1852 | |||
1853 | fn baz() { | ||
1854 | let mut x = Foo(4); | ||
1855 | x.0$0 = 5; | ||
1856 | } | ||
1857 | "#, | ||
1858 | "error: No identifier available to rename", | ||
1859 | ); | ||
1860 | } | ||
1861 | |||
1862 | #[test] | ||
1863 | fn test_rename_builtin() { | ||
1864 | check( | ||
1865 | "foo", | ||
1866 | r#" | ||
1867 | fn foo() { | ||
1868 | let x: i32$0 = 0; | ||
1869 | } | ||
1870 | "#, | ||
1871 | "error: Cannot rename builtin type", | ||
1872 | ); | ||
1873 | } | ||
1874 | |||
1875 | #[test] | ||
1876 | fn test_rename_self() { | ||
1877 | check( | ||
1878 | "foo", | ||
1879 | r#" | ||
1880 | struct Foo {} | ||
1881 | |||
1882 | impl Foo { | ||
1883 | fn foo(self) -> Self$0 { | ||
1884 | self | ||
1885 | } | ||
1886 | } | ||
1887 | "#, | ||
1888 | "error: Cannot rename `Self`", | ||
1889 | ); | ||
1890 | } | ||
1790 | } | 1891 | } |