aboutsummaryrefslogtreecommitdiff
path: root/crates/ide
diff options
context:
space:
mode:
authorDanny Zhu <[email protected]>2021-04-25 21:28:38 +0100
committerDanny Zhu <[email protected]>2021-04-25 22:08:56 +0100
commit09fc5e1dd7f8635d111c12e745cf782fe3b58d95 (patch)
treed96e6a788638b64c3203b499f5f97985adab60f6 /crates/ide
parent1b4defd2409d6a6120b40a0730e9822a819e971d (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')
-rw-r--r--crates/ide/src/display/navigation_target.rs8
-rw-r--r--crates/ide/src/references/rename.rs103
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
21use crate::FileSymbol; 21use 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#"
644struct Foo(i32);
645
646fn 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#"
659fn 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#"
671struct Foo {}
672
673impl 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#"
1851struct Foo(i32);
1852
1853fn 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#"
1867fn 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#"
1880struct Foo {}
1881
1882impl Foo {
1883 fn foo(self) -> Self$0 {
1884 self
1885 }
1886}
1887"#,
1888 "error: Cannot rename `Self`",
1889 );
1890 }
1790} 1891}