aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Wirth <[email protected]>2021-02-13 22:35:04 +0000
committerLukas Wirth <[email protected]>2021-02-13 22:47:21 +0000
commit7b64622780bfa33c593ba856bdb6cfc31b220265 (patch)
treee9e7a8493dd3d158721fb25fa51ccb8dfa6b7f38
parent9b045069241f3a610f5ee1ff448e6e8997b5f92f (diff)
Don't rename field record patterns directly
-rw-r--r--crates/ide/src/references/rename.rs107
-rw-r--r--crates/syntax/src/ast/node_ext.rs5
2 files changed, 73 insertions, 39 deletions
diff --git a/crates/ide/src/references/rename.rs b/crates/ide/src/references/rename.rs
index bbd9426b6..08f16b54d 100644
--- a/crates/ide/src/references/rename.rs
+++ b/crates/ide/src/references/rename.rs
@@ -183,25 +183,41 @@ fn source_edit_from_references(
183) -> (FileId, TextEdit) { 183) -> (FileId, TextEdit) {
184 let mut edit = TextEdit::builder(); 184 let mut edit = TextEdit::builder();
185 for reference in references { 185 for reference in references {
186 match reference.name.as_name_ref() { 186 let (range, replacement) = match &reference.name {
187 // if the ranges differ then the node is inside a macro call, we can't really attempt 187 // if the ranges differ then the node is inside a macro call, we can't really attempt
188 // to make special rewrites like shorthand syntax and such, so just rename the node in 188 // to make special rewrites like shorthand syntax and such, so just rename the node in
189 // the macro input 189 // the macro input
190 Some(name_ref) if name_ref.syntax().text_range() == reference.range => { 190 NameLike::NameRef(name_ref) if name_ref.syntax().text_range() == reference.range => {
191 let (range, replacement) = source_edit_from_name_ref(name_ref, new_name, def); 191 source_edit_from_name_ref(name_ref, new_name, def)
192 edit.replace(range, replacement);
193 } 192 }
194 _ => edit.replace(reference.range, new_name.to_owned()), 193 NameLike::Name(name) if name.syntax().text_range() == reference.range => {
195 }; 194 source_edit_from_name(name, new_name)
195 }
196 _ => None,
197 }
198 .unwrap_or_else(|| (reference.range, new_name.to_string()));
199 edit.replace(range, replacement);
196 } 200 }
197 (file_id, edit.finish()) 201 (file_id, edit.finish())
198} 202}
199 203
204fn source_edit_from_name(name: &ast::Name, new_name: &str) -> Option<(TextRange, String)> {
205 if let Some(_) = ast::RecordPatField::for_field_name(name) {
206 if let Some(ident_pat) = name.syntax().parent().and_then(ast::IdentPat::cast) {
207 return Some((
208 TextRange::empty(ident_pat.syntax().text_range().start()),
209 format!("{}: ", new_name),
210 ));
211 }
212 }
213 None
214}
215
200fn source_edit_from_name_ref( 216fn source_edit_from_name_ref(
201 name_ref: &ast::NameRef, 217 name_ref: &ast::NameRef,
202 new_name: &str, 218 new_name: &str,
203 def: Definition, 219 def: Definition,
204) -> (TextRange, String) { 220) -> Option<(TextRange, String)> {
205 if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) { 221 if let Some(record_field) = ast::RecordExprField::for_name_ref(name_ref) {
206 let rcf_name_ref = record_field.name_ref(); 222 let rcf_name_ref = record_field.name_ref();
207 let rcf_expr = record_field.expr(); 223 let rcf_expr = record_field.expr();
@@ -215,7 +231,7 @@ fn source_edit_from_name_ref(
215 // we do not want to erase attributes hence this range start 231 // we do not want to erase attributes hence this range start
216 let s = field_name.syntax().text_range().start(); 232 let s = field_name.syntax().text_range().start();
217 let e = record_field.syntax().text_range().end(); 233 let e = record_field.syntax().text_range().end();
218 return (TextRange::new(s, e), new_name.to_owned()); 234 return Some((TextRange::new(s, e), new_name.to_owned()));
219 } 235 }
220 } else if init == *name_ref { 236 } else if init == *name_ref {
221 if field_name.text() == new_name { 237 if field_name.text() == new_name {
@@ -224,32 +240,27 @@ fn source_edit_from_name_ref(
224 // we do not want to erase attributes hence this range start 240 // we do not want to erase attributes hence this range start
225 let s = field_name.syntax().text_range().start(); 241 let s = field_name.syntax().text_range().start();
226 let e = record_field.syntax().text_range().end(); 242 let e = record_field.syntax().text_range().end();
227 return (TextRange::new(s, e), new_name.to_owned()); 243 return Some((TextRange::new(s, e), new_name.to_owned()));
228 } 244 }
229 } 245 }
246 None
230 } 247 }
231 // init shorthand 248 // init shorthand
232 (None, Some(_)) => { 249 // FIXME: instead of splitting the shorthand, recursively trigger a rename of the
233 // FIXME: instead of splitting the shorthand, recursively trigger a rename of the 250 // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
234 // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547 251 (None, Some(_)) if matches!(def, Definition::Field(_)) => {
235 match def { 252 mark::hit!(test_rename_field_in_field_shorthand);
236 Definition::Field(_) => { 253 let s = name_ref.syntax().text_range().start();
237 mark::hit!(test_rename_field_in_field_shorthand); 254 Some((TextRange::empty(s), format!("{}: ", new_name)))
238 let s = name_ref.syntax().text_range().start(); 255 }
239 return (TextRange::empty(s), format!("{}: ", new_name)); 256 (None, Some(_)) if matches!(def, Definition::Local(_)) => {
240 } 257 mark::hit!(test_rename_local_in_field_shorthand);
241 Definition::Local(_) => { 258 let s = name_ref.syntax().text_range().end();
242 mark::hit!(test_rename_local_in_field_shorthand); 259 Some((TextRange::empty(s), format!(": {}", new_name)))
243 let s = name_ref.syntax().text_range().end();
244 return (TextRange::empty(s), format!(": {}", new_name));
245 }
246 _ => {}
247 }
248 } 260 }
249 _ => {} 261 _ => None,
250 } 262 }
251 } 263 } else if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) {
252 if let Some(record_field) = ast::RecordPatField::for_field_name_ref(name_ref) {
253 let rcf_name_ref = record_field.name_ref(); 264 let rcf_name_ref = record_field.name_ref();
254 let rcf_pat = record_field.pat(); 265 let rcf_pat = record_field.pat();
255 match (rcf_name_ref, rcf_pat) { 266 match (rcf_name_ref, rcf_pat) {
@@ -262,13 +273,16 @@ fn source_edit_from_name_ref(
262 // we do not want to erase attributes hence this range start 273 // we do not want to erase attributes hence this range start
263 let s = field_name.syntax().text_range().start(); 274 let s = field_name.syntax().text_range().start();
264 let e = record_field.syntax().text_range().end(); 275 let e = record_field.syntax().text_range().end();
265 return (TextRange::new(s, e), new_name.to_owned()); 276 Some((TextRange::new(s, e), pat.to_string()))
277 } else {
278 None
266 } 279 }
267 } 280 }
268 _ => {} 281 _ => None,
269 } 282 }
283 } else {
284 None
270 } 285 }
271 (name_ref.syntax().text_range(), new_name.to_owned())
272} 286}
273 287
274fn rename_mod( 288fn rename_mod(
@@ -1491,7 +1505,7 @@ fn foo(i: i32) -> Foo {
1491 } 1505 }
1492 1506
1493 #[test] 1507 #[test]
1494 fn test_struct_field_destructure_into_shorthand() { 1508 fn test_struct_field_pat_into_shorthand() {
1495 mark::check!(test_rename_field_put_init_shorthand_pat); 1509 mark::check!(test_rename_field_put_init_shorthand_pat);
1496 check( 1510 check(
1497 "baz", 1511 "baz",
@@ -1499,16 +1513,16 @@ fn foo(i: i32) -> Foo {
1499struct Foo { i$0: i32 } 1513struct Foo { i$0: i32 }
1500 1514
1501fn foo(foo: Foo) { 1515fn foo(foo: Foo) {
1502 let Foo { i: baz } = foo; 1516 let Foo { i: ref baz @ qux } = foo;
1503 let _ = baz; 1517 let _ = qux;
1504} 1518}
1505"#, 1519"#,
1506 r#" 1520 r#"
1507struct Foo { baz: i32 } 1521struct Foo { baz: i32 }
1508 1522
1509fn foo(foo: Foo) { 1523fn foo(foo: Foo) {
1510 let Foo { baz } = foo; 1524 let Foo { ref baz @ qux } = foo;
1511 let _ = baz; 1525 let _ = qux;
1512} 1526}
1513"#, 1527"#,
1514 ); 1528 );
@@ -1582,6 +1596,27 @@ fn foo(Foo { i: bar }: foo) -> i32 {
1582 } 1596 }
1583 1597
1584 #[test] 1598 #[test]
1599 fn test_struct_field_complex_ident_pat() {
1600 check(
1601 "baz",
1602 r#"
1603struct Foo { i$0: i32 }
1604
1605fn foo(foo: Foo) {
1606 let Foo { ref i } = foo;
1607}
1608"#,
1609 r#"
1610struct Foo { baz: i32 }
1611
1612fn foo(foo: Foo) {
1613 let Foo { baz: ref i } = foo;
1614}
1615"#,
1616 );
1617 }
1618
1619 #[test]
1585 fn test_rename_lifetimes() { 1620 fn test_rename_lifetimes() {
1586 mark::check!(rename_lifetime); 1621 mark::check!(rename_lifetime);
1587 check( 1622 check(
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index b105cb0e0..307e150e9 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -3,12 +3,11 @@
3 3
4use std::fmt; 4use std::fmt;
5 5
6use ast::AttrsOwner;
7use itertools::Itertools; 6use itertools::Itertools;
8use parser::SyntaxKind; 7use parser::SyntaxKind;
9 8
10use crate::{ 9use crate::{
11 ast::{self, support, AstNode, AstToken, NameOwner, SyntaxNode}, 10 ast::{self, support, AstNode, AstToken, AttrsOwner, NameOwner, SyntaxNode},
12 SmolStr, SyntaxElement, SyntaxToken, T, 11 SmolStr, SyntaxElement, SyntaxToken, T,
13}; 12};
14 13
@@ -324,7 +323,7 @@ impl ast::RecordPatField {
324 323
325 pub fn for_field_name(field_name: &ast::Name) -> Option<ast::RecordPatField> { 324 pub fn for_field_name(field_name: &ast::Name) -> Option<ast::RecordPatField> {
326 let candidate = 325 let candidate =
327 field_name.syntax().ancestors().nth(3).and_then(ast::RecordPatField::cast)?; 326 field_name.syntax().ancestors().nth(2).and_then(ast::RecordPatField::cast)?;
328 match candidate.field_name()? { 327 match candidate.field_name()? {
329 NameOrNameRef::Name(name) if name == *field_name => Some(candidate), 328 NameOrNameRef::Name(name) if name == *field_name => Some(candidate),
330 _ => None, 329 _ => None,