aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src/rename.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src/rename.rs')
-rw-r--r--crates/ide/src/rename.rs1790
1 files changed, 1790 insertions, 0 deletions
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
new file mode 100644
index 000000000..8096dfa0e
--- /dev/null
+++ b/crates/ide/src/rename.rs
@@ -0,0 +1,1790 @@
1//! Renaming functionality.
2//!
3//! This is mostly front-end for [`ide_db::rename`], but it also includes the
4//! tests. This module also implements a couple of magic tricks, like renaming
5//! `self` and to `self` (to switch between associated function and method).
6use hir::{AsAssocItem, InFile, Semantics};
7use ide_db::{
8 base_db::FileId,
9 defs::{Definition, NameClass, NameRefClass},
10 rename::{bail, format_err, source_edit_from_references, IdentifierKind},
11 RootDatabase,
12};
13use stdx::never;
14use syntax::{ast, AstNode, SyntaxNode};
15
16use text_edit::TextEdit;
17
18use crate::{FilePosition, RangeInfo, SourceChange};
19
20pub use ide_db::rename::RenameError;
21
22type RenameResult<T> = Result<T, RenameError>;
23
24/// Prepares a rename. The sole job of this function is to return the TextRange of the thing that is
25/// being targeted for a rename.
26pub(crate) fn prepare_rename(
27 db: &RootDatabase,
28 position: FilePosition,
29) -> RenameResult<RangeInfo<()>> {
30 let sema = Semantics::new(db);
31 let source_file = sema.parse(position.file_id);
32 let syntax = source_file.syntax();
33
34 let def = find_definition(&sema, syntax, position)?;
35 let frange = def
36 .range_for_rename(&sema)
37 .ok_or_else(|| format_err!("No references found at position"))?;
38 Ok(RangeInfo::new(frange.range, ()))
39}
40
41// Feature: Rename
42//
43// Renames the item below the cursor and all of its references
44//
45// |===
46// | Editor | Shortcut
47//
48// | VS Code | kbd:[F2]
49// |===
50//
51// image::https://user-images.githubusercontent.com/48062697/113065582-055aae80-91b1-11eb-8ade-2b58e6d81883.gif[]
52pub(crate) fn rename(
53 db: &RootDatabase,
54 position: FilePosition,
55 new_name: &str,
56) -> RenameResult<SourceChange> {
57 let sema = Semantics::new(db);
58 rename_with_semantics(&sema, position, new_name)
59}
60
61pub(crate) fn rename_with_semantics(
62 sema: &Semantics<RootDatabase>,
63 position: FilePosition,
64 new_name: &str,
65) -> RenameResult<SourceChange> {
66 let source_file = sema.parse(position.file_id);
67 let syntax = source_file.syntax();
68
69 let def = find_definition(sema, syntax, position)?;
70
71 if let Definition::Local(local) = def {
72 if let Some(self_param) = local.as_self_param(sema.db) {
73 cov_mark::hit!(rename_self_to_param);
74 return rename_self_to_param(sema, local, self_param, new_name);
75 }
76 if new_name == "self" {
77 cov_mark::hit!(rename_to_self);
78 return rename_to_self(sema, local);
79 }
80 }
81
82 def.rename(sema, new_name)
83}
84
85/// Called by the client when it is about to rename a file.
86pub(crate) fn will_rename_file(
87 db: &RootDatabase,
88 file_id: FileId,
89 new_name_stem: &str,
90) -> Option<SourceChange> {
91 let sema = Semantics::new(db);
92 let module = sema.to_module_def(file_id)?;
93 let def = Definition::ModuleDef(module.into());
94 let mut change = def.rename(&sema, new_name_stem).ok()?;
95 change.file_system_edits.clear();
96 Some(change)
97}
98
99fn find_definition(
100 sema: &Semantics<RootDatabase>,
101 syntax: &SyntaxNode,
102 position: FilePosition,
103) -> RenameResult<Definition> {
104 match sema
105 .find_node_at_offset_with_descend(syntax, position.offset)
106 .ok_or_else(|| format_err!("No references found at position"))?
107 {
108 // renaming aliases would rename the item being aliased as the HIR doesn't track aliases yet
109 ast::NameLike::Name(name)
110 if name.syntax().parent().map_or(false, |it| ast::Rename::can_cast(it.kind())) =>
111 {
112 bail!("Renaming aliases is currently unsupported")
113 }
114 ast::NameLike::Name(name) => {
115 NameClass::classify(sema, &name).map(|class| class.referenced_or_defined(sema.db))
116 }
117 ast::NameLike::NameRef(name_ref) => {
118 if let Some(def) =
119 NameRefClass::classify(sema, &name_ref).map(|class| class.referenced(sema.db))
120 {
121 // if the name differs from the definitions name it has to be an alias
122 if def.name(sema.db).map_or(false, |it| it.to_string() != name_ref.text()) {
123 bail!("Renaming aliases is currently unsupported");
124 }
125 Some(def)
126 } else {
127 None
128 }
129 }
130 ast::NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime)
131 .map(|class| NameRefClass::referenced(class, sema.db))
132 .or_else(|| {
133 NameClass::classify_lifetime(sema, &lifetime)
134 .map(|it| it.referenced_or_defined(sema.db))
135 }),
136 }
137 .ok_or_else(|| format_err!("No references found at position"))
138}
139
140fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> {
141 if never!(local.is_self(sema.db)) {
142 bail!("rename_to_self invoked on self");
143 }
144
145 let fn_def = match local.parent(sema.db) {
146 hir::DefWithBody::Function(func) => func,
147 _ => bail!("Cannot rename local to self outside of function"),
148 };
149
150 if let Some(_) = fn_def.self_param(sema.db) {
151 bail!("Method already has a self parameter");
152 }
153
154 let params = fn_def.assoc_fn_params(sema.db);
155 let first_param = params
156 .first()
157 .ok_or_else(|| format_err!("Cannot rename local to self unless it is a parameter"))?;
158 if first_param.as_local(sema.db) != local {
159 bail!("Only the first parameter may be renamed to self");
160 }
161
162 let assoc_item = fn_def
163 .as_assoc_item(sema.db)
164 .ok_or_else(|| format_err!("Cannot rename parameter to self for free function"))?;
165 let impl_ = match assoc_item.container(sema.db) {
166 hir::AssocItemContainer::Trait(_) => {
167 bail!("Cannot rename parameter to self for trait functions");
168 }
169 hir::AssocItemContainer::Impl(impl_) => impl_,
170 };
171 let first_param_ty = first_param.ty();
172 let impl_ty = impl_.self_ty(sema.db);
173 let (ty, self_param) = if impl_ty.remove_ref().is_some() {
174 // if the impl is a ref to the type we can just match the `&T` with self directly
175 (first_param_ty.clone(), "self")
176 } else {
177 first_param_ty.remove_ref().map_or((first_param_ty.clone(), "self"), |ty| {
178 (ty, if first_param_ty.is_mutable_reference() { "&mut self" } else { "&self" })
179 })
180 };
181
182 if ty != impl_ty {
183 bail!("Parameter type differs from impl block type");
184 }
185
186 let InFile { file_id, value: param_source } =
187 first_param.source(sema.db).ok_or_else(|| format_err!("No source for parameter found"))?;
188
189 let def = Definition::Local(local);
190 let usages = def.usages(sema).all();
191 let mut source_change = SourceChange::default();
192 source_change.extend(usages.iter().map(|(&file_id, references)| {
193 (file_id, source_edit_from_references(references, def, "self"))
194 }));
195 source_change.insert_source_edit(
196 file_id.original_file(sema.db),
197 TextEdit::replace(param_source.syntax().text_range(), String::from(self_param)),
198 );
199 Ok(source_change)
200}
201
202fn rename_self_to_param(
203 sema: &Semantics<RootDatabase>,
204 local: hir::Local,
205 self_param: hir::SelfParam,
206 new_name: &str,
207) -> RenameResult<SourceChange> {
208 if new_name == "self" {
209 // Let's do nothing rather than complain.
210 cov_mark::hit!(rename_self_to_self);
211 return Ok(SourceChange::default());
212 }
213
214 let identifier_kind = IdentifierKind::classify(new_name)?;
215
216 let InFile { file_id, value: self_param } =
217 self_param.source(sema.db).ok_or_else(|| format_err!("cannot find function source"))?;
218
219 let def = Definition::Local(local);
220 let usages = def.usages(sema).all();
221 let edit = text_edit_from_self_param(&self_param, new_name)
222 .ok_or_else(|| format_err!("No target type found"))?;
223 if usages.len() > 1 && identifier_kind == IdentifierKind::Underscore {
224 bail!("Cannot rename reference to `_` as it is being referenced multiple times");
225 }
226 let mut source_change = SourceChange::default();
227 source_change.insert_source_edit(file_id.original_file(sema.db), edit);
228 source_change.extend(usages.iter().map(|(&file_id, references)| {
229 (file_id, source_edit_from_references(references, def, new_name))
230 }));
231 Ok(source_change)
232}
233
234fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Option<TextEdit> {
235 fn target_type_name(impl_def: &ast::Impl) -> Option<String> {
236 if let Some(ast::Type::PathType(p)) = impl_def.self_ty() {
237 return Some(p.path()?.segment()?.name_ref()?.text().to_string());
238 }
239 None
240 }
241
242 let impl_def = self_param.syntax().ancestors().find_map(ast::Impl::cast)?;
243 let type_name = target_type_name(&impl_def)?;
244
245 let mut replacement_text = String::from(new_name);
246 replacement_text.push_str(": ");
247 match (self_param.amp_token(), self_param.mut_token()) {
248 (Some(_), None) => replacement_text.push('&'),
249 (Some(_), Some(_)) => replacement_text.push_str("&mut "),
250 (_, _) => (),
251 };
252 replacement_text.push_str(type_name.as_str());
253
254 Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text))
255}
256
257#[cfg(test)]
258mod tests {
259 use expect_test::{expect, Expect};
260 use stdx::trim_indent;
261 use test_utils::assert_eq_text;
262 use text_edit::TextEdit;
263
264 use crate::{fixture, FileId};
265
266 use super::{RangeInfo, RenameError};
267
268 fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) {
269 let ra_fixture_after = &trim_indent(ra_fixture_after);
270 let (analysis, position) = fixture::position(ra_fixture_before);
271 let rename_result = analysis
272 .rename(position, new_name)
273 .unwrap_or_else(|err| panic!("Rename to '{}' was cancelled: {}", new_name, err));
274 match rename_result {
275 Ok(source_change) => {
276 let mut text_edit_builder = TextEdit::builder();
277 let mut file_id: Option<FileId> = None;
278 for edit in source_change.source_file_edits {
279 file_id = Some(edit.0);
280 for indel in edit.1.into_iter() {
281 text_edit_builder.replace(indel.delete, indel.insert);
282 }
283 }
284 if let Some(file_id) = file_id {
285 let mut result = analysis.file_text(file_id).unwrap().to_string();
286 text_edit_builder.finish().apply(&mut result);
287 assert_eq_text!(ra_fixture_after, &*result);
288 }
289 }
290 Err(err) => {
291 if ra_fixture_after.starts_with("error:") {
292 let error_message = ra_fixture_after
293 .chars()
294 .into_iter()
295 .skip("error:".len())
296 .collect::<String>();
297 assert_eq!(error_message.trim(), err.to_string());
298 return;
299 } else {
300 panic!("Rename to '{}' failed unexpectedly: {}", new_name, err)
301 }
302 }
303 };
304 }
305
306 fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) {
307 let (analysis, position) = fixture::position(ra_fixture);
308 let source_change =
309 analysis.rename(position, new_name).unwrap().expect("Expect returned a RenameError");
310 expect.assert_debug_eq(&source_change)
311 }
312
313 fn check_prepare(ra_fixture: &str, expect: Expect) {
314 let (analysis, position) = fixture::position(ra_fixture);
315 let result = analysis
316 .prepare_rename(position)
317 .unwrap_or_else(|err| panic!("PrepareRename was cancelled: {}", err));
318 match result {
319 Ok(RangeInfo { range, info: () }) => {
320 let source = analysis.file_text(position.file_id).unwrap();
321 expect.assert_eq(&format!("{:?}: {}", range, &source[range]))
322 }
323 Err(RenameError(err)) => expect.assert_eq(&err),
324 };
325 }
326
327 #[test]
328 fn test_prepare_rename_namelikes() {
329 check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]);
330 check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"8..17: 'lifetime"#]]);
331 check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"3..7: name"#]]);
332 }
333
334 #[test]
335 fn test_prepare_rename_in_macro() {
336 check_prepare(
337 r"macro_rules! foo {
338 ($ident:ident) => {
339 pub struct $ident;
340 }
341}
342foo!(Foo$0);",
343 expect![[r#"83..86: Foo"#]],
344 );
345 }
346
347 #[test]
348 fn test_prepare_rename_keyword() {
349 check_prepare(r"struct$0 Foo;", expect![[r#"No references found at position"#]]);
350 }
351
352 #[test]
353 fn test_prepare_rename_tuple_field() {
354 check_prepare(
355 r#"
356struct Foo(i32);
357
358fn baz() {
359 let mut x = Foo(4);
360 x.0$0 = 5;
361}
362"#,
363 expect![[r#"No references found at position"#]],
364 );
365 }
366
367 #[test]
368 fn test_prepare_rename_builtin() {
369 check_prepare(
370 r#"
371fn foo() {
372 let x: i32$0 = 0;
373}
374"#,
375 expect![[r#"No references found at position"#]],
376 );
377 }
378
379 #[test]
380 fn test_prepare_rename_self() {
381 check_prepare(
382 r#"
383struct Foo {}
384
385impl Foo {
386 fn foo(self) -> Self$0 {
387 self
388 }
389}
390"#,
391 expect![[r#"No references found at position"#]],
392 );
393 }
394
395 #[test]
396 fn test_rename_to_underscore() {
397 check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#);
398 }
399
400 #[test]
401 fn test_rename_to_raw_identifier() {
402 check("r#fn", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let r#fn = 1; }"#);
403 }
404
405 #[test]
406 fn test_rename_to_invalid_identifier1() {
407 check(
408 "invalid!",
409 r#"fn main() { let i$0 = 1; }"#,
410 "error: Invalid name `invalid!`: not an identifier",
411 );
412 }
413
414 #[test]
415 fn test_rename_to_invalid_identifier2() {
416 check(
417 "multiple tokens",
418 r#"fn main() { let i$0 = 1; }"#,
419 "error: Invalid name `multiple tokens`: not an identifier",
420 );
421 }
422
423 #[test]
424 fn test_rename_to_invalid_identifier3() {
425 check(
426 "let",
427 r#"fn main() { let i$0 = 1; }"#,
428 "error: Invalid name `let`: not an identifier",
429 );
430 }
431
432 #[test]
433 fn test_rename_to_invalid_identifier_lifetime() {
434 cov_mark::check!(rename_not_an_ident_ref);
435 check(
436 "'foo",
437 r#"fn main() { let i$0 = 1; }"#,
438 "error: Invalid name `'foo`: not an identifier",
439 );
440 }
441
442 #[test]
443 fn test_rename_to_invalid_identifier_lifetime2() {
444 cov_mark::check!(rename_not_a_lifetime_ident_ref);
445 check(
446 "foo",
447 r#"fn main<'a>(_: &'a$0 ()) {}"#,
448 "error: Invalid name `foo`: not a lifetime identifier",
449 );
450 }
451
452 #[test]
453 fn test_rename_to_underscore_invalid() {
454 cov_mark::check!(rename_underscore_multiple);
455 check(
456 "_",
457 r#"fn main(foo$0: ()) {foo;}"#,
458 "error: Cannot rename reference to `_` as it is being referenced multiple times",
459 );
460 }
461
462 #[test]
463 fn test_rename_mod_invalid() {
464 check(
465 "'foo",
466 r#"mod foo$0 {}"#,
467 "error: Invalid name `'foo`: cannot rename module to 'foo",
468 );
469 }
470
471 #[test]
472 fn test_rename_for_local() {
473 check(
474 "k",
475 r#"
476fn main() {
477 let mut i = 1;
478 let j = 1;
479 i = i$0 + j;
480
481 { i = 0; }
482
483 i = 5;
484}
485"#,
486 r#"
487fn main() {
488 let mut k = 1;
489 let j = 1;
490 k = k + j;
491
492 { k = 0; }
493
494 k = 5;
495}
496"#,
497 );
498 }
499
500 #[test]
501 fn test_rename_unresolved_reference() {
502 check(
503 "new_name",
504 r#"fn main() { let _ = unresolved_ref$0; }"#,
505 "error: No references found at position",
506 );
507 }
508
509 #[test]
510 fn test_rename_for_macro_args() {
511 check(
512 "b",
513 r#"
514macro_rules! foo {($i:ident) => {$i} }
515fn main() {
516 let a$0 = "test";
517 foo!(a);
518}
519"#,
520 r#"
521macro_rules! foo {($i:ident) => {$i} }
522fn main() {
523 let b = "test";
524 foo!(b);
525}
526"#,
527 );
528 }
529
530 #[test]
531 fn test_rename_for_macro_args_rev() {
532 check(
533 "b",
534 r#"
535macro_rules! foo {($i:ident) => {$i} }
536fn main() {
537 let a = "test";
538 foo!(a$0);
539}
540"#,
541 r#"
542macro_rules! foo {($i:ident) => {$i} }
543fn main() {
544 let b = "test";
545 foo!(b);
546}
547"#,
548 );
549 }
550
551 #[test]
552 fn test_rename_for_macro_define_fn() {
553 check(
554 "bar",
555 r#"
556macro_rules! define_fn {($id:ident) => { fn $id{} }}
557define_fn!(foo);
558fn main() {
559 fo$0o();
560}
561"#,
562 r#"
563macro_rules! define_fn {($id:ident) => { fn $id{} }}
564define_fn!(bar);
565fn main() {
566 bar();
567}
568"#,
569 );
570 }
571
572 #[test]
573 fn test_rename_for_macro_define_fn_rev() {
574 check(
575 "bar",
576 r#"
577macro_rules! define_fn {($id:ident) => { fn $id{} }}
578define_fn!(fo$0o);
579fn main() {
580 foo();
581}
582"#,
583 r#"
584macro_rules! define_fn {($id:ident) => { fn $id{} }}
585define_fn!(bar);
586fn main() {
587 bar();
588}
589"#,
590 );
591 }
592
593 #[test]
594 fn test_rename_for_param_inside() {
595 check("j", r#"fn foo(i : u32) -> u32 { i$0 }"#, r#"fn foo(j : u32) -> u32 { j }"#);
596 }
597
598 #[test]
599 fn test_rename_refs_for_fn_param() {
600 check("j", r#"fn foo(i$0 : u32) -> u32 { i }"#, r#"fn foo(j : u32) -> u32 { j }"#);
601 }
602
603 #[test]
604 fn test_rename_for_mut_param() {
605 check("j", r#"fn foo(mut i$0 : u32) -> u32 { i }"#, r#"fn foo(mut j : u32) -> u32 { j }"#);
606 }
607
608 #[test]
609 fn test_rename_struct_field() {
610 check(
611 "j",
612 r#"
613struct Foo { i$0: i32 }
614
615impl Foo {
616 fn new(i: i32) -> Self {
617 Self { i: i }
618 }
619}
620"#,
621 r#"
622struct Foo { j: i32 }
623
624impl Foo {
625 fn new(i: i32) -> Self {
626 Self { j: i }
627 }
628}
629"#,
630 );
631 }
632
633 #[test]
634 fn test_rename_field_in_field_shorthand() {
635 cov_mark::check!(test_rename_field_in_field_shorthand);
636 check(
637 "j",
638 r#"
639struct Foo { i$0: i32 }
640
641impl Foo {
642 fn new(i: i32) -> Self {
643 Self { i }
644 }
645}
646"#,
647 r#"
648struct Foo { j: i32 }
649
650impl Foo {
651 fn new(i: i32) -> Self {
652 Self { j: i }
653 }
654}
655"#,
656 );
657 }
658
659 #[test]
660 fn test_rename_local_in_field_shorthand() {
661 cov_mark::check!(test_rename_local_in_field_shorthand);
662 check(
663 "j",
664 r#"
665struct Foo { i: i32 }
666
667impl Foo {
668 fn new(i$0: i32) -> Self {
669 Self { i }
670 }
671}
672"#,
673 r#"
674struct Foo { i: i32 }
675
676impl Foo {
677 fn new(j: i32) -> Self {
678 Self { i: j }
679 }
680}
681"#,
682 );
683 }
684
685 #[test]
686 fn test_field_shorthand_correct_struct() {
687 check(
688 "j",
689 r#"
690struct Foo { i$0: i32 }
691struct Bar { i: i32 }
692
693impl Bar {
694 fn new(i: i32) -> Self {
695 Self { i }
696 }
697}
698"#,
699 r#"
700struct Foo { j: i32 }
701struct Bar { i: i32 }
702
703impl Bar {
704 fn new(i: i32) -> Self {
705 Self { i }
706 }
707}
708"#,
709 );
710 }
711
712 #[test]
713 fn test_shadow_local_for_struct_shorthand() {
714 check(
715 "j",
716 r#"
717struct Foo { i: i32 }
718
719fn baz(i$0: i32) -> Self {
720 let x = Foo { i };
721 {
722 let i = 0;
723 Foo { i }
724 }
725}
726"#,
727 r#"
728struct Foo { i: i32 }
729
730fn baz(j: i32) -> Self {
731 let x = Foo { i: j };
732 {
733 let i = 0;
734 Foo { i }
735 }
736}
737"#,
738 );
739 }
740
741 #[test]
742 fn test_rename_mod() {
743 check_expect(
744 "foo2",
745 r#"
746//- /lib.rs
747mod bar;
748
749//- /bar.rs
750mod foo$0;
751
752//- /bar/foo.rs
753// empty
754"#,
755 expect![[r#"
756 SourceChange {
757 source_file_edits: {
758 FileId(
759 1,
760 ): TextEdit {
761 indels: [
762 Indel {
763 insert: "foo2",
764 delete: 4..7,
765 },
766 ],
767 },
768 },
769 file_system_edits: [
770 MoveFile {
771 src: FileId(
772 2,
773 ),
774 dst: AnchoredPathBuf {
775 anchor: FileId(
776 2,
777 ),
778 path: "foo2.rs",
779 },
780 },
781 ],
782 is_snippet: false,
783 }
784 "#]],
785 );
786 }
787
788 #[test]
789 fn test_rename_mod_in_use_tree() {
790 check_expect(
791 "quux",
792 r#"
793//- /main.rs
794pub mod foo;
795pub mod bar;
796fn main() {}
797
798//- /foo.rs
799pub struct FooContent;
800
801//- /bar.rs
802use crate::foo$0::FooContent;
803"#,
804 expect![[r#"
805 SourceChange {
806 source_file_edits: {
807 FileId(
808 0,
809 ): TextEdit {
810 indels: [
811 Indel {
812 insert: "quux",
813 delete: 8..11,
814 },
815 ],
816 },
817 FileId(
818 2,
819 ): TextEdit {
820 indels: [
821 Indel {
822 insert: "quux",
823 delete: 11..14,
824 },
825 ],
826 },
827 },
828 file_system_edits: [
829 MoveFile {
830 src: FileId(
831 1,
832 ),
833 dst: AnchoredPathBuf {
834 anchor: FileId(
835 1,
836 ),
837 path: "quux.rs",
838 },
839 },
840 ],
841 is_snippet: false,
842 }
843 "#]],
844 );
845 }
846
847 #[test]
848 fn test_rename_mod_in_dir() {
849 check_expect(
850 "foo2",
851 r#"
852//- /lib.rs
853mod fo$0o;
854//- /foo/mod.rs
855// empty
856"#,
857 expect![[r#"
858 SourceChange {
859 source_file_edits: {
860 FileId(
861 0,
862 ): TextEdit {
863 indels: [
864 Indel {
865 insert: "foo2",
866 delete: 4..7,
867 },
868 ],
869 },
870 },
871 file_system_edits: [
872 MoveFile {
873 src: FileId(
874 1,
875 ),
876 dst: AnchoredPathBuf {
877 anchor: FileId(
878 1,
879 ),
880 path: "../foo2/mod.rs",
881 },
882 },
883 ],
884 is_snippet: false,
885 }
886 "#]],
887 );
888 }
889
890 #[test]
891 fn test_rename_unusually_nested_mod() {
892 check_expect(
893 "bar",
894 r#"
895//- /lib.rs
896mod outer { mod fo$0o; }
897
898//- /outer/foo.rs
899// empty
900"#,
901 expect![[r#"
902 SourceChange {
903 source_file_edits: {
904 FileId(
905 0,
906 ): TextEdit {
907 indels: [
908 Indel {
909 insert: "bar",
910 delete: 16..19,
911 },
912 ],
913 },
914 },
915 file_system_edits: [
916 MoveFile {
917 src: FileId(
918 1,
919 ),
920 dst: AnchoredPathBuf {
921 anchor: FileId(
922 1,
923 ),
924 path: "bar.rs",
925 },
926 },
927 ],
928 is_snippet: false,
929 }
930 "#]],
931 );
932 }
933
934 #[test]
935 fn test_module_rename_in_path() {
936 check(
937 "baz",
938 r#"
939mod $0foo { pub fn bar() {} }
940
941fn main() { foo::bar(); }
942"#,
943 r#"
944mod baz { pub fn bar() {} }
945
946fn main() { baz::bar(); }
947"#,
948 );
949 }
950
951 #[test]
952 fn test_rename_mod_filename_and_path() {
953 check_expect(
954 "foo2",
955 r#"
956//- /lib.rs
957mod bar;
958fn f() {
959 bar::foo::fun()
960}
961
962//- /bar.rs
963pub mod foo$0;
964
965//- /bar/foo.rs
966// pub fn fun() {}
967"#,
968 expect![[r#"
969 SourceChange {
970 source_file_edits: {
971 FileId(
972 0,
973 ): TextEdit {
974 indels: [
975 Indel {
976 insert: "foo2",
977 delete: 27..30,
978 },
979 ],
980 },
981 FileId(
982 1,
983 ): TextEdit {
984 indels: [
985 Indel {
986 insert: "foo2",
987 delete: 8..11,
988 },
989 ],
990 },
991 },
992 file_system_edits: [
993 MoveFile {
994 src: FileId(
995 2,
996 ),
997 dst: AnchoredPathBuf {
998 anchor: FileId(
999 2,
1000 ),
1001 path: "foo2.rs",
1002 },
1003 },
1004 ],
1005 is_snippet: false,
1006 }
1007 "#]],
1008 );
1009 }
1010
1011 #[test]
1012 fn test_enum_variant_from_module_1() {
1013 cov_mark::check!(rename_non_local);
1014 check(
1015 "Baz",
1016 r#"
1017mod foo {
1018 pub enum Foo { Bar$0 }
1019}
1020
1021fn func(f: foo::Foo) {
1022 match f {
1023 foo::Foo::Bar => {}
1024 }
1025}
1026"#,
1027 r#"
1028mod foo {
1029 pub enum Foo { Baz }
1030}
1031
1032fn func(f: foo::Foo) {
1033 match f {
1034 foo::Foo::Baz => {}
1035 }
1036}
1037"#,
1038 );
1039 }
1040
1041 #[test]
1042 fn test_enum_variant_from_module_2() {
1043 check(
1044 "baz",
1045 r#"
1046mod foo {
1047 pub struct Foo { pub bar$0: uint }
1048}
1049
1050fn foo(f: foo::Foo) {
1051 let _ = f.bar;
1052}
1053"#,
1054 r#"
1055mod foo {
1056 pub struct Foo { pub baz: uint }
1057}
1058
1059fn foo(f: foo::Foo) {
1060 let _ = f.baz;
1061}
1062"#,
1063 );
1064 }
1065
1066 #[test]
1067 fn test_parameter_to_self() {
1068 cov_mark::check!(rename_to_self);
1069 check(
1070 "self",
1071 r#"
1072struct Foo { i: i32 }
1073
1074impl Foo {
1075 fn f(foo$0: &mut Foo) -> i32 {
1076 foo.i
1077 }
1078}
1079"#,
1080 r#"
1081struct Foo { i: i32 }
1082
1083impl Foo {
1084 fn f(&mut self) -> i32 {
1085 self.i
1086 }
1087}
1088"#,
1089 );
1090 check(
1091 "self",
1092 r#"
1093struct Foo { i: i32 }
1094
1095impl Foo {
1096 fn f(foo$0: Foo) -> i32 {
1097 foo.i
1098 }
1099}
1100"#,
1101 r#"
1102struct Foo { i: i32 }
1103
1104impl Foo {
1105 fn f(self) -> i32 {
1106 self.i
1107 }
1108}
1109"#,
1110 );
1111 }
1112
1113 #[test]
1114 fn test_parameter_to_self_error_no_impl() {
1115 check(
1116 "self",
1117 r#"
1118struct Foo { i: i32 }
1119
1120fn f(foo$0: &mut Foo) -> i32 {
1121 foo.i
1122}
1123"#,
1124 "error: Cannot rename parameter to self for free function",
1125 );
1126 check(
1127 "self",
1128 r#"
1129struct Foo { i: i32 }
1130struct Bar;
1131
1132impl Bar {
1133 fn f(foo$0: &mut Foo) -> i32 {
1134 foo.i
1135 }
1136}
1137"#,
1138 "error: Parameter type differs from impl block type",
1139 );
1140 }
1141
1142 #[test]
1143 fn test_parameter_to_self_error_not_first() {
1144 check(
1145 "self",
1146 r#"
1147struct Foo { i: i32 }
1148impl Foo {
1149 fn f(x: (), foo$0: &mut Foo) -> i32 {
1150 foo.i
1151 }
1152}
1153"#,
1154 "error: Only the first parameter may be renamed to self",
1155 );
1156 }
1157
1158 #[test]
1159 fn test_parameter_to_self_impl_ref() {
1160 check(
1161 "self",
1162 r#"
1163struct Foo { i: i32 }
1164impl &Foo {
1165 fn f(foo$0: &Foo) -> i32 {
1166 foo.i
1167 }
1168}
1169"#,
1170 r#"
1171struct Foo { i: i32 }
1172impl &Foo {
1173 fn f(self) -> i32 {
1174 self.i
1175 }
1176}
1177"#,
1178 );
1179 }
1180
1181 #[test]
1182 fn test_self_to_parameter() {
1183 check(
1184 "foo",
1185 r#"
1186struct Foo { i: i32 }
1187
1188impl Foo {
1189 fn f(&mut $0self) -> i32 {
1190 self.i
1191 }
1192}
1193"#,
1194 r#"
1195struct Foo { i: i32 }
1196
1197impl Foo {
1198 fn f(foo: &mut Foo) -> i32 {
1199 foo.i
1200 }
1201}
1202"#,
1203 );
1204 }
1205
1206 #[test]
1207 fn test_owned_self_to_parameter() {
1208 cov_mark::check!(rename_self_to_param);
1209 check(
1210 "foo",
1211 r#"
1212struct Foo { i: i32 }
1213
1214impl Foo {
1215 fn f($0self) -> i32 {
1216 self.i
1217 }
1218}
1219"#,
1220 r#"
1221struct Foo { i: i32 }
1222
1223impl Foo {
1224 fn f(foo: Foo) -> i32 {
1225 foo.i
1226 }
1227}
1228"#,
1229 );
1230 }
1231
1232 #[test]
1233 fn test_self_in_path_to_parameter() {
1234 check(
1235 "foo",
1236 r#"
1237struct Foo { i: i32 }
1238
1239impl Foo {
1240 fn f(&self) -> i32 {
1241 let self_var = 1;
1242 self$0.i
1243 }
1244}
1245"#,
1246 r#"
1247struct Foo { i: i32 }
1248
1249impl Foo {
1250 fn f(foo: &Foo) -> i32 {
1251 let self_var = 1;
1252 foo.i
1253 }
1254}
1255"#,
1256 );
1257 }
1258
1259 #[test]
1260 fn test_rename_field_put_init_shorthand() {
1261 cov_mark::check!(test_rename_field_put_init_shorthand);
1262 check(
1263 "bar",
1264 r#"
1265struct Foo { i$0: i32 }
1266
1267fn foo(bar: i32) -> Foo {
1268 Foo { i: bar }
1269}
1270"#,
1271 r#"
1272struct Foo { bar: i32 }
1273
1274fn foo(bar: i32) -> Foo {
1275 Foo { bar }
1276}
1277"#,
1278 );
1279 }
1280
1281 #[test]
1282 fn test_rename_local_put_init_shorthand() {
1283 cov_mark::check!(test_rename_local_put_init_shorthand);
1284 check(
1285 "i",
1286 r#"
1287struct Foo { i: i32 }
1288
1289fn foo(bar$0: i32) -> Foo {
1290 Foo { i: bar }
1291}
1292"#,
1293 r#"
1294struct Foo { i: i32 }
1295
1296fn foo(i: i32) -> Foo {
1297 Foo { i }
1298}
1299"#,
1300 );
1301 }
1302
1303 #[test]
1304 fn test_struct_field_pat_into_shorthand() {
1305 cov_mark::check!(test_rename_field_put_init_shorthand_pat);
1306 check(
1307 "baz",
1308 r#"
1309struct Foo { i$0: i32 }
1310
1311fn foo(foo: Foo) {
1312 let Foo { i: ref baz @ qux } = foo;
1313 let _ = qux;
1314}
1315"#,
1316 r#"
1317struct Foo { baz: i32 }
1318
1319fn foo(foo: Foo) {
1320 let Foo { ref baz @ qux } = foo;
1321 let _ = qux;
1322}
1323"#,
1324 );
1325 }
1326
1327 #[test]
1328 fn test_rename_binding_in_destructure_pat() {
1329 let expected_fixture = r#"
1330struct Foo {
1331 i: i32,
1332}
1333
1334fn foo(foo: Foo) {
1335 let Foo { i: bar } = foo;
1336 let _ = bar;
1337}
1338"#;
1339 check(
1340 "bar",
1341 r#"
1342struct Foo {
1343 i: i32,
1344}
1345
1346fn foo(foo: Foo) {
1347 let Foo { i: b } = foo;
1348 let _ = b$0;
1349}
1350"#,
1351 expected_fixture,
1352 );
1353 check(
1354 "bar",
1355 r#"
1356struct Foo {
1357 i: i32,
1358}
1359
1360fn foo(foo: Foo) {
1361 let Foo { i } = foo;
1362 let _ = i$0;
1363}
1364"#,
1365 expected_fixture,
1366 );
1367 }
1368
1369 #[test]
1370 fn test_rename_binding_in_destructure_param_pat() {
1371 check(
1372 "bar",
1373 r#"
1374struct Foo {
1375 i: i32
1376}
1377
1378fn foo(Foo { i }: foo) -> i32 {
1379 i$0
1380}
1381"#,
1382 r#"
1383struct Foo {
1384 i: i32
1385}
1386
1387fn foo(Foo { i: bar }: foo) -> i32 {
1388 bar
1389}
1390"#,
1391 )
1392 }
1393
1394 #[test]
1395 fn test_struct_field_complex_ident_pat() {
1396 check(
1397 "baz",
1398 r#"
1399struct Foo { i$0: i32 }
1400
1401fn foo(foo: Foo) {
1402 let Foo { ref i } = foo;
1403}
1404"#,
1405 r#"
1406struct Foo { baz: i32 }
1407
1408fn foo(foo: Foo) {
1409 let Foo { baz: ref i } = foo;
1410}
1411"#,
1412 );
1413 }
1414
1415 #[test]
1416 fn test_rename_lifetimes() {
1417 cov_mark::check!(rename_lifetime);
1418 check(
1419 "'yeeee",
1420 r#"
1421trait Foo<'a> {
1422 fn foo() -> &'a ();
1423}
1424impl<'a> Foo<'a> for &'a () {
1425 fn foo() -> &'a$0 () {
1426 unimplemented!()
1427 }
1428}
1429"#,
1430 r#"
1431trait Foo<'a> {
1432 fn foo() -> &'a ();
1433}
1434impl<'yeeee> Foo<'yeeee> for &'yeeee () {
1435 fn foo() -> &'yeeee () {
1436 unimplemented!()
1437 }
1438}
1439"#,
1440 )
1441 }
1442
1443 #[test]
1444 fn test_rename_bind_pat() {
1445 check(
1446 "new_name",
1447 r#"
1448fn main() {
1449 enum CustomOption<T> {
1450 None,
1451 Some(T),
1452 }
1453
1454 let test_variable = CustomOption::Some(22);
1455
1456 match test_variable {
1457 CustomOption::Some(foo$0) if foo == 11 => {}
1458 _ => (),
1459 }
1460}"#,
1461 r#"
1462fn main() {
1463 enum CustomOption<T> {
1464 None,
1465 Some(T),
1466 }
1467
1468 let test_variable = CustomOption::Some(22);
1469
1470 match test_variable {
1471 CustomOption::Some(new_name) if new_name == 11 => {}
1472 _ => (),
1473 }
1474}"#,
1475 );
1476 }
1477
1478 #[test]
1479 fn test_rename_label() {
1480 check(
1481 "'foo",
1482 r#"
1483fn foo<'a>() -> &'a () {
1484 'a: {
1485 'b: loop {
1486 break 'a$0;
1487 }
1488 }
1489}
1490"#,
1491 r#"
1492fn foo<'a>() -> &'a () {
1493 'foo: {
1494 'b: loop {
1495 break 'foo;
1496 }
1497 }
1498}
1499"#,
1500 )
1501 }
1502
1503 #[test]
1504 fn test_self_to_self() {
1505 cov_mark::check!(rename_self_to_self);
1506 check(
1507 "self",
1508 r#"
1509struct Foo;
1510impl Foo {
1511 fn foo(self$0) {}
1512}
1513"#,
1514 r#"
1515struct Foo;
1516impl Foo {
1517 fn foo(self) {}
1518}
1519"#,
1520 )
1521 }
1522
1523 #[test]
1524 fn test_rename_field_in_pat_in_macro_doesnt_shorthand() {
1525 // ideally we would be able to make this emit a short hand, but I doubt this is easily possible
1526 check(
1527 "baz",
1528 r#"
1529macro_rules! foo {
1530 ($pattern:pat) => {
1531 let $pattern = loop {};
1532 };
1533}
1534struct Foo {
1535 bar$0: u32,
1536}
1537fn foo() {
1538 foo!(Foo { bar: baz });
1539}
1540"#,
1541 r#"
1542macro_rules! foo {
1543 ($pattern:pat) => {
1544 let $pattern = loop {};
1545 };
1546}
1547struct Foo {
1548 baz: u32,
1549}
1550fn foo() {
1551 foo!(Foo { baz: baz });
1552}
1553"#,
1554 )
1555 }
1556
1557 #[test]
1558 fn test_rename_tuple_field() {
1559 check(
1560 "foo",
1561 r#"
1562struct Foo(i32);
1563
1564fn baz() {
1565 let mut x = Foo(4);
1566 x.0$0 = 5;
1567}
1568"#,
1569 "error: No identifier available to rename",
1570 );
1571 }
1572
1573 #[test]
1574 fn test_rename_builtin() {
1575 check(
1576 "foo",
1577 r#"
1578fn foo() {
1579 let x: i32$0 = 0;
1580}
1581"#,
1582 "error: Cannot rename builtin type",
1583 );
1584 }
1585
1586 #[test]
1587 fn test_rename_self() {
1588 check(
1589 "foo",
1590 r#"
1591struct Foo {}
1592
1593impl Foo {
1594 fn foo(self) -> Self$0 {
1595 self
1596 }
1597}
1598"#,
1599 "error: Cannot rename `Self`",
1600 );
1601 }
1602
1603 #[test]
1604 fn test_rename_ignores_self_ty() {
1605 check(
1606 "Fo0",
1607 r#"
1608struct $0Foo;
1609
1610impl Foo where Self: {}
1611"#,
1612 r#"
1613struct Fo0;
1614
1615impl Fo0 where Self: {}
1616"#,
1617 );
1618 }
1619
1620 #[test]
1621 fn test_rename_fails_on_aliases() {
1622 check(
1623 "Baz",
1624 r#"
1625struct Foo;
1626use Foo as Bar$0;
1627"#,
1628 "error: Renaming aliases is currently unsupported",
1629 );
1630 check(
1631 "Baz",
1632 r#"
1633struct Foo;
1634use Foo as Bar;
1635use Bar$0;
1636"#,
1637 "error: Renaming aliases is currently unsupported",
1638 );
1639 }
1640
1641 #[test]
1642 fn test_rename_trait_method() {
1643 let res = r"
1644trait Foo {
1645 fn foo(&self) {
1646 self.foo();
1647 }
1648}
1649
1650impl Foo for () {
1651 fn foo(&self) {
1652 self.foo();
1653 }
1654}";
1655 check(
1656 "foo",
1657 r#"
1658trait Foo {
1659 fn bar$0(&self) {
1660 self.bar();
1661 }
1662}
1663
1664impl Foo for () {
1665 fn bar(&self) {
1666 self.bar();
1667 }
1668}"#,
1669 res,
1670 );
1671 check(
1672 "foo",
1673 r#"
1674trait Foo {
1675 fn bar(&self) {
1676 self.bar$0();
1677 }
1678}
1679
1680impl Foo for () {
1681 fn bar(&self) {
1682 self.bar();
1683 }
1684}"#,
1685 res,
1686 );
1687 check(
1688 "foo",
1689 r#"
1690trait Foo {
1691 fn bar(&self) {
1692 self.bar();
1693 }
1694}
1695
1696impl Foo for () {
1697 fn bar$0(&self) {
1698 self.bar();
1699 }
1700}"#,
1701 res,
1702 );
1703 check(
1704 "foo",
1705 r#"
1706trait Foo {
1707 fn bar(&self) {
1708 self.bar();
1709 }
1710}
1711
1712impl Foo for () {
1713 fn bar(&self) {
1714 self.bar$0();
1715 }
1716}"#,
1717 res,
1718 );
1719 }
1720
1721 #[test]
1722 fn test_rename_trait_const() {
1723 let res = r"
1724trait Foo {
1725 const FOO: ();
1726}
1727
1728impl Foo for () {
1729 const FOO: ();
1730}
1731fn f() { <()>::FOO; }";
1732 check(
1733 "FOO",
1734 r#"
1735trait Foo {
1736 const BAR$0: ();
1737}
1738
1739impl Foo for () {
1740 const BAR: ();
1741}
1742fn f() { <()>::BAR; }"#,
1743 res,
1744 );
1745 check(
1746 "FOO",
1747 r#"
1748trait Foo {
1749 const BAR: ();
1750}
1751
1752impl Foo for () {
1753 const BAR$0: ();
1754}
1755fn f() { <()>::BAR; }"#,
1756 res,
1757 );
1758 check(
1759 "FOO",
1760 r#"
1761trait Foo {
1762 const BAR: ();
1763}
1764
1765impl Foo for () {
1766 const BAR: ();
1767}
1768fn f() { <()>::BAR$0; }"#,
1769 res,
1770 );
1771 }
1772
1773 #[test]
1774 fn macros_are_broken_lol() {
1775 cov_mark::check!(macros_are_broken_lol);
1776 check(
1777 "lol",
1778 r#"
1779macro_rules! m { () => { fn f() {} } }
1780m!();
1781fn main() { f$0() }
1782"#,
1783 r#"
1784macro_rules! m { () => { fn f() {} } }
1785lol
1786fn main() { lol() }
1787"#,
1788 )
1789 }
1790}