@@ -75,8 +75,7 @@ pub(crate) fn rename_with_semantics(
75
75
let source_file = sema. parse ( position. file_id ) ;
76
76
let syntax = source_file. syntax ( ) ;
77
77
78
- let def = find_definition ( sema, syntax, position)
79
- . ok_or_else ( || format_err ! ( "No references found at position" ) ) ?;
78
+ let def = find_definition ( sema, syntax, position) ?;
80
79
match def {
81
80
Definition :: ModuleDef ( ModuleDef :: Module ( module) ) => rename_mod ( & sema, module, new_name) ,
82
81
def => rename_reference ( sema, def, new_name) ,
@@ -149,18 +148,30 @@ fn find_definition(
149
148
sema : & Semantics < RootDatabase > ,
150
149
syntax : & SyntaxNode ,
151
150
position : FilePosition ,
152
- ) -> Option < Definition > {
153
- let def = match find_name_like ( sema, syntax, position) ? {
154
- NameLike :: Name ( name) => NameClass :: classify ( sema, & name) ?. referenced_or_defined ( sema. db ) ,
155
- NameLike :: NameRef ( name_ref) => NameRefClass :: classify ( sema, & name_ref) ?. referenced ( sema. db ) ,
151
+ ) -> RenameResult < Definition > {
152
+ match find_name_like ( sema, syntax, position)
153
+ . ok_or_else ( || format_err ! ( "No references found at position" ) ) ?
154
+ {
155
+ // renaming aliases would rename the item being aliased as the HIR doesn't track aliases yet
156
+ NameLike :: Name ( name)
157
+ if name. syntax ( ) . parent ( ) . map_or ( false , |it| ast:: Rename :: can_cast ( it. kind ( ) ) ) =>
158
+ {
159
+ bail ! ( "Renaming aliases is currently unsupported" )
160
+ }
161
+ NameLike :: Name ( name) => {
162
+ NameClass :: classify ( sema, & name) . map ( |class| class. referenced_or_defined ( sema. db ) )
163
+ }
164
+ NameLike :: NameRef ( name_ref) => {
165
+ NameRefClass :: classify ( sema, & name_ref) . map ( |class| class. referenced ( sema. db ) )
166
+ }
156
167
NameLike :: Lifetime ( lifetime) => NameRefClass :: classify_lifetime ( sema, & lifetime)
157
168
. map ( |class| NameRefClass :: referenced ( class, sema. db ) )
158
169
. or_else ( || {
159
170
NameClass :: classify_lifetime ( sema, & lifetime)
160
171
. map ( |it| it. referenced_or_defined ( sema. db ) )
161
- } ) ? ,
162
- } ;
163
- Some ( def )
172
+ } ) ,
173
+ }
174
+ . ok_or_else ( || format_err ! ( "No references found at position" ) )
164
175
}
165
176
166
177
fn source_edit_from_references (
@@ -173,21 +184,40 @@ fn source_edit_from_references(
173
184
let mut edit = TextEdit :: builder ( ) ;
174
185
for reference in references {
175
186
let ( range, replacement) = match & reference. name {
176
- NameLike :: Name ( _) => ( None , format ! ( "{}" , new_name) ) ,
177
- NameLike :: NameRef ( name_ref) => source_edit_from_name_ref ( name_ref, new_name, def) ,
178
- NameLike :: Lifetime ( _) => ( None , format ! ( "{}" , new_name) ) ,
179
- } ;
180
- // FIXME: Some(range) will be incorrect when we are inside macros
181
- edit. replace ( range. unwrap_or ( reference. range ) , replacement) ;
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
189
+ // the macro input
190
+ NameLike :: NameRef ( name_ref) if name_ref. syntax ( ) . text_range ( ) == reference. range => {
191
+ source_edit_from_name_ref ( name_ref, new_name, def)
192
+ }
193
+ NameLike :: Name ( name) if name. syntax ( ) . text_range ( ) == reference. range => {
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) ;
182
200
}
183
201
( file_id, edit. finish ( ) )
184
202
}
185
203
204
+ fn 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
+
186
216
fn source_edit_from_name_ref (
187
217
name_ref : & ast:: NameRef ,
188
218
new_name : & str ,
189
219
def : Definition ,
190
- ) -> ( Option < TextRange > , String ) {
220
+ ) -> Option < ( TextRange , String ) > {
191
221
if let Some ( record_field) = ast:: RecordExprField :: for_name_ref ( name_ref) {
192
222
let rcf_name_ref = record_field. name_ref ( ) ;
193
223
let rcf_expr = record_field. expr ( ) ;
@@ -197,45 +227,40 @@ fn source_edit_from_name_ref(
197
227
if field_name == * name_ref {
198
228
if init. text ( ) == new_name {
199
229
mark:: hit!( test_rename_field_put_init_shorthand) ;
200
- // same names, we can use a shorthand here instead
230
+ // same names, we can use a shorthand here instead.
201
231
// we do not want to erase attributes hence this range start
202
232
let s = field_name. syntax ( ) . text_range ( ) . start ( ) ;
203
233
let e = record_field. syntax ( ) . text_range ( ) . end ( ) ;
204
- return ( Some ( TextRange :: new ( s, e) ) , format ! ( "{}" , new_name) ) ;
234
+ return Some ( ( TextRange :: new ( s, e) , new_name. to_owned ( ) ) ) ;
205
235
}
206
236
} else if init == * name_ref {
207
237
if field_name. text ( ) == new_name {
208
238
mark:: hit!( test_rename_local_put_init_shorthand) ;
209
- // same names, we can use a shorthand here instead
239
+ // same names, we can use a shorthand here instead.
210
240
// we do not want to erase attributes hence this range start
211
241
let s = field_name. syntax ( ) . text_range ( ) . start ( ) ;
212
242
let e = record_field. syntax ( ) . text_range ( ) . end ( ) ;
213
- return ( Some ( TextRange :: new ( s, e) ) , format ! ( "{}" , new_name) ) ;
243
+ return Some ( ( TextRange :: new ( s, e) , new_name. to_owned ( ) ) ) ;
214
244
}
215
245
}
246
+ None
216
247
}
217
248
// init shorthand
218
- ( None , Some ( _) ) => {
219
- // FIXME: instead of splitting the shorthand, recursively trigger a rename of the
220
- // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
221
- match def {
222
- Definition :: Field ( _) => {
223
- mark:: hit!( test_rename_field_in_field_shorthand) ;
224
- let s = name_ref. syntax ( ) . text_range ( ) . start ( ) ;
225
- return ( Some ( TextRange :: empty ( s) ) , format ! ( "{}: " , new_name) ) ;
226
- }
227
- Definition :: Local ( _) => {
228
- mark:: hit!( test_rename_local_in_field_shorthand) ;
229
- let s = name_ref. syntax ( ) . text_range ( ) . end ( ) ;
230
- return ( Some ( TextRange :: empty ( s) ) , format ! ( ": {}" , new_name) ) ;
231
- }
232
- _ => { }
233
- }
249
+ // FIXME: instead of splitting the shorthand, recursively trigger a rename of the
250
+ // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
251
+ ( None , Some ( _) ) if matches ! ( def, Definition :: Field ( _) ) => {
252
+ mark:: hit!( test_rename_field_in_field_shorthand) ;
253
+ let s = name_ref. syntax ( ) . text_range ( ) . start ( ) ;
254
+ Some ( ( TextRange :: empty ( s) , format ! ( "{}: " , new_name) ) )
234
255
}
235
- _ => { }
256
+ ( None , Some ( _) ) if matches ! ( def, Definition :: Local ( _) ) => {
257
+ mark:: hit!( test_rename_local_in_field_shorthand) ;
258
+ let s = name_ref. syntax ( ) . text_range ( ) . end ( ) ;
259
+ Some ( ( TextRange :: empty ( s) , format ! ( ": {}" , new_name) ) )
260
+ }
261
+ _ => None ,
236
262
}
237
- }
238
- if let Some ( record_field) = ast:: RecordPatField :: for_field_name_ref ( name_ref) {
263
+ } else if let Some ( record_field) = ast:: RecordPatField :: for_field_name_ref ( name_ref) {
239
264
let rcf_name_ref = record_field. name_ref ( ) ;
240
265
let rcf_pat = record_field. pat ( ) ;
241
266
match ( rcf_name_ref, rcf_pat) {
@@ -244,17 +269,20 @@ fn source_edit_from_name_ref(
244
269
// field name is being renamed
245
270
if pat. name ( ) . map_or ( false , |it| it. text ( ) == new_name) {
246
271
mark:: hit!( test_rename_field_put_init_shorthand_pat) ;
247
- // same names, we can use a shorthand here instead
272
+ // same names, we can use a shorthand here instead/
248
273
// we do not want to erase attributes hence this range start
249
274
let s = field_name. syntax ( ) . text_range ( ) . start ( ) ;
250
275
let e = record_field. syntax ( ) . text_range ( ) . end ( ) ;
251
- return ( Some ( TextRange :: new ( s, e) ) , format ! ( "{}" , new_name) ) ;
276
+ Some ( ( TextRange :: new ( s, e) , pat. to_string ( ) ) )
277
+ } else {
278
+ None
252
279
}
253
280
}
254
- _ => { }
281
+ _ => None ,
255
282
}
283
+ } else {
284
+ None
256
285
}
257
- ( None , format ! ( "{}" , new_name) )
258
286
}
259
287
260
288
fn rename_mod (
@@ -1477,24 +1505,24 @@ fn foo(i: i32) -> Foo {
1477
1505
}
1478
1506
1479
1507
#[ test]
1480
- fn test_struct_field_destructure_into_shorthand ( ) {
1508
+ fn test_struct_field_pat_into_shorthand ( ) {
1481
1509
mark:: check!( test_rename_field_put_init_shorthand_pat) ;
1482
1510
check (
1483
1511
"baz" ,
1484
1512
r#"
1485
1513
struct Foo { i$0: i32 }
1486
1514
1487
1515
fn foo(foo: Foo) {
1488
- let Foo { i: baz } = foo;
1489
- let _ = baz ;
1516
+ let Foo { i: ref baz @ qux } = foo;
1517
+ let _ = qux ;
1490
1518
}
1491
1519
"# ,
1492
1520
r#"
1493
1521
struct Foo { baz: i32 }
1494
1522
1495
1523
fn foo(foo: Foo) {
1496
- let Foo { baz } = foo;
1497
- let _ = baz ;
1524
+ let Foo { ref baz @ qux } = foo;
1525
+ let _ = qux ;
1498
1526
}
1499
1527
"# ,
1500
1528
) ;
@@ -1567,6 +1595,27 @@ fn foo(Foo { i: bar }: foo) -> i32 {
1567
1595
)
1568
1596
}
1569
1597
1598
+ #[ test]
1599
+ fn test_struct_field_complex_ident_pat ( ) {
1600
+ check (
1601
+ "baz" ,
1602
+ r#"
1603
+ struct Foo { i$0: i32 }
1604
+
1605
+ fn foo(foo: Foo) {
1606
+ let Foo { ref i } = foo;
1607
+ }
1608
+ "# ,
1609
+ r#"
1610
+ struct Foo { baz: i32 }
1611
+
1612
+ fn foo(foo: Foo) {
1613
+ let Foo { baz: ref i } = foo;
1614
+ }
1615
+ "# ,
1616
+ ) ;
1617
+ }
1618
+
1570
1619
#[ test]
1571
1620
fn test_rename_lifetimes ( ) {
1572
1621
mark:: check!( rename_lifetime) ;
@@ -1671,6 +1720,40 @@ struct Foo;
1671
1720
impl Foo {
1672
1721
fn foo(self) {}
1673
1722
}
1723
+ "# ,
1724
+ )
1725
+ }
1726
+
1727
+ #[ test]
1728
+ fn test_rename_field_in_pat_in_macro_doesnt_shorthand ( ) {
1729
+ // ideally we would be able to make this emit a short hand, but I doubt this is easily possible
1730
+ check (
1731
+ "baz" ,
1732
+ r#"
1733
+ macro_rules! foo {
1734
+ ($pattern:pat) => {
1735
+ let $pattern = loop {};
1736
+ };
1737
+ }
1738
+ struct Foo {
1739
+ bar$0: u32,
1740
+ }
1741
+ fn foo() {
1742
+ foo!(Foo { bar: baz });
1743
+ }
1744
+ "# ,
1745
+ r#"
1746
+ macro_rules! foo {
1747
+ ($pattern:pat) => {
1748
+ let $pattern = loop {};
1749
+ };
1750
+ }
1751
+ struct Foo {
1752
+ baz: u32,
1753
+ }
1754
+ fn foo() {
1755
+ foo!(Foo { baz: baz });
1756
+ }
1674
1757
"# ,
1675
1758
)
1676
1759
}
0 commit comments