aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/references
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/references')
-rw-r--r--crates/ra_ide/src/references/rename.rs1210
1 files changed, 614 insertions, 596 deletions
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 28c6349b1..8735ec53c 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -1,11 +1,14 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{ModuleSource, Semantics}; 3use hir::{Module, ModuleDef, ModuleSource, Semantics};
4use ra_db::{RelativePath, RelativePathBuf, SourceDatabaseExt}; 4use ra_db::SourceDatabaseExt;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::{
6 defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass},
7 RootDatabase,
8};
6use ra_syntax::{ 9use ra_syntax::{
7 algo::find_node_at_offset, ast, ast::TypeAscriptionOwner, lex_single_valid_syntax_kind, 10 algo::find_node_at_offset, ast, ast::NameOwner, ast::TypeAscriptionOwner,
8 AstNode, SyntaxKind, SyntaxNode, SyntaxToken, 11 lex_single_valid_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken,
9}; 12};
10use ra_text_edit::TextEdit; 13use ra_text_edit::TextEdit;
11use std::convert::TryInto; 14use std::convert::TryInto;
@@ -21,35 +24,53 @@ pub(crate) fn rename(
21 position: FilePosition, 24 position: FilePosition,
22 new_name: &str, 25 new_name: &str,
23) -> Option<RangeInfo<SourceChange>> { 26) -> Option<RangeInfo<SourceChange>> {
27 let sema = Semantics::new(db);
28
24 match lex_single_valid_syntax_kind(new_name)? { 29 match lex_single_valid_syntax_kind(new_name)? {
25 SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (), 30 SyntaxKind::IDENT | SyntaxKind::UNDERSCORE => (),
26 SyntaxKind::SELF_KW => return rename_to_self(db, position), 31 SyntaxKind::SELF_KW => return rename_to_self(&sema, position),
27 _ => return None, 32 _ => return None,
28 } 33 }
29 34
30 let sema = Semantics::new(db);
31 let source_file = sema.parse(position.file_id); 35 let source_file = sema.parse(position.file_id);
32 let syntax = source_file.syntax(); 36 let syntax = source_file.syntax();
33 if let Some((ast_name, ast_module)) = find_name_and_module_at_offset(syntax, position) { 37 if let Some(module) = find_module_at_offset(&sema, position, syntax) {
34 let range = ast_name.syntax().text_range(); 38 rename_mod(&sema, position, module, new_name)
35 rename_mod(&sema, &ast_name, &ast_module, position, new_name)
36 .map(|info| RangeInfo::new(range, info))
37 } else if let Some(self_token) = 39 } else if let Some(self_token) =
38 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW) 40 syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::SELF_KW)
39 { 41 {
40 rename_self_to_param(db, position, self_token, new_name) 42 rename_self_to_param(&sema, position, self_token, new_name)
41 } else { 43 } else {
42 rename_reference(sema.db, position, new_name) 44 rename_reference(&sema, position, new_name)
43 } 45 }
44} 46}
45 47
46fn find_name_and_module_at_offset( 48fn find_module_at_offset(
47 syntax: &SyntaxNode, 49 sema: &Semantics<RootDatabase>,
48 position: FilePosition, 50 position: FilePosition,
49) -> Option<(ast::Name, ast::Module)> { 51 syntax: &SyntaxNode,
50 let ast_name = find_node_at_offset::<ast::Name>(syntax, position.offset)?; 52) -> Option<Module> {
51 let ast_module = ast::Module::cast(ast_name.syntax().parent()?)?; 53 let ident = syntax.token_at_offset(position.offset).find(|t| t.kind() == SyntaxKind::IDENT)?;
52 Some((ast_name, ast_module)) 54
55 let module = match_ast! {
56 match (ident.parent()) {
57 ast::NameRef(name_ref) => {
58 match classify_name_ref(sema, &name_ref)? {
59 NameRefClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module,
60 _ => return None,
61 }
62 },
63 ast::Name(name) => {
64 match classify_name(&sema, &name)? {
65 NameClass::Definition(Definition::ModuleDef(ModuleDef::Module(module))) => module,
66 _ => return None,
67 }
68 },
69 _ => return None,
70 }
71 };
72
73 Some(module)
53} 74}
54 75
55fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFileEdit { 76fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFileEdit {
@@ -78,61 +99,53 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil
78 99
79fn rename_mod( 100fn rename_mod(
80 sema: &Semantics<RootDatabase>, 101 sema: &Semantics<RootDatabase>,
81 ast_name: &ast::Name,
82 ast_module: &ast::Module,
83 position: FilePosition, 102 position: FilePosition,
103 module: Module,
84 new_name: &str, 104 new_name: &str,
85) -> Option<SourceChange> { 105) -> Option<RangeInfo<SourceChange>> {
86 let mut source_file_edits = Vec::new(); 106 let mut source_file_edits = Vec::new();
87 let mut file_system_edits = Vec::new(); 107 let mut file_system_edits = Vec::new();
88 if let Some(module) = sema.to_def(ast_module) { 108
89 let src = module.definition_source(sema.db); 109 let src = module.definition_source(sema.db);
90 let file_id = src.file_id.original_file(sema.db); 110 let file_id = src.file_id.original_file(sema.db);
91 match src.value { 111 match src.value {
92 ModuleSource::SourceFile(..) => { 112 ModuleSource::SourceFile(..) => {
93 let mod_path: RelativePathBuf = sema.db.file_relative_path(file_id); 113 // mod is defined in path/to/dir/mod.rs
94 // mod is defined in path/to/dir/mod.rs 114 let dst = if module.is_mod_rs(sema.db) {
95 let dst_path = if mod_path.file_stem() == Some("mod") { 115 format!("../{}/mod.rs", new_name)
96 mod_path 116 } else {
97 .parent() 117 format!("{}.rs", new_name)
98 .and_then(|p| p.parent()) 118 };
99 .or_else(|| Some(RelativePath::new(""))) 119 let move_file = FileSystemEdit::MoveFile { src: file_id, anchor: file_id, dst };
100 .map(|p| p.join(new_name).join("mod.rs")) 120 file_system_edits.push(move_file);
101 } else {
102 Some(mod_path.with_file_name(new_name).with_extension("rs"))
103 };
104 if let Some(path) = dst_path {
105 let move_file = FileSystemEdit::MoveFile {
106 src: file_id,
107 dst_source_root: sema.db.file_source_root(position.file_id),
108 dst_path: path,
109 };
110 file_system_edits.push(move_file);
111 }
112 }
113 ModuleSource::Module(..) => {}
114 } 121 }
122 ModuleSource::Module(..) => {}
115 } 123 }
116 124
117 let edit = SourceFileEdit { 125 if let Some(src) = module.declaration_source(sema.db) {
118 file_id: position.file_id, 126 let file_id = src.file_id.original_file(sema.db);
119 edit: TextEdit::replace(ast_name.syntax().text_range(), new_name.into()), 127 let name = src.value.name()?;
120 }; 128 let edit = SourceFileEdit {
121 source_file_edits.push(edit); 129 file_id,
122 130 edit: TextEdit::replace(name.syntax().text_range(), new_name.into()),
123 if let Some(RangeInfo { range: _, info: refs }) = find_all_refs(sema.db, position, None) { 131 };
124 let ref_edits = refs 132 source_file_edits.push(edit);
125 .references
126 .into_iter()
127 .map(|reference| source_edit_from_reference(reference, new_name));
128 source_file_edits.extend(ref_edits);
129 } 133 }
130 134
131 Some(SourceChange::from_edits(source_file_edits, file_system_edits)) 135 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
136 let ref_edits = refs
137 .references
138 .into_iter()
139 .map(|reference| source_edit_from_reference(reference, new_name));
140 source_file_edits.extend(ref_edits);
141
142 Some(RangeInfo::new(range, SourceChange::from_edits(source_file_edits, file_system_edits)))
132} 143}
133 144
134fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<SourceChange>> { 145fn rename_to_self(
135 let sema = Semantics::new(db); 146 sema: &Semantics<RootDatabase>,
147 position: FilePosition,
148) -> Option<RangeInfo<SourceChange>> {
136 let source_file = sema.parse(position.file_id); 149 let source_file = sema.parse(position.file_id);
137 let syn = source_file.syntax(); 150 let syn = source_file.syntax();
138 151
@@ -147,7 +160,7 @@ fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo
147 _ => return None, // not renaming other types 160 _ => return None, // not renaming other types
148 }; 161 };
149 162
150 let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; 163 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
151 164
152 let param_range = first_param.syntax().text_range(); 165 let param_range = first_param.syntax().text_range();
153 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs 166 let (param_ref, usages): (Vec<Reference>, Vec<Reference>) = refs
@@ -171,7 +184,7 @@ fn rename_to_self(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo
171 ), 184 ),
172 }); 185 });
173 186
174 Some(RangeInfo::new(range, SourceChange::source_file_edits(edits))) 187 Some(RangeInfo::new(range, SourceChange::from(edits)))
175} 188}
176 189
177fn text_edit_from_self_param( 190fn text_edit_from_self_param(
@@ -199,16 +212,15 @@ fn text_edit_from_self_param(
199} 212}
200 213
201fn rename_self_to_param( 214fn rename_self_to_param(
202 db: &RootDatabase, 215 sema: &Semantics<RootDatabase>,
203 position: FilePosition, 216 position: FilePosition,
204 self_token: SyntaxToken, 217 self_token: SyntaxToken,
205 new_name: &str, 218 new_name: &str,
206) -> Option<RangeInfo<SourceChange>> { 219) -> Option<RangeInfo<SourceChange>> {
207 let sema = Semantics::new(db);
208 let source_file = sema.parse(position.file_id); 220 let source_file = sema.parse(position.file_id);
209 let syn = source_file.syntax(); 221 let syn = source_file.syntax();
210 222
211 let text = db.file_text(position.file_id); 223 let text = sema.db.file_text(position.file_id);
212 let fn_def = find_node_at_offset::<ast::FnDef>(syn, position.offset)?; 224 let fn_def = find_node_at_offset::<ast::FnDef>(syn, position.offset)?;
213 let search_range = fn_def.syntax().text_range(); 225 let search_range = fn_def.syntax().text_range();
214 226
@@ -234,15 +246,15 @@ fn rename_self_to_param(
234 let range = ast::SelfParam::cast(self_token.parent()) 246 let range = ast::SelfParam::cast(self_token.parent())
235 .map_or(self_token.text_range(), |p| p.syntax().text_range()); 247 .map_or(self_token.text_range(), |p| p.syntax().text_range());
236 248
237 Some(RangeInfo::new(range, SourceChange::source_file_edits(edits))) 249 Some(RangeInfo::new(range, SourceChange::from(edits)))
238} 250}
239 251
240fn rename_reference( 252fn rename_reference(
241 db: &RootDatabase, 253 sema: &Semantics<RootDatabase>,
242 position: FilePosition, 254 position: FilePosition,
243 new_name: &str, 255 new_name: &str,
244) -> Option<RangeInfo<SourceChange>> { 256) -> Option<RangeInfo<SourceChange>> {
245 let RangeInfo { range, info: refs } = find_all_refs(db, position, None)?; 257 let RangeInfo { range, info: refs } = find_all_refs(sema, position, None)?;
246 258
247 let edit = refs 259 let edit = refs
248 .into_iter() 260 .into_iter()
@@ -253,57 +265,56 @@ fn rename_reference(
253 return None; 265 return None;
254 } 266 }
255 267
256 Some(RangeInfo::new(range, SourceChange::source_file_edits(edit))) 268 Some(RangeInfo::new(range, SourceChange::from(edit)))
257} 269}
258 270
259#[cfg(test)] 271#[cfg(test)]
260mod tests { 272mod tests {
261 use insta::assert_debug_snapshot; 273 use expect::{expect, Expect};
262 use ra_text_edit::TextEditBuilder; 274 use ra_text_edit::TextEditBuilder;
275 use stdx::trim_indent;
263 use test_utils::{assert_eq_text, mark}; 276 use test_utils::{assert_eq_text, mark};
264 277
265 use crate::{ 278 use crate::{mock_analysis::analysis_and_position, FileId};
266 mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId, 279
267 }; 280 fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
281 let ra_fixture_after = &trim_indent(ra_fixture_after);
282 let (analysis, position) = analysis_and_position(ra_fixture_before);
283 let source_change = analysis.rename(position, new_name).unwrap();
284 let mut text_edit_builder = TextEditBuilder::default();
285 let mut file_id: Option<FileId> = None;
286 if let Some(change) = source_change {
287 for edit in change.info.source_file_edits {
288 file_id = Some(edit.file_id);
289 for indel in edit.edit.into_iter() {
290 text_edit_builder.replace(indel.delete, indel.insert);
291 }
292 }
293 }
294 let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string();
295 text_edit_builder.finish().apply(&mut result);
296 assert_eq_text!(ra_fixture_after, &*result);
297 }
298
299 fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) {
300 let (analysis, position) = analysis_and_position(ra_fixture);
301 let source_change = analysis.rename(position, new_name).unwrap().unwrap();
302 expect.assert_debug_eq(&source_change)
303 }
268 304
269 #[test] 305 #[test]
270 fn test_rename_to_underscore() { 306 fn test_rename_to_underscore() {
271 test_rename( 307 check("_", r#"fn main() { let i<|> = 1; }"#, r#"fn main() { let _ = 1; }"#);
272 r#"
273 fn main() {
274 let i<|> = 1;
275 }"#,
276 "_",
277 r#"
278 fn main() {
279 let _ = 1;
280 }"#,
281 );
282 } 308 }
283 309
284 #[test] 310 #[test]
285 fn test_rename_to_raw_identifier() { 311 fn test_rename_to_raw_identifier() {
286 test_rename( 312 check("r#fn", r#"fn main() { let i<|> = 1; }"#, r#"fn main() { let r#fn = 1; }"#);
287 r#"
288 fn main() {
289 let i<|> = 1;
290 }"#,
291 "r#fn",
292 r#"
293 fn main() {
294 let r#fn = 1;
295 }"#,
296 );
297 } 313 }
298 314
299 #[test] 315 #[test]
300 fn test_rename_to_invalid_identifier() { 316 fn test_rename_to_invalid_identifier() {
301 let (analysis, position) = single_file_with_position( 317 let (analysis, position) = analysis_and_position(r#"fn main() { let i<|> = 1; }"#);
302 "
303 fn main() {
304 let i<|> = 1;
305 }",
306 );
307 let new_name = "invalid!"; 318 let new_name = "invalid!";
308 let source_change = analysis.rename(position, new_name).unwrap(); 319 let source_change = analysis.rename(position, new_name).unwrap();
309 assert!(source_change.is_none()); 320 assert!(source_change.is_none());
@@ -311,682 +322,689 @@ mod tests {
311 322
312 #[test] 323 #[test]
313 fn test_rename_for_local() { 324 fn test_rename_for_local() {
314 test_rename( 325 check(
326 "k",
315 r#" 327 r#"
316 fn main() { 328fn main() {
317 let mut i = 1; 329 let mut i = 1;
318 let j = 1; 330 let j = 1;
319 i = i<|> + j; 331 i = i<|> + j;
320 332
321 { 333 { i = 0; }
322 i = 0;
323 }
324 334
325 i = 5; 335 i = 5;
326 }"#, 336}
327 "k", 337"#,
328 r#" 338 r#"
329 fn main() { 339fn main() {
330 let mut k = 1; 340 let mut k = 1;
331 let j = 1; 341 let j = 1;
332 k = k + j; 342 k = k + j;
333 343
334 { 344 { k = 0; }
335 k = 0;
336 }
337 345
338 k = 5; 346 k = 5;
339 }"#, 347}
348"#,
340 ); 349 );
341 } 350 }
342 351
343 #[test] 352 #[test]
344 fn test_rename_for_macro_args() { 353 fn test_rename_for_macro_args() {
345 test_rename( 354 check(
346 r#"
347 macro_rules! foo {($i:ident) => {$i} }
348 fn main() {
349 let a<|> = "test";
350 foo!(a);
351 }"#,
352 "b", 355 "b",
353 r#" 356 r#"
354 macro_rules! foo {($i:ident) => {$i} } 357macro_rules! foo {($i:ident) => {$i} }
355 fn main() { 358fn main() {
356 let b = "test"; 359 let a<|> = "test";
357 foo!(b); 360 foo!(a);
358 }"#, 361}
362"#,
363 r#"
364macro_rules! foo {($i:ident) => {$i} }
365fn main() {
366 let b = "test";
367 foo!(b);
368}
369"#,
359 ); 370 );
360 } 371 }
361 372
362 #[test] 373 #[test]
363 fn test_rename_for_macro_args_rev() { 374 fn test_rename_for_macro_args_rev() {
364 test_rename( 375 check(
365 r#"
366 macro_rules! foo {($i:ident) => {$i} }
367 fn main() {
368 let a = "test";
369 foo!(a<|>);
370 }"#,
371 "b", 376 "b",
372 r#" 377 r#"
373 macro_rules! foo {($i:ident) => {$i} } 378macro_rules! foo {($i:ident) => {$i} }
374 fn main() { 379fn main() {
375 let b = "test"; 380 let a = "test";
376 foo!(b); 381 foo!(a<|>);
377 }"#, 382}
383"#,
384 r#"
385macro_rules! foo {($i:ident) => {$i} }
386fn main() {
387 let b = "test";
388 foo!(b);
389}
390"#,
378 ); 391 );
379 } 392 }
380 393
381 #[test] 394 #[test]
382 fn test_rename_for_macro_define_fn() { 395 fn test_rename_for_macro_define_fn() {
383 test_rename( 396 check(
384 r#"
385 macro_rules! define_fn {($id:ident) => { fn $id{} }}
386 define_fn!(foo);
387 fn main() {
388 fo<|>o();
389 }"#,
390 "bar", 397 "bar",
391 r#" 398 r#"
392 macro_rules! define_fn {($id:ident) => { fn $id{} }} 399macro_rules! define_fn {($id:ident) => { fn $id{} }}
393 define_fn!(bar); 400define_fn!(foo);
394 fn main() { 401fn main() {
395 bar(); 402 fo<|>o();
396 }"#, 403}
404"#,
405 r#"
406macro_rules! define_fn {($id:ident) => { fn $id{} }}
407define_fn!(bar);
408fn main() {
409 bar();
410}
411"#,
397 ); 412 );
398 } 413 }
399 414
400 #[test] 415 #[test]
401 fn test_rename_for_macro_define_fn_rev() { 416 fn test_rename_for_macro_define_fn_rev() {
402 test_rename( 417 check(
403 r#"
404 macro_rules! define_fn {($id:ident) => { fn $id{} }}
405 define_fn!(fo<|>o);
406 fn main() {
407 foo();
408 }"#,
409 "bar", 418 "bar",
410 r#" 419 r#"
411 macro_rules! define_fn {($id:ident) => { fn $id{} }} 420macro_rules! define_fn {($id:ident) => { fn $id{} }}
412 define_fn!(bar); 421define_fn!(fo<|>o);
413 fn main() { 422fn main() {
414 bar(); 423 foo();
415 }"#, 424}
425"#,
426 r#"
427macro_rules! define_fn {($id:ident) => { fn $id{} }}
428define_fn!(bar);
429fn main() {
430 bar();
431}
432"#,
416 ); 433 );
417 } 434 }
418 435
419 #[test] 436 #[test]
420 fn test_rename_for_param_inside() { 437 fn test_rename_for_param_inside() {
421 test_rename( 438 check("j", r#"fn foo(i : u32) -> u32 { i<|> }"#, r#"fn foo(j : u32) -> u32 { j }"#);
422 r#"
423 fn foo(i : u32) -> u32 {
424 i<|>
425 }"#,
426 "j",
427 r#"
428 fn foo(j : u32) -> u32 {
429 j
430 }"#,
431 );
432 } 439 }
433 440
434 #[test] 441 #[test]
435 fn test_rename_refs_for_fn_param() { 442 fn test_rename_refs_for_fn_param() {
436 test_rename( 443 check("j", r#"fn foo(i<|> : u32) -> u32 { i }"#, r#"fn foo(j : u32) -> u32 { j }"#);
437 r#"
438 fn foo(i<|> : u32) -> u32 {
439 i
440 }"#,
441 "new_name",
442 r#"
443 fn foo(new_name : u32) -> u32 {
444 new_name
445 }"#,
446 );
447 } 444 }
448 445
449 #[test] 446 #[test]
450 fn test_rename_for_mut_param() { 447 fn test_rename_for_mut_param() {
451 test_rename( 448 check("j", r#"fn foo(mut i<|> : u32) -> u32 { i }"#, r#"fn foo(mut j : u32) -> u32 { j }"#);
452 r#"
453 fn foo(mut i<|> : u32) -> u32 {
454 i
455 }"#,
456 "new_name",
457 r#"
458 fn foo(mut new_name : u32) -> u32 {
459 new_name
460 }"#,
461 );
462 } 449 }
463 450
464 #[test] 451 #[test]
465 fn test_rename_struct_field() { 452 fn test_rename_struct_field() {
466 test_rename( 453 check(
454 "j",
467 r#" 455 r#"
468 struct Foo { 456struct Foo { i<|>: i32 }
469 i<|>: i32,
470 }
471 457
472 impl Foo { 458impl Foo {
473 fn new(i: i32) -> Self { 459 fn new(i: i32) -> Self {
474 Self { i: i } 460 Self { i: i }
475 }
476 } 461 }
477 "#, 462}
478 "j", 463"#,
479 r#" 464 r#"
480 struct Foo { 465struct Foo { j: i32 }
481 j: i32,
482 }
483 466
484 impl Foo { 467impl Foo {
485 fn new(i: i32) -> Self { 468 fn new(i: i32) -> Self {
486 Self { j: i } 469 Self { j: i }
487 }
488 } 470 }
489 "#, 471}
472"#,
490 ); 473 );
491 } 474 }
492 475
493 #[test] 476 #[test]
494 fn test_rename_struct_field_for_shorthand() { 477 fn test_rename_struct_field_for_shorthand() {
495 mark::check!(test_rename_struct_field_for_shorthand); 478 mark::check!(test_rename_struct_field_for_shorthand);
496 test_rename( 479 check(
480 "j",
497 r#" 481 r#"
498 struct Foo { 482struct Foo { i<|>: i32 }
499 i<|>: i32,
500 }
501 483
502 impl Foo { 484impl Foo {
503 fn new(i: i32) -> Self { 485 fn new(i: i32) -> Self {
504 Self { i } 486 Self { i }
505 }
506 } 487 }
507 "#, 488}
508 "j", 489"#,
509 r#" 490 r#"
510 struct Foo { 491struct Foo { j: i32 }
511 j: i32,
512 }
513 492
514 impl Foo { 493impl Foo {
515 fn new(i: i32) -> Self { 494 fn new(i: i32) -> Self {
516 Self { j: i } 495 Self { j: i }
517 }
518 } 496 }
519 "#, 497}
498"#,
520 ); 499 );
521 } 500 }
522 501
523 #[test] 502 #[test]
524 fn test_rename_local_for_field_shorthand() { 503 fn test_rename_local_for_field_shorthand() {
525 mark::check!(test_rename_local_for_field_shorthand); 504 mark::check!(test_rename_local_for_field_shorthand);
526 test_rename( 505 check(
506 "j",
527 r#" 507 r#"
528 struct Foo { 508struct Foo { i: i32 }
529 i: i32,
530 }
531 509
532 impl Foo { 510impl Foo {
533 fn new(i<|>: i32) -> Self { 511 fn new(i<|>: i32) -> Self {
534 Self { i } 512 Self { i }
535 }
536 } 513 }
537 "#, 514}
538 "j", 515"#,
539 r#" 516 r#"
540 struct Foo { 517struct Foo { i: i32 }
541 i: i32,
542 }
543 518
544 impl Foo { 519impl Foo {
545 fn new(j: i32) -> Self { 520 fn new(j: i32) -> Self {
546 Self { i: j } 521 Self { i: j }
547 }
548 } 522 }
549 "#, 523}
524"#,
550 ); 525 );
551 } 526 }
552 527
553 #[test] 528 #[test]
554 fn test_field_shorthand_correct_struct() { 529 fn test_field_shorthand_correct_struct() {
555 test_rename( 530 check(
556 r#"
557 struct Foo {
558 i<|>: i32,
559 }
560
561 struct Bar {
562 i: i32,
563 }
564
565 impl Bar {
566 fn new(i: i32) -> Self {
567 Self { i }
568 }
569 }
570 "#,
571 "j", 531 "j",
572 r#" 532 r#"
573 struct Foo { 533struct Foo { i<|>: i32 }
574 j: i32, 534struct Bar { i: i32 }
575 }
576 535
577 struct Bar { 536impl Bar {
578 i: i32, 537 fn new(i: i32) -> Self {
538 Self { i }
579 } 539 }
540}
541"#,
542 r#"
543struct Foo { j: i32 }
544struct Bar { i: i32 }
580 545
581 impl Bar { 546impl Bar {
582 fn new(i: i32) -> Self { 547 fn new(i: i32) -> Self {
583 Self { i } 548 Self { i }
584 }
585 } 549 }
586 "#, 550}
551"#,
587 ); 552 );
588 } 553 }
589 554
590 #[test] 555 #[test]
591 fn test_shadow_local_for_struct_shorthand() { 556 fn test_shadow_local_for_struct_shorthand() {
592 test_rename( 557 check(
558 "j",
593 r#" 559 r#"
594 struct Foo { 560struct Foo { i: i32 }
595 i: i32,
596 }
597 561
598 fn baz(i<|>: i32) -> Self { 562fn baz(i<|>: i32) -> Self {
599 let x = Foo { i }; 563 let x = Foo { i };
600 { 564 {
601 let i = 0; 565 let i = 0;
602 Foo { i } 566 Foo { i }
603 }
604 } 567 }
605 "#, 568}
606 "j", 569"#,
607 r#" 570 r#"
608 struct Foo { 571struct Foo { i: i32 }
609 i: i32,
610 }
611 572
612 fn baz(j: i32) -> Self { 573fn baz(j: i32) -> Self {
613 let x = Foo { i: j }; 574 let x = Foo { i: j };
614 { 575 {
615 let i = 0; 576 let i = 0;
616 Foo { i } 577 Foo { i }
617 }
618 } 578 }
619 "#, 579}
580"#,
620 ); 581 );
621 } 582 }
622 583
623 #[test] 584 #[test]
624 fn test_rename_mod() { 585 fn test_rename_mod() {
625 let (analysis, position) = analysis_and_position( 586 check_expect(
626 " 587 "foo2",
627 //- /lib.rs 588 r#"
628 mod bar; 589//- /lib.rs
629 590mod bar;
630 //- /bar.rs 591
631 mod foo<|>; 592//- /bar.rs
632 593mod foo<|>;
633 //- /bar/foo.rs 594
634 // emtpy 595//- /bar/foo.rs
635 ", 596// empty
597"#,
598 expect![[r#"
599 RangeInfo {
600 range: 4..7,
601 info: SourceChange {
602 source_file_edits: [
603 SourceFileEdit {
604 file_id: FileId(
605 2,
606 ),
607 edit: TextEdit {
608 indels: [
609 Indel {
610 insert: "foo2",
611 delete: 4..7,
612 },
613 ],
614 },
615 },
616 ],
617 file_system_edits: [
618 MoveFile {
619 src: FileId(
620 3,
621 ),
622 anchor: FileId(
623 3,
624 ),
625 dst: "foo2.rs",
626 },
627 ],
628 is_snippet: false,
629 },
630 }
631 "#]],
636 ); 632 );
637 let new_name = "foo2"; 633 }
638 let source_change = analysis.rename(position, new_name).unwrap(); 634
639 assert_debug_snapshot!(&source_change, 635 #[test]
640@r###" 636 fn test_rename_mod_in_use_tree() {
641 Some( 637 check_expect(
642 RangeInfo { 638 "quux",
643 range: 4..7, 639 r#"
644 info: SourceChange { 640//- /main.rs
645 source_file_edits: [ 641pub mod foo;
646 SourceFileEdit { 642pub mod bar;
647 file_id: FileId( 643fn main() {}
648 2, 644
649 ), 645//- /foo.rs
650 edit: TextEdit { 646pub struct FooContent;
651 indels: [ 647
652 Indel { 648//- /bar.rs
653 insert: "foo2", 649use crate::foo<|>::FooContent;
654 delete: 4..7, 650"#,
655 }, 651 expect![[r#"
656 ], 652 RangeInfo {
653 range: 11..14,
654 info: SourceChange {
655 source_file_edits: [
656 SourceFileEdit {
657 file_id: FileId(
658 1,
659 ),
660 edit: TextEdit {
661 indels: [
662 Indel {
663 insert: "quux",
664 delete: 8..11,
665 },
666 ],
667 },
657 }, 668 },
658 }, 669 SourceFileEdit {
659 ], 670 file_id: FileId(
660 file_system_edits: [ 671 3,
661 MoveFile { 672 ),
662 src: FileId( 673 edit: TextEdit {
663 3, 674 indels: [
664 ), 675 Indel {
665 dst_source_root: SourceRootId( 676 insert: "quux",
666 0, 677 delete: 11..14,
667 ), 678 },
668 dst_path: "bar/foo2.rs", 679 ],
669 }, 680 },
670 ], 681 },
671 is_snippet: false, 682 ],
672 }, 683 file_system_edits: [
673 }, 684 MoveFile {
674 ) 685 src: FileId(
675 "###); 686 2,
687 ),
688 anchor: FileId(
689 2,
690 ),
691 dst: "quux.rs",
692 },
693 ],
694 is_snippet: false,
695 },
696 }
697 "#]],
698 );
676 } 699 }
677 700
678 #[test] 701 #[test]
679 fn test_rename_mod_in_dir() { 702 fn test_rename_mod_in_dir() {
680 let (analysis, position) = analysis_and_position( 703 check_expect(
681 " 704 "foo2",
682 //- /lib.rs 705 r#"
683 mod fo<|>o; 706//- /lib.rs
684 //- /foo/mod.rs 707mod fo<|>o;
685 // emtpy 708//- /foo/mod.rs
686 ", 709// emtpy
687 ); 710"#,
688 let new_name = "foo2"; 711 expect![[r#"
689 let source_change = analysis.rename(position, new_name).unwrap(); 712 RangeInfo {
690 assert_debug_snapshot!(&source_change, 713 range: 4..7,
691 @r###" 714 info: SourceChange {
692 Some( 715 source_file_edits: [
693 RangeInfo { 716 SourceFileEdit {
694 range: 4..7, 717 file_id: FileId(
695 info: SourceChange { 718 1,
696 source_file_edits: [ 719 ),
697 SourceFileEdit { 720 edit: TextEdit {
698 file_id: FileId( 721 indels: [
699 1, 722 Indel {
700 ), 723 insert: "foo2",
701 edit: TextEdit { 724 delete: 4..7,
702 indels: [ 725 },
703 Indel { 726 ],
704 insert: "foo2", 727 },
705 delete: 4..7,
706 },
707 ],
708 }, 728 },
709 }, 729 ],
710 ], 730 file_system_edits: [
711 file_system_edits: [ 731 MoveFile {
712 MoveFile { 732 src: FileId(
713 src: FileId( 733 2,
714 2, 734 ),
715 ), 735 anchor: FileId(
716 dst_source_root: SourceRootId( 736 2,
717 0, 737 ),
718 ), 738 dst: "../foo2/mod.rs",
719 dst_path: "foo2/mod.rs", 739 },
720 }, 740 ],
721 ], 741 is_snippet: false,
722 is_snippet: false, 742 },
723 }, 743 }
724 }, 744 "#]],
725 ) 745 );
726 "###
727 );
728 } 746 }
729 747
730 #[test] 748 #[test]
731 fn test_module_rename_in_path() { 749 fn test_rename_unusually_nested_mod() {
732 test_rename( 750 check_expect(
751 "bar",
733 r#" 752 r#"
734 mod <|>foo { 753//- /lib.rs
735 pub fn bar() {} 754mod outer { mod fo<|>o; }
755
756//- /outer/foo.rs
757// emtpy
758"#,
759 expect![[r#"
760 RangeInfo {
761 range: 16..19,
762 info: SourceChange {
763 source_file_edits: [
764 SourceFileEdit {
765 file_id: FileId(
766 1,
767 ),
768 edit: TextEdit {
769 indels: [
770 Indel {
771 insert: "bar",
772 delete: 16..19,
773 },
774 ],
775 },
776 },
777 ],
778 file_system_edits: [
779 MoveFile {
780 src: FileId(
781 2,
782 ),
783 anchor: FileId(
784 2,
785 ),
786 dst: "bar.rs",
787 },
788 ],
789 is_snippet: false,
790 },
791 }
792 "#]],
793 );
736 } 794 }
737 795
738 fn main() { 796 #[test]
739 foo::bar(); 797 fn test_module_rename_in_path() {
740 }"#, 798 check(
741 "baz", 799 "baz",
742 r#" 800 r#"
743 mod baz { 801mod <|>foo { pub fn bar() {} }
744 pub fn bar() {}
745 }
746 802
747 fn main() { 803fn main() { foo::bar(); }
748 baz::bar(); 804"#,
749 }"#, 805 r#"
806mod baz { pub fn bar() {} }
807
808fn main() { baz::bar(); }
809"#,
750 ); 810 );
751 } 811 }
752 812
753 #[test] 813 #[test]
754 fn test_rename_mod_filename_and_path() { 814 fn test_rename_mod_filename_and_path() {
755 let (analysis, position) = analysis_and_position( 815 check_expect(
756 " 816 "foo2",
757 //- /lib.rs 817 r#"
758 mod bar; 818//- /lib.rs
759 fn f() { 819mod bar;
760 bar::foo::fun() 820fn f() {
761 } 821 bar::foo::fun()
762 822}
763 //- /bar.rs
764 pub mod foo<|>;
765 823
766 //- /bar/foo.rs 824//- /bar.rs
767 // pub fn fun() {} 825pub mod foo<|>;
768 ", 826
769 ); 827//- /bar/foo.rs
770 let new_name = "foo2"; 828// pub fn fun() {}
771 let source_change = analysis.rename(position, new_name).unwrap(); 829"#,
772 assert_debug_snapshot!(&source_change, 830 expect![[r#"
773@r###" 831 RangeInfo {
774 Some( 832 range: 8..11,
775 RangeInfo { 833 info: SourceChange {
776 range: 8..11, 834 source_file_edits: [
777 info: SourceChange { 835 SourceFileEdit {
778 source_file_edits: [ 836 file_id: FileId(
779 SourceFileEdit { 837 2,
780 file_id: FileId( 838 ),
781 2, 839 edit: TextEdit {
782 ), 840 indels: [
783 edit: TextEdit { 841 Indel {
784 indels: [ 842 insert: "foo2",
785 Indel { 843 delete: 8..11,
786 insert: "foo2", 844 },
787 delete: 8..11, 845 ],
788 }, 846 },
789 ],
790 }, 847 },
791 }, 848 SourceFileEdit {
792 SourceFileEdit { 849 file_id: FileId(
793 file_id: FileId( 850 1,
794 1, 851 ),
795 ), 852 edit: TextEdit {
796 edit: TextEdit { 853 indels: [
797 indels: [ 854 Indel {
798 Indel { 855 insert: "foo2",
799 insert: "foo2", 856 delete: 27..30,
800 delete: 27..30, 857 },
801 }, 858 ],
802 ], 859 },
803 }, 860 },
804 }, 861 ],
805 ], 862 file_system_edits: [
806 file_system_edits: [ 863 MoveFile {
807 MoveFile { 864 src: FileId(
808 src: FileId( 865 3,
809 3, 866 ),
810 ), 867 anchor: FileId(
811 dst_source_root: SourceRootId( 868 3,
812 0, 869 ),
813 ), 870 dst: "foo2.rs",
814 dst_path: "bar/foo2.rs", 871 },
815 }, 872 ],
816 ], 873 is_snippet: false,
817 is_snippet: false, 874 },
818 }, 875 }
819 }, 876 "#]],
820 ) 877 );
821 "###);
822 } 878 }
823 879
824 #[test] 880 #[test]
825 fn test_enum_variant_from_module_1() { 881 fn test_enum_variant_from_module_1() {
826 test_rename( 882 check(
883 "Baz",
827 r#" 884 r#"
828 mod foo { 885mod foo {
829 pub enum Foo { 886 pub enum Foo { Bar<|> }
830 Bar<|>, 887}
831 }
832 }
833 888
834 fn func(f: foo::Foo) { 889fn func(f: foo::Foo) {
835 match f { 890 match f {
836 foo::Foo::Bar => {} 891 foo::Foo::Bar => {}
837 }
838 } 892 }
839 "#, 893}
840 "Baz", 894"#,
841 r#" 895 r#"
842 mod foo { 896mod foo {
843 pub enum Foo { 897 pub enum Foo { Baz }
844 Baz, 898}
845 }
846 }
847 899
848 fn func(f: foo::Foo) { 900fn func(f: foo::Foo) {
849 match f { 901 match f {
850 foo::Foo::Baz => {} 902 foo::Foo::Baz => {}
851 }
852 } 903 }
853 "#, 904}
905"#,
854 ); 906 );
855 } 907 }
856 908
857 #[test] 909 #[test]
858 fn test_enum_variant_from_module_2() { 910 fn test_enum_variant_from_module_2() {
859 test_rename( 911 check(
912 "baz",
860 r#" 913 r#"
861 mod foo { 914mod foo {
862 pub struct Foo { 915 pub struct Foo { pub bar<|>: uint }
863 pub bar<|>: uint, 916}
864 }
865 }
866 917
867 fn foo(f: foo::Foo) { 918fn foo(f: foo::Foo) {
868 let _ = f.bar; 919 let _ = f.bar;
869 } 920}
870 "#, 921"#,
871 "baz",
872 r#" 922 r#"
873 mod foo { 923mod foo {
874 pub struct Foo { 924 pub struct Foo { pub baz: uint }
875 pub baz: uint, 925}
876 }
877 }
878 926
879 fn foo(f: foo::Foo) { 927fn foo(f: foo::Foo) {
880 let _ = f.baz; 928 let _ = f.baz;
881 } 929}
882 "#, 930"#,
883 ); 931 );
884 } 932 }
885 933
886 #[test] 934 #[test]
887 fn test_parameter_to_self() { 935 fn test_parameter_to_self() {
888 test_rename( 936 check(
937 "self",
889 r#" 938 r#"
890 struct Foo { 939struct Foo { i: i32 }
891 i: i32,
892 }
893 940
894 impl Foo { 941impl Foo {
895 fn f(foo<|>: &mut Foo) -> i32 { 942 fn f(foo<|>: &mut Foo) -> i32 {
896 foo.i 943 foo.i
897 }
898 } 944 }
899 "#, 945}
900 "self", 946"#,
901 r#" 947 r#"
902 struct Foo { 948struct Foo { i: i32 }
903 i: i32,
904 }
905 949
906 impl Foo { 950impl Foo {
907 fn f(&mut self) -> i32 { 951 fn f(&mut self) -> i32 {
908 self.i 952 self.i
909 }
910 } 953 }
911 "#, 954}
955"#,
912 ); 956 );
913 } 957 }
914 958
915 #[test] 959 #[test]
916 fn test_self_to_parameter() { 960 fn test_self_to_parameter() {
917 test_rename( 961 check(
962 "foo",
918 r#" 963 r#"
919 struct Foo { 964struct Foo { i: i32 }
920 i: i32,
921 }
922 965
923 impl Foo { 966impl Foo {
924 fn f(&mut <|>self) -> i32 { 967 fn f(&mut <|>self) -> i32 {
925 self.i 968 self.i
926 }
927 } 969 }
928 "#, 970}
929 "foo", 971"#,
930 r#" 972 r#"
931 struct Foo { 973struct Foo { i: i32 }
932 i: i32,
933 }
934 974
935 impl Foo { 975impl Foo {
936 fn f(foo: &mut Foo) -> i32 { 976 fn f(foo: &mut Foo) -> i32 {
937 foo.i 977 foo.i
938 }
939 } 978 }
940 "#, 979}
980"#,
941 ); 981 );
942 } 982 }
943 983
944 #[test] 984 #[test]
945 fn test_self_in_path_to_parameter() { 985 fn test_self_in_path_to_parameter() {
946 test_rename( 986 check(
987 "foo",
947 r#" 988 r#"
948 struct Foo { 989struct Foo { i: i32 }
949 i: i32,
950 }
951 990
952 impl Foo { 991impl Foo {
953 fn f(&self) -> i32 { 992 fn f(&self) -> i32 {
954 let self_var = 1; 993 let self_var = 1;
955 self<|>.i 994 self<|>.i
956 }
957 } 995 }
958 "#, 996}
959 "foo", 997"#,
960 r#" 998 r#"
961 struct Foo { 999struct Foo { i: i32 }
962 i: i32,
963 }
964 1000
965 impl Foo { 1001impl Foo {
966 fn f(foo: &Foo) -> i32 { 1002 fn f(foo: &Foo) -> i32 {
967 let self_var = 1; 1003 let self_var = 1;
968 foo.i 1004 foo.i
969 }
970 } 1005 }
971 "#, 1006}
1007"#,
972 ); 1008 );
973 } 1009 }
974
975 fn test_rename(text: &str, new_name: &str, expected: &str) {
976 let (analysis, position) = single_file_with_position(text);
977 let source_change = analysis.rename(position, new_name).unwrap();
978 let mut text_edit_builder = TextEditBuilder::default();
979 let mut file_id: Option<FileId> = None;
980 if let Some(change) = source_change {
981 for edit in change.info.source_file_edits {
982 file_id = Some(edit.file_id);
983 for indel in edit.edit.into_iter() {
984 text_edit_builder.replace(indel.delete, indel.insert);
985 }
986 }
987 }
988 let mut result = analysis.file_text(file_id.unwrap()).unwrap().to_string();
989 text_edit_builder.finish().apply(&mut result);
990 assert_eq_text!(expected, &*result);
991 }
992} 1010}