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`.
This commit is contained in:
parent
1b4defd240
commit
09fc5e1dd7
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
use crate::FileSymbol;
|
use crate::FileSymbol;
|
||||||
|
|
||||||
/// `NavigationTarget` represents and element in the editor's UI which you can
|
/// `NavigationTarget` represents an element in the editor's UI which you can
|
||||||
/// click on to navigate to a particular piece of code.
|
/// click on to navigate to a particular piece of code.
|
||||||
///
|
///
|
||||||
/// Typically, a `NavigationTarget` corresponds to some element in the source
|
/// Typically, a `NavigationTarget` corresponds to some element in the source
|
||||||
@ -35,12 +35,10 @@ pub struct NavigationTarget {
|
|||||||
/// Clients should use this range to answer "is the cursor inside the
|
/// Clients should use this range to answer "is the cursor inside the
|
||||||
/// element?" question.
|
/// element?" question.
|
||||||
pub full_range: TextRange,
|
pub full_range: TextRange,
|
||||||
/// A "most interesting" range withing the `full_range`.
|
/// A "most interesting" range within the `full_range`.
|
||||||
///
|
///
|
||||||
/// Typically, `full_range` is the whole syntax node, including doc
|
/// Typically, `full_range` is the whole syntax node, including doc
|
||||||
/// comments, and `focus_range` is the range of the identifier. "Most
|
/// comments, and `focus_range` is the range of the identifier.
|
||||||
/// interesting" range within the full range, typically the range of
|
|
||||||
/// identifier.
|
|
||||||
///
|
///
|
||||||
/// Clients should place the cursor on this range when navigating to this target.
|
/// Clients should place the cursor on this range when navigating to this target.
|
||||||
pub focus_range: Option<TextRange>,
|
pub focus_range: Option<TextRange>,
|
||||||
|
@ -50,6 +50,17 @@ pub(crate) fn prepare_rename(
|
|||||||
let sema = Semantics::new(db);
|
let sema = Semantics::new(db);
|
||||||
let source_file = sema.parse(position.file_id);
|
let source_file = sema.parse(position.file_id);
|
||||||
let syntax = source_file.syntax();
|
let syntax = source_file.syntax();
|
||||||
|
|
||||||
|
let def = find_definition(&sema, syntax, position)?;
|
||||||
|
match def {
|
||||||
|
Definition::SelfType(_) => bail!("Cannot rename `Self`"),
|
||||||
|
Definition::ModuleDef(ModuleDef::BuiltinType(_)) => bail!("Cannot rename builtin type"),
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
let nav =
|
||||||
|
def.try_to_nav(sema.db).ok_or_else(|| format_err!("No references found at position"))?;
|
||||||
|
nav.focus_range.ok_or_else(|| format_err!("No identifier available to rename"))?;
|
||||||
|
|
||||||
let name_like = sema
|
let name_like = sema
|
||||||
.find_node_at_offset_with_descend(&syntax, position.offset)
|
.find_node_at_offset_with_descend(&syntax, position.offset)
|
||||||
.ok_or_else(|| format_err!("No references found at position"))?;
|
.ok_or_else(|| format_err!("No references found at position"))?;
|
||||||
@ -507,7 +518,8 @@ fn source_edit_from_def(
|
|||||||
def.try_to_nav(sema.db).ok_or_else(|| format_err!("No references found at position"))?;
|
def.try_to_nav(sema.db).ok_or_else(|| format_err!("No references found at position"))?;
|
||||||
|
|
||||||
let mut replacement_text = String::new();
|
let mut replacement_text = String::new();
|
||||||
let mut repl_range = nav.focus_or_full_range();
|
let mut repl_range =
|
||||||
|
nav.focus_range.ok_or_else(|| format_err!("No identifier available to rename"))?;
|
||||||
if let Definition::Local(local) = def {
|
if let Definition::Local(local) = def {
|
||||||
if let Either::Left(pat) = local.source(sema.db).value {
|
if let Either::Left(pat) = local.source(sema.db).value {
|
||||||
if matches!(
|
if matches!(
|
||||||
@ -625,6 +637,49 @@ fn test_prepare_rename_keyword() {
|
|||||||
check_prepare(r"struct$0 Foo;", expect![[r#"No references found at position"#]]);
|
check_prepare(r"struct$0 Foo;", expect![[r#"No references found at position"#]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_prepare_rename_tuple_field() {
|
||||||
|
check_prepare(
|
||||||
|
r#"
|
||||||
|
struct Foo(i32);
|
||||||
|
|
||||||
|
fn baz() {
|
||||||
|
let mut x = Foo(4);
|
||||||
|
x.0$0 = 5;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"No identifier available to rename"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_prepare_rename_builtin() {
|
||||||
|
check_prepare(
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
let x: i32$0 = 0;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"Cannot rename builtin type"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_prepare_rename_self() {
|
||||||
|
check_prepare(
|
||||||
|
r#"
|
||||||
|
struct Foo {}
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn foo(self) -> Self$0 {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"Cannot rename `Self`"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rename_to_underscore() {
|
fn test_rename_to_underscore() {
|
||||||
check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#);
|
check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#);
|
||||||
@ -1787,4 +1842,50 @@ fn foo() {
|
|||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rename_tuple_field() {
|
||||||
|
check(
|
||||||
|
"foo",
|
||||||
|
r#"
|
||||||
|
struct Foo(i32);
|
||||||
|
|
||||||
|
fn baz() {
|
||||||
|
let mut x = Foo(4);
|
||||||
|
x.0$0 = 5;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
"error: No identifier available to rename",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rename_builtin() {
|
||||||
|
check(
|
||||||
|
"foo",
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
let x: i32$0 = 0;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
"error: Cannot rename builtin type",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_rename_self() {
|
||||||
|
check(
|
||||||
|
"foo",
|
||||||
|
r#"
|
||||||
|
struct Foo {}
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn foo(self) -> Self$0 {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
"error: Cannot rename `Self`",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user