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