Don't rename field record patterns directly

This commit is contained in:
Lukas Wirth 2021-02-13 23:35:04 +01:00
parent 9b04506924
commit 7b64622780
2 changed files with 73 additions and 39 deletions

View file

@ -183,25 +183,41 @@ fn source_edit_from_references(
) -> (FileId, TextEdit) { ) -> (FileId, TextEdit) {
let mut edit = TextEdit::builder(); let mut edit = TextEdit::builder();
for reference in references { for reference in references {
match reference.name.as_name_ref() { let (range, replacement) = match &reference.name {
// if the ranges differ then the node is inside a macro call, we can't really attempt // if the ranges differ then the node is inside a macro call, we can't really attempt
// to make special rewrites like shorthand syntax and such, so just rename the node in // to make special rewrites like shorthand syntax and such, so just rename the node in
// the macro input // the macro input
Some(name_ref) if name_ref.syntax().text_range() == reference.range => { NameLike::NameRef(name_ref) if name_ref.syntax().text_range() == reference.range => {
let (range, replacement) = source_edit_from_name_ref(name_ref, new_name, def); source_edit_from_name_ref(name_ref, new_name, def)
}
NameLike::Name(name) if name.syntax().text_range() == reference.range => {
source_edit_from_name(name, new_name)
}
_ => None,
}
.unwrap_or_else(|| (reference.range, new_name.to_string()));
edit.replace(range, replacement); edit.replace(range, replacement);
} }
_ => edit.replace(reference.range, new_name.to_owned()),
};
}
(file_id, edit.finish()) (file_id, edit.finish())
} }
fn source_edit_from_name(name: &ast::Name, new_name: &str) -> Option<(TextRange, String)> {
if let Some(_) = ast::RecordPatField::for_field_name(name) {
if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
return Some((
TextRange::empty(ident_pat.syntax().text_range().start()),
format!("{}: ", new_name),
));
}
}
None
}
fn source_edit_from_name_ref( fn source_edit_from_name_ref(
name_ref: &ast::NameRef, name_ref: &ast::NameRef,
new_name: &str, new_name: &str,
def: Definition, def: Definition,
) -> (TextRange, String) { ) -> Option<(TextRange, String)> {
if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) { if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) {
let rcf_name_ref = record_field.name_ref(); let rcf_name_ref = record_field.name_ref();
let rcf_expr = record_field.expr(); let rcf_expr = record_field.expr();
@ -215,7 +231,7 @@ fn source_edit_from_name_ref(
// we do not want to erase attributes hence this range start // we do not want to erase attributes hence this range start
let s = field_name.syntax().text_range().start(); let s = field_name.syntax().text_range().start();
let e = record_field.syntax().text_range().end(); let e = record_field.syntax().text_range().end();
return (TextRange::new(s, e), new_name.to_owned()); return Some((TextRange::new(s, e), new_name.to_owned()));
} }
} else if init == *name_ref { } else if init == *name_ref {
if field_name.text() == new_name { if field_name.text() == new_name {
@ -224,32 +240,27 @@ fn source_edit_from_name_ref(
// we do not want to erase attributes hence this range start // we do not want to erase attributes hence this range start
let s = field_name.syntax().text_range().start(); let s = field_name.syntax().text_range().start();
let e = record_field.syntax().text_range().end(); let e = record_field.syntax().text_range().end();
return (TextRange::new(s, e), new_name.to_owned()); return Some((TextRange::new(s, e), new_name.to_owned()));
} }
} }
None
} }
// init shorthand // init shorthand
(None, Some(_)) => {
// FIXME: instead of splitting the shorthand, recursively trigger a rename of the // FIXME: instead of splitting the shorthand, recursively trigger a rename of the
// other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
match def { (None, Some(_)) if matches!(def, Definition::Field(_)) => {
Definition::Field(_) => {
mark::hit!(test_rename_field_in_field_shorthand); mark::hit!(test_rename_field_in_field_shorthand);
let s = name_ref.syntax().text_range().start(); let s = name_ref.syntax().text_range().start();
return (TextRange::empty(s), format!("{}: ", new_name)); Some((TextRange::empty(s), format!("{}: ", new_name)))
} }
Definition::Local(_) => { (None, Some(_)) if matches!(def, Definition::Local(_)) => {
mark::hit!(test_rename_local_in_field_shorthand); mark::hit!(test_rename_local_in_field_shorthand);
let s = name_ref.syntax().text_range().end(); let s = name_ref.syntax().text_range().end();
return (TextRange::empty(s), format!(": {}", new_name)); Some((TextRange::empty(s), format!(": {}", new_name)))
} }
_ => {} _ => None,
} }
} } else if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) {
_ => {}
}
}
if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) {
let rcf_name_ref = record_field.name_ref(); let rcf_name_ref = record_field.name_ref();
let rcf_pat = record_field.pat(); let rcf_pat = record_field.pat();
match (rcf_name_ref, rcf_pat) { match (rcf_name_ref, rcf_pat) {
@ -262,13 +273,16 @@ fn source_edit_from_name_ref(
// we do not want to erase attributes hence this range start // we do not want to erase attributes hence this range start
let s = field_name.syntax().text_range().start(); let s = field_name.syntax().text_range().start();
let e = record_field.syntax().text_range().end(); let e = record_field.syntax().text_range().end();
return (TextRange::new(s, e), new_name.to_owned()); Some((TextRange::new(s, e), pat.to_string()))
} else {
None
} }
} }
_ => {} _ => None,
} }
} else {
None
} }
(name_ref.syntax().text_range(), new_name.to_owned())
} }
fn rename_mod( fn rename_mod(
@ -1491,7 +1505,7 @@ fn foo(i: i32) -> Foo {
} }
#[test] #[test]
fn test_struct_field_destructure_into_shorthand() { fn test_struct_field_pat_into_shorthand() {
mark::check!(test_rename_field_put_init_shorthand_pat); mark::check!(test_rename_field_put_init_shorthand_pat);
check( check(
"baz", "baz",
@ -1499,16 +1513,16 @@ fn foo(i: i32) -> Foo {
struct Foo { i$0: i32 } struct Foo { i$0: i32 }
fn foo(foo: Foo) { fn foo(foo: Foo) {
let Foo { i: baz } = foo; let Foo { i: ref baz @ qux } = foo;
let _ = baz; let _ = qux;
} }
"#, "#,
r#" r#"
struct Foo { baz: i32 } struct Foo { baz: i32 }
fn foo(foo: Foo) { fn foo(foo: Foo) {
let Foo { baz } = foo; let Foo { ref baz @ qux } = foo;
let _ = baz; let _ = qux;
} }
"#, "#,
); );
@ -1581,6 +1595,27 @@ fn foo(Foo { i: bar }: foo) -> i32 {
) )
} }
#[test]
fn test_struct_field_complex_ident_pat() {
check(
"baz",
r#"
struct Foo { i$0: i32 }
fn foo(foo: Foo) {
let Foo { ref i } = foo;
}
"#,
r#"
struct Foo { baz: i32 }
fn foo(foo: Foo) {
let Foo { baz: ref i } = foo;
}
"#,
);
}
#[test] #[test]
fn test_rename_lifetimes() { fn test_rename_lifetimes() {
mark::check!(rename_lifetime); mark::check!(rename_lifetime);

View file

@ -3,12 +3,11 @@
use std::fmt; use std::fmt;
use ast::AttrsOwner;
use itertools::Itertools; use itertools::Itertools;
use parser::SyntaxKind; use parser::SyntaxKind;
use crate::{ use crate::{
ast::{self, support, AstNode, AstToken, NameOwner, SyntaxNode}, ast::{self, support, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode},
SmolStr, SyntaxElement, SyntaxToken, T, SmolStr, SyntaxElement, SyntaxToken, T,
}; };
@ -324,7 +323,7 @@ impl ast::RecordPatField {
pub fn for_field_name(field_name: &ast::Name) -> Option<ast::RecordPatField> { pub fn for_field_name(field_name: &ast::Name) -> Option<ast::RecordPatField> {
let candidate = let candidate =
field_name.syntax().ancestors().nth(3).and_then(ast::RecordPatField::cast)?; field_name.syntax().ancestors().nth(2).and_then(ast::RecordPatField::cast)?;
match candidate.field_name()? { match candidate.field_name()? {
NameOrNameRef::Name(name) if name == *field_name => Some(candidate), NameOrNameRef::Name(name) if name == *field_name => Some(candidate),
_ => None, _ => None,